Compare commits

...

332 commits

Author SHA1 Message Date
bearoso
0525ea043e
Merge pull request #969 from yeager/master
Adding Swedish translation
2025-02-24 18:12:29 -06:00
Daniel Nylander
43d01935a9
Update sv.po 2025-02-09 08:41:54 +01:00
Daniel Nylander
db1b823558
Add files via upload 2025-02-09 08:39:58 +01:00
Daniel Nylander
0a567bd4fc
Add files via upload 2025-02-08 19:43:42 +01:00
Daniel Nylander
e3af507c5b
Add files via upload 2025-02-08 19:08:32 +01:00
Daniel Nylander
72922ce708
Adding Swedish translation 2025-02-08 18:09:59 +01:00
BearOso
663738341a qt,gtk/vulkan: Add VK_EXT_present_wait support again. 2025-01-24 15:55:41 -06:00
BearOso
191cdf462a vulkan/wayland: Use mailbox present mode.
FIFO can stall when other windows obscure the main window.
2025-01-24 13:33:59 -06:00
BearOso
5df649d49a Qt: Use SDL 3.2.0 tag. 2025-01-21 16:38:16 -06:00
bearoso
48fe934463
Merge pull request #967 from gilchavezm/patch-3
Qt: Update EmuApplication.cpp: handle Quit binding
2024-12-17 20:25:35 -06:00
Gilberto Chavez-Martinez
6794f51461
Update EmuApplication.cpp: handle Quit binding
Added handling of binding for Quit.
2024-12-17 19:24:07 -05:00
BearOso
b83f789343 sdl3/audio: Don't double the buffer size requested. 2024-12-13 20:06:07 -06:00
BearOso
1dbfd2e141 Qt: Use FetchContent for SDL3 on Windows or if not found. 2024-12-13 19:47:15 -06:00
BearOso
73b71c865b Qt: Allow building on unix without SDL video support. 2024-12-13 19:12:54 -06:00
BearOso
959da2aa04 Qt: Free bindings. 2024-12-13 19:08:09 -06:00
BearOso
03ec60cfda Qt: Fix up SDL3 button binding. 2024-12-13 19:04:56 -06:00
BearOso
fa94c74b84 common/audio/sdl3: Use a mutex for buffer accesses. 2024-12-13 17:23:20 -06:00
BearOso
56e58cdf99 Qt: Switch to SDL3 for audio and input.
SDL3 is not a submodule in the git tree yet, so as to not cause
an extra checkout for the other ports. It will eventually go in
external/SDL.
2024-12-13 17:14:34 -06:00
BearOso
2ffc66c33c Gtk/Wayland: Only allow one pause state change per focus change.
Check whether we've already paused for it.

Gtk/Wayland sends multiple focus_notify events with the same
state, even when it doesn't change. This occurs when popup
menus are opened and closed.
2024-12-09 15:32:34 -06:00
BearOso
9be3ed49a8 Qt/Vulkan: Don't show canvas before embedding.
It would briefly pop up as a new window.
2024-12-08 15:56:09 -06:00
BearOso
0e309e5c7c Gtk/Qt/Vulkan: Don't set vsync before swapping.
This destroys the swapchain.
2024-12-06 15:10:54 -06:00
BearOso
a7d59843da Qt: Adapt to 6.8 changes for Wayland.
Remove parent stackwidget. Don't set native properties for the render,
widget because they apply to the main window instead. Subsurfaces
don't need that anyway.
2024-12-06 14:58:22 -06:00
BearOso
fd05ca7df5 Qt: Show paths in native format on Win32. 2024-10-28 17:32:29 -05:00
BearOso
81f189cf57 Qt: Fix build on Windows. 2024-10-28 17:07:10 -05:00
BearOso
b5d9217881 Win32: Don't build fmtlib.
Windows port doesn't use it.
2024-10-26 20:29:21 -05:00
BearOso
e6c4f617cc Vulkan: Move descriptor pool out of context. 2024-10-26 20:26:47 -05:00
BearOso
20768ed8b2 Remove accidental commit of xbrz. 2024-10-25 18:58:49 -05:00
BearOso
8a33829bce external: Update fmtlib. 2024-10-25 18:54:14 -05:00
BearOso
f6a46f5831 Clean up unused headers. 2024-10-22 14:58:50 -05:00
BearOso
170a6aa1a4 Vulkan: Favor default initializers. 2024-10-22 14:27:23 -05:00
BearOso
932b093d98 Gtk/OpenGL: Fix context resize broken in is_x11/is_wayland change. 2024-10-20 19:30:15 -05:00
BearOso
c6e0fbf866 Vulkan: Remove unused line. 2024-10-17 15:21:31 -05:00
BearOso
14910960eb Vulkan: Add common function files. 2024-10-17 15:16:43 -05:00
BearOso
79f6e911f3 Vulkan: Use helper for image transitions. 2024-10-17 15:15:21 -05:00
BearOso
39c0f8418b Vulkan: Use recommended access flags for image layout transitions. 2024-10-17 13:48:46 -05:00
BearOso
71f2ba2d7e Gtk: Confused is_wayland/is_x11. 2024-10-16 13:18:19 -05:00
BearOso
5b1f20ec52 Gtk: Use is_x11 function. 2024-10-14 20:45:44 -05:00
BearOso
1fc9f26522 Gtk: Fix refresh rate detection break. 2024-10-14 10:16:05 -05:00
BearOso
0c228f0e0c Gtk: Use a Wayland check function. 2024-10-12 17:06:07 -05:00
BearOso
b19e31c83f Gtk: Get Superscope working. 2024-10-12 17:05:26 -05:00
BearOso
8028d3b6ca S-DSP: Initialize a couple registers to fix Magical Drop. 2024-10-11 13:53:24 -05:00
BearOso
13824a6ef4 Vulkan: Allocate swapchain on context create. 2024-10-07 14:43:52 -05:00
BearOso
befb0ba768 Qt: Use C++20. 2024-10-07 14:24:26 -05:00
BearOso
14c434d40d Vulkan: Use references in Swapchain. 2024-10-05 17:46:01 -05:00
BearOso
08403d47d1 Vulkan/win32: Fix build errors. 2024-10-03 11:27:07 -05:00
BearOso
febcf27482 Vulkan: Separate context, surface, and swapchain. 2024-10-03 11:19:42 -05:00
OV2
72e4946410 win32: use selected bank for slot save/load, display both slot and bank
in info string (#953)
2024-09-27 21:10:25 +02:00
BearOso
0727b4a474 Gtk: Clean up Snes9xWindow a bit. 2024-09-15 14:35:23 -05:00
bearoso
e06f1887d4
Merge pull request #950 from Thorarin/load-preview-fix
Fix load with preview file names and time stamps on extra banks
2024-09-15 10:18:26 -05:00
BearOso
9ed6f9d86b Screenshot: Don't use colon in filename. Use localtime
instead of localtime_r/s. Enable screenshots on Qt build.
2024-09-15 10:14:46 -05:00
Marcel Veldhuizen
d5ba1c6017 Fix load with preview file names and time stamps on extra banks 2024-09-14 23:32:58 +02:00
BearOso
0c547f3486 Gtk/Wayland: Different workaround for Gtk damage bug.
Instead of completely shutting down the display driver, shrink the
subsurface when removing fullscreen so that when the parent window
sends events when it receives damage or is resized.
2024-09-12 14:21:03 -05:00
BearOso
ea243051ed Save screenshot files with date for easier sorting. 2024-09-11 19:22:45 -05:00
BearOso
ebd9df46ec Gtk: Avoid saving the size while fullscreening occurs. 2024-09-11 14:45:07 -05:00
BearOso
5449e2d3c5 vulkan: Fix compile errors reported in Issue #946. 2024-09-06 13:35:46 -05:00
BearOso
645a4712e7 vulkan: Remove waiting extensions.
I guess these aren't widely supported.
2024-09-05 18:09:11 -05:00
BearOso
15ae9de25b Qt/Vulkan: Don't try to draw black any more. 2024-09-05 16:10:06 -05:00
BearOso
fb89cbf4c4 vulkan: Tidy a bit. 2024-09-05 16:07:37 -05:00
BearOso
c8895c8cdb vulkan: Add support for VK_KHR_present_wait
May have a tighter CPU-GPU sync for lower latency.
2024-09-02 13:17:08 -05:00
BearOso
8559143576 Fix compile on Windows. 2024-08-10 18:03:55 -05:00
BearOso
1773782575 Move vulkan to common/video. 2024-08-10 17:41:47 -05:00
BearOso
9f7173f819 Move shaders directory into common/video/opengl. 2024-08-10 17:08:57 -05:00
BearOso
5c7847acbb Sort common OpenGL and Wayland files into folders. 2024-08-10 17:00:40 -05:00
BearOso
8b1d67397e Qt: Experiment with alternate widget while a game isn't running. 2024-08-02 18:10:03 -05:00
BearOso
e92b93ca9c libretro: Clear SRAM after loading ROM. 2024-07-29 15:57:03 -05:00
BearOso
5d9f5b061b Qt: Output S9xMessage to console. 2024-07-29 15:57:03 -05:00
BearOso
cc49a06c77 external: Update stb_image.h to reduce warnings 2024-07-29 15:57:03 -05:00
bearoso
4a20cfc024
Merge pull request #940 from lgv5/master
Gtk: Fix build on 32-bits systems.
2024-07-28 17:34:35 -05:00
Lucas Gabriel Vuotto
bff02194a7 Gtk: Fix build on 32-bits systems. 2024-07-28 20:52:20 +00:00
BearOso
8a9b8cfcfd SA1: Change mapping type for banks 40->4f on SA1. 2024-07-26 15:12:58 -05:00
BearOso
881eeaed9a Update some version strings. 2024-07-14 14:31:51 -05:00
Michael Buckley
7c9c220931 macOS: Fix keyboard settings warning 2024-07-14 12:27:08 -07:00
Michael Buckley
43b6efb12b MacOS: Fix building in Xcode 15. 2024-07-14 11:38:55 -07:00
OV2
18096d9f68 imgui: add movie frame count and watches display (#167) 2024-07-14 02:20:47 +02:00
OV2
921f9f7b83 Update changes.txt 2024-07-07 13:48:47 +02:00
BearOso
dcd279afe0 Update changes.txt. 2024-07-06 10:59:00 -05:00
BearOso
d514d135a7 Gtk: Create config directory in get_config_dir if it doesn't exist. 2024-06-18 15:38:21 -05:00
BearOso
ed3695f704 Gtk: Fix config directory order.
Search for XDG_CONFIG_HOME. If that exists, use
$XDG_CONFIG_HOME/snes9x, otherwise use $HOME/.config/snes9x.

Remove broken legacy check.
2024-06-18 15:35:05 -05:00
BearOso
a9e64edf73 Vulkan/Win32: Fix compile error. 2024-06-13 16:50:12 -05:00
BearOso
008cbcd1a1 Vulkan: Simplify set_vsync. Remove relaxed fifo.
It looks like relaxed fifo tears when refresh rate doesn't
match because it always misses a refresh interval.
2024-06-13 16:17:36 -05:00
BearOso
3980a9d6d4 Vulkan: Fix inverted logic. 2024-06-13 15:56:04 -05:00
BearOso
5c78493f4e Vulkan: Add device wait back to swapchain recreation. 2024-06-13 14:39:36 -05:00
BearOso
33e40a8f16 Vulkan: Refactor present modes. Add relaxed. 2024-06-13 11:37:00 -05:00
BearOso
2e25b70cf0 Vulkan: Associate new fence with swapchain image, not frame resources. 2024-06-13 11:16:36 -05:00
BearOso
5949bbab97 Vulkan: Utilize VK_EXT_swapchain_maintenance1.
This is core in Vulkan 1.1.

We can now change vsync state without a new swapchain.

A fence is signaled when image is on screen, so we can possibly
be a little more precise with timing and avoid a whole device wait.
2024-06-12 16:54:13 -05:00
BearOso
99990af31e Vulkan: Don't use mailbox for vsync case. 2024-06-11 18:04:35 -05:00
BearOso
c02ee4373e Vulkan: Refactor device acquisition for cleanness. 2024-06-11 16:07:38 -05:00
BearOso
4f1a2d9c29 Vulkan: Remove exception handler/old swapchain use.
Exceptions are now turned off anyway.
2024-06-11 16:06:30 -05:00
BearOso
c7b77d4a76 Win32: Throttle frame rate on alternate interlaced frames. 2024-06-07 14:27:47 -05:00
OV2
a277d7f9e8 win32: properly save bank+/- hotkeys (fixes #925) 2024-05-26 17:06:20 +02:00
BearOso
55724eba1d Qt: Change browse button to "Open Folder" button when location isn't custom.
Enables quick access to the ROM directory or the config directory.
2024-05-25 12:04:58 -05:00
BearOso
794b4fdc72 Qt: Remove debug print from hat press. 2024-05-24 20:05:27 -05:00
BearOso
738e53989e Cheats: Retain enabled state when updating existing cheat.
Fix inverted logic in Qt cheat window.
2024-05-18 19:25:33 -05:00
BearOso
8f41776532 Qt: Hide software filter box.
May remove this in the future because it's unneeded with shaders.
2024-05-13 14:26:02 -05:00
BearOso
c9b90655bd Qt/Windows: Match system color scheme.
Use windowsvista style for light, dark fusion scheme for dark.
2024-05-13 13:58:31 -05:00
BearOso
cfabbd7f97 Qt: Prefer non-local config dir on Windows, and check for correct name. 2024-05-12 15:11:47 -05:00
OV2
911b416d28 win32: vulkan: apply vsync setting during init 2024-05-12 01:23:23 +02:00
OV2
f62eb40ac7 win32: skip framerate throttling in turbo mode (#853) 2024-05-12 01:20:34 +02:00
BearOso
6dd6f1945b Update version strings from 1.62.3 to 1.63. 2024-05-10 14:37:25 -05:00
OV2
d62f14212e win32: default new cheat val to current cheat val, show error on empty
new val (#918)
2024-05-10 00:29:19 +02:00
OV2
87f050feba win32: add cheat edit and search dialogs as hotkeys (#918) 2024-05-10 00:10:13 +02:00
OV2
bf83f0f605 win32: add hotkey to switch aspect ratio (#912) 2024-05-10 00:09:20 +02:00
OV2
ba6f67510e win32: fix warnings 2024-05-09 14:58:46 +02:00
OV2
be53955553 win32: allow multiselect in cheat dialog, handle delete and selection
(#916)
2024-05-08 16:53:11 +02:00
BearOso
add607c38f Qt: Add save slot status info. 2024-05-07 14:27:28 -05:00
BearOso
c476e4acdc Gtk: Show existence of save state on slot select. 2024-05-07 14:18:40 -05:00
BearOso
bac6798141 Win32: Fix missing semicolon. 2024-05-07 14:06:33 -05:00
BearOso
73cb8014f1 Win32: Show whether save state exists when slot selected. 2024-05-07 14:01:59 -05:00
OV2
77f86ef4b6 win32: only deinint d3d imgui elements if they were initialized 2024-05-05 19:25:47 +02:00
BearOso
51b6528224 Vulkan: Fix a couple of unsigned/signed warnings. 2024-05-04 15:43:54 -05:00
BearOso
c39e86c0c6 Qt: More built-in icons. 2024-05-04 15:38:29 -05:00
BearOso
771b0ffc37 Qt: Mouse fixes. 2024-05-01 16:09:44 -05:00
BearOso
dcccf28bde Qt: Add mouse support. 2024-04-30 16:28:45 -05:00
BearOso
f6f9cf09e1 CMakeFiles: Tweaks. 2024-04-30 14:39:07 -05:00
Sneed
2bb7723220 boot 4mb superfx roms 2024-04-28 08:26:06 -05:00
BearOso
fdcff015dc CPU: Stop emulation if CPU is deadlocked. 2024-04-27 17:44:21 -05:00
BearOso
986dd2a061 SuperFX: Allow execution from ROM in higher banks. 2024-04-27 16:59:11 -05:00
BearOso
65ef81ca2d SuperFX: Attempt to fix execution bank check against SCMR. 2024-04-27 16:46:05 -05:00
bearoso
0a527f3adc
Merge pull request #906 from pstef/update-minizip
Update minizip to version 1.1
2024-04-27 11:11:53 -05:00
pstef
496d2f0020
Merge branch 'snes9xgit:master' into update-minizip 2024-04-26 13:55:37 +02:00
BearOso
af4ec50b16 SuperFX: Run when any bit of SCMR is set. 2024-04-25 15:49:56 -05:00
BearOso
582128bce7 Qt: Implement Swap Controller 1 & 2 2024-04-22 15:58:35 -05:00
BearOso
98cac16b46 Qt: Allow changing controller ports. No mouse yet. 2024-04-22 15:58:35 -05:00
bearoso
8077396d39
Merge pull request #914 from bernborgess/fix-vulkan-semicolon
Vulkan: Fixed semicolon in vulcan_context.cpp
2024-04-20 09:46:53 -05:00
bernborgess
3c09b1ea63 Fixed semicolon in vulcan_context.cpp 2024-04-20 09:23:21 -03:00
BearOso
bb905521df Vulkan: Simplify some code. 2024-04-19 16:29:09 -05:00
BearOso
0ae69cd518 Qt: Update paths before loading game. 2024-04-18 14:37:55 -05:00
BearOso
8750a9ec26 Vulkan: Silence some warnings. 2024-04-17 17:12:51 -05:00
BearOso
a5a0a80464 Qt: Fix cheats dialog. 2024-04-17 15:51:38 -05:00
BearOso
e0849ab384 Qt/Windows: Just use a fixed dark fusion style.
Fix dark/light icon calculation to not be based on actual colors,
not the OS theme.
2024-04-16 14:47:57 -05:00
BearOso
31db46516d Qt/Windows: Don't use new windows11 style with Qt 6.7.
It has some glitches.
2024-04-15 15:51:41 -05:00
BearOso
9d22dbb8d8 Cirrus.yml: Update FreeBSD target. 2024-04-13 18:14:51 -05:00
BearOso
0ed93cd732 vulkan-headers: Roll back to v1.3.280.
v1.3.281 contains a broken Vulkan-Hpp on 32-bit.
2024-04-13 14:01:50 -05:00
BearOso
3d5e412fb5 Vulkan-Hpp: Fix VULKAN_HPP_NO_NODISCARD_WARNINGS. 2024-04-13 13:49:48 -05:00
BearOso
130942f578 Vulkan: Fix compile issues on Windows because of min/max. 2024-04-13 11:06:14 -05:00
BearOso
46c6bd7eb4 Vulkan-Hpp: Don't use exceptions. 2024-04-12 18:47:16 -05:00
BearOso
7cf9f59923 Update VulkanMemoryAllocator-Hpp. 2024-04-06 16:28:03 -05:00
BearOso
3ebc239034 Update vulkan-headers to v1.3.281. 2024-04-06 16:23:47 -05:00
Nebuleon Fumika
5fbc17672a Guard Z_BZIP2ED with #ifdef HAVE_BZIP2 in miniunz.c, unzip.c 2024-03-19 00:14:22 +01:00
Piotr Paweł Stefaniak
76207f9586 formatting before cherry-pick 2024-03-19 00:14:22 +01:00
Piotr Paweł Stefaniak
860a928eca Remove a register keyword
that resurfaced with the recent version update.
2024-03-18 23:49:04 +01:00
Brandon Wright
94a649b4bb Fix compiling on Gentoo Linux due to use of non-public API. 2024-03-18 23:47:20 +01:00
Piotr Paweł Stefaniak
3791810d2c Update zconf.h and zlib.h for MacOSX to import z_crc_type
This is the first revision that introduces the z_crc_type that is used by
the recently updated minizip code.
2024-03-18 22:00:03 +01:00
Piotr Paweł Stefaniak
8c93a8c12b Update minizip to version 1.1
This enables compilation under my Fedora aarch64 system.
2024-03-18 20:51:22 +01:00
bearoso
1e1c45be07
Merge pull request #900 from StanleyKid-22/master
Updated Ukrainian translation
2024-02-14 10:31:34 -06:00
StanleyKid-22
4336892724
Updated Ukrainian translation 2024-02-14 15:47:32 +02:00
StanleyKid-22
7b7f126f5e
Merge branch 'snes9xgit:master' into master 2024-02-14 15:44:58 +02:00
BearOso
be6372c034 Gtk/Wayland: Disable display driver during unfullscreening.
Gtk incorrectly doesn't resize widgets beneath a subsurface.
2024-02-08 12:49:12 -06:00
BearOso
68af8fe783 Vulkan/Wayland: Use old swapchain size if none provided. 2024-02-07 18:41:36 -06:00
BearOso
f83a57fe25 Qt: Use DEFINES for core library.
Fixes compile.
2024-02-06 18:08:44 -06:00
bearoso
e76abdc4ef
Merge pull request #887 from alarixnia/unix-bsd
unix: Fix support for systems other than Linux
2024-01-28 10:45:43 -06:00
StanleyKid-22
0aad83b35e
Update Ukrainian translation
Hotfix for 2 strings.
2024-01-28 12:30:54 +02:00
StanleyKid-22
1785bb4467
Updated Ukrainian translation
Updated to actual project version 1.62.3.
2024-01-27 20:27:04 +02:00
BearOso
f0001ab428 Wayland: Reorder resizing operations
Ensure a buffer is in place before calling viewporter.
2024-01-09 10:31:49 -06:00
BearOso
9b77335345 Wayland: Unset source coordinates to just use whole buffer. 2024-01-09 10:05:37 -06:00
OV2
652adc81a4 win32: fix libretro build 2024-01-03 00:20:56 +01:00
OV2
56a5d3c31f win32: restore rom info dialog colors 2024-01-02 20:42:07 +01:00
BearOso
4dddce8b88 Qt: Abstract cheats interface. 2023-12-16 13:12:09 -06:00
BearOso
b8d71c6562 Qt: Update actual bindings after using menu items. 2023-11-30 17:12:42 -06:00
BearOso
7dcae9d56d qt: Build fmt library. 2023-11-30 16:58:25 -06:00
BearOso
6e738950c6 win32: Don't realloc graphics buffers when dialog shown. 2023-11-30 16:57:47 -06:00
BearOso
ca158abcb6 Qt: Add scripts used for building minimal binary Qt. 2023-11-28 18:35:43 -06:00
BearOso
026307219c Qt: Update Windows build with prebuilt Qt. 2023-11-28 18:35:43 -06:00
nia
62501a2218 unix: Support OSS audio on systems other than Linux. 2023-11-20 11:16:08 +01:00
nia
666761c95a unix: Avoid redefining libm function log2l 2023-11-20 10:56:52 +01:00
bearoso
97291767b8
Merge pull request #886 from alarixnia/dlopen
gtk: Use CMAKE_DL_LIBS
2023-11-19 17:57:15 -06:00
bearoso
91b08bfeb7
Merge pull request #885 from JakeSmarter/de
Add de l10n
2023-11-19 17:56:48 -06:00
bearoso
a049345125
Merge pull request #881 from JakeSmarter/pl
More i18n
2023-11-19 17:56:30 -06:00
nia
5fb99b7ad6 gtk: Use CMAKE_DL_LIBS
Fixes building snes9x-gtk on systems where dlopen is in libc
and without an empty libdl stub.
2023-11-20 00:13:18 +01:00
OV2
5c65edbbca win32: map waveOut devices to mmdevices via endpoint id 2023-11-18 22:25:20 +01:00
Jake Smarter
38c28e1f9c Add de l10n 2023-11-18 19:28:18 +01:00
OV2
d30060cdc1 win32: try MMDeviceEnumerator for waveOut device enumeration before
falling back to device caps (#883)
2023-11-03 19:07:09 +01:00
Jake Smarter
665ddf86f2 * Add plurals
* Drop proper names from l10n
* Add string parameter numbers for correct l10n freedom
* Update POT file
* Update pl l10n based on new POT file
* Add locale specific number formatting where applicable
* Make use of Glib’s logging facility
2023-10-30 15:46:21 +01:00
BearOso
bd918f60fb Qt/Wayland: Fix case when window is reparented for server-side decoration.
Apparently the top-level window surface can start at negative coordinates, so the subsurface needs to be offset by that much more.
2023-10-11 15:02:08 -05:00
BearOso
953de52465 Qt: Fix issues with input rate and vrr. 2023-10-11 10:44:31 -05:00
BearOso
b8d0b8ec9e Fix some unsigned/signed comparisons. 2023-10-10 19:44:06 -05:00
BearOso
397cb98347 Vulkan: Remove out-of-date message from exception handler. Clean up a bit. 2023-10-10 19:38:56 -05:00
BearOso
94125d4781 Resampler: Consistently use signed ints. 2023-10-10 19:22:40 -05:00
BearOso
58bdb3efa0 Vulkan: Use mailbox when available. Suppress debug messages. 2023-10-09 15:01:35 -05:00
BearOso
e3f1790711 Vulkan/Gtk: Make instance creation fail more robust. 2023-10-09 14:15:46 -05:00
BearOso
039e68d306 Gtk: Don't allow threading with NTSC filter. 2023-10-09 14:01:52 -05:00
BearOso
49556cd5d1 Gtk: Blargg NTSC outputs reduced width in 512-mode. 2023-10-09 13:46:53 -05:00
BearOso
01e408e4c8 (Qt,Gtk)/OpenGL: Specify unpack alignment. 2023-10-08 11:03:27 -05:00
BearOso
0e03a36847 Update Vulkan Memory Allocator and .hpp. 2023-09-23 10:22:48 -05:00
BearOso
9be1ee83b3 Gtk: Compile std_chrono_throttle.cpp unconditionally. 2023-09-16 10:40:29 -05:00
BearOso
84ac947567 memmap: Fix SD3 Italian translation that uses ExtHiROM.
Lower Mother 2 hack heuristic score to 3.
2023-09-11 12:10:05 -05:00
bearoso
153965e5f2
Merge pull request #868 from JakeSmarter/pl
Add GTK+ pl translations
2023-09-09 16:39:45 -05:00
BearOso
f7ddc412f9 Vulkan: Catch out-of-date exception on swapchain acquire. 2023-09-04 18:57:35 -05:00
BearOso
67b6d47c09 Vulkan: Catch out-of-date on present that vulkan.hpp throws. 2023-09-04 14:00:03 -05:00
BearOso
02cb6dc319 Vulkan: Set graphics queue on swapchain creation. 2023-09-04 13:45:16 -05:00
BearOso
5314bc8db0 Vulkan: Check image extents for surface compatibility. 2023-09-04 11:56:10 -05:00
BearOso
f45e22b08d Qt: Change vsync setting immediately when config changes. 2023-09-04 11:38:00 -05:00
BearOso
7ad393572f Vulkan: Catch swapchain create exceptions. Don't try to use immediate present mode if not supported. 2023-09-04 11:37:28 -05:00
BearOso
d96b2a711b Qt: Handle failed context creation better. 2023-09-04 11:03:21 -05:00
BearOso
cf49325555 Qt: More overclock options. 2023-09-02 16:07:16 -05:00
BearOso
6555feecda Wayland: Round preferred scale multiplication.
wl_fractional_scale_v1 spec says to round halfway away from zero.
2023-08-31 14:01:09 -05:00
Jake Smarter
e2087adffb Add GTK+ pl translations 2023-08-31 11:42:32 +02:00
OV2
94fbbfe0dd Close zip files during patch search in release (fixes #864) 2023-08-27 15:40:36 +02:00
BearOso
6fce8bd2ca Qt: Clean up style tweaking. 2023-08-25 11:47:29 -05:00
BearOso
2be85e8a44 Gtk: Fix additional hardcoded paths. 2023-08-25 11:47:29 -05:00
BearOso
086da07b53 Qt: White icons for dark color schemes. 2023-08-25 11:47:29 -05:00
BearOso
d18cfb1e77 Qt: Compilation fix. Help updates. 2023-08-25 11:47:29 -05:00
BearOso
942f4ae971 Fix compilation on win32. 2023-08-25 11:47:29 -05:00
BearOso
56edd4c0b7 Qt: Finish cheats. Add what's this stuff. 2023-08-25 11:47:29 -05:00
BearOso
bd41d010f3 Qt: Attach cheats dialog directly to Snes9x.
Update sizing for cheats and shader params dialogs.
2023-08-25 11:47:29 -05:00
BearOso
34aab85f69 Qt: Fix non-windows build. 2023-08-25 11:47:29 -05:00
BearOso
ea28e6d0d9 Qt: Add unworking cheats window. 2023-08-25 11:47:29 -05:00
BearOso
9dbb36ead0 Qt: Changes to ensure UTF-8 encoding on windows. 2023-08-25 11:47:29 -05:00
BearOso
2ea377cba1 Qt: More windows cleaning. 2023-08-25 11:47:29 -05:00
BearOso
c8b21fa461 Qt: Portable mode. Fix hang on close. 2023-08-25 11:47:29 -05:00
BearOso
2e3d5e1102 Qt: Use bundled pcre2. 2023-08-25 11:47:29 -05:00
BearOso
51ed006b6c Qt: Wording. 2023-08-25 11:47:29 -05:00
BearOso
4f91b2430f Qt: More updates. 2023-08-25 11:47:29 -05:00
BearOso
03c9a10cc2 Qt: Use copy for software output path. 2023-08-25 11:47:29 -05:00
BearOso
2fd8b52163 Qt: More updates. 2023-08-25 11:47:29 -05:00
BearOso
876eaa8fb2 Add submodule cubeb. 2023-08-25 11:47:29 -05:00
BearOso
ad613b5f0b Fix OpenGL shader changed race. 2023-08-25 11:47:29 -05:00
BearOso
8bb1e7747d UI element resize without canvas recreate. Capture slot by value when loading/saving states. 2023-08-25 11:47:29 -05:00
BearOso
61bafc329d Make sure wayland surface is created for OpenGL, too. 2023-08-25 11:47:29 -05:00
BearOso
6d71430806 Make sure window is visible before creating wayland vulkan context. 2023-08-25 11:47:29 -05:00
BearOso
3f0f246028 Fix context resizing and joystick detection. 2023-08-25 11:47:29 -05:00
BearOso
e42dd27cd1 Threading fixes. 2023-08-25 11:47:29 -05:00
BearOso
a438d3fa42 Threading. 2023-08-25 11:47:29 -05:00
BearOso
177a802186 Minimized fix. 2023-08-25 11:47:29 -05:00
BearOso
8a4732c9f2 Fix segfault. 2023-08-25 11:47:29 -05:00
BearOso
dd2072b28e Fix silence adder. 2023-08-25 11:47:29 -05:00
BearOso
7f20a93637 Don't double-wait. 2023-08-25 11:47:29 -05:00
Idiot
f1837aa04f Proper sleep implementation for Windows. 2023-08-25 11:47:29 -05:00
Idiot
faaf900eb7 More precise std::chrono throttle implementation. 2023-08-25 11:47:29 -05:00
BearOso
12e7f0d21f Update input rate at right locations. 2023-08-25 11:47:29 -05:00
BearOso
ec8bdaa6eb WaylandSurface: Do roundtrip to get fractional size before initialization. 2023-08-25 11:47:29 -05:00
BearOso
5e021b1abc OpaquePaintEvent. 2023-08-25 11:47:29 -05:00
BearOso
744f69a264 Windows changes. 2023-08-25 11:47:29 -05:00
BearOso
e9b4f6d7fe Work on sound driver levels. 2023-08-25 11:47:29 -05:00
BearOso
62c4686fc6 Sound buffer tweaking. 2023-08-25 11:47:29 -05:00
BearOso
bafea657e7 Working. 2023-08-25 11:47:29 -05:00
BearOso
db97e698b2 Mouse cursor and Set Size->10x. 2023-08-25 11:47:29 -05:00
BearOso
f8c71178d9 Sound reset and signal handlers. 2023-08-25 11:47:29 -05:00
BearOso
4aaf009dbb Use Qt ini file implementation. 2023-08-25 11:47:29 -05:00
BearOso
4000503584 Separation. 2023-08-25 11:47:29 -05:00
BearOso
7b6ba01760 Vulkan: Change timeouts to be more reasonable.
Report when timeout fails.
2023-08-25 11:47:29 -05:00
BearOso
c13e4d8330 Try to fix resampler to be thread-safe. 2023-08-25 11:47:29 -05:00
BearOso
376e6de81c Qt: Remove old files. Clean whitespace. 2023-08-25 11:47:29 -05:00
BearOso
fa16fbace4 Fix OpenGL ImGui shutdown. Fix config folder on Windows. 2023-08-25 11:47:29 -05:00
BearOso
16da4c2d26 Fix WASAPI preference. 2023-08-25 11:47:29 -05:00
BearOso
1b1325066b Qt port. 2023-08-25 11:47:29 -05:00
BearOso
19d0016c5a Gtk: Use GNUInstallDirs. 2023-08-24 14:19:55 -05:00
BearOso
d0c45f36b1 Gtk: Update translation misc. 2023-08-24 11:15:52 -05:00
BearOso
33d871d40e Cheats: Allow address:byte format. 2023-08-23 15:24:02 -05:00
bearoso
b488e426b3
Update TRANSLATING.txt 2023-08-23 15:13:01 -05:00
bearoso
e49165c560
win32/d3d9: Don't reset ImGui with device.
Just recreate necessary objects.
2023-07-11 15:19:50 -05:00
BearOso
81efc82f88 Move formerly Gtk sound drivers to common directory. 2023-06-07 16:06:55 -05:00
BearOso
8c5b6d012e Gtk: Simplify drivers by moving port code to gtk_sound.cpp. 2023-06-07 15:34:10 -05:00
BearOso
9a0712b258 Gtk: Rename frontend-common to common and move files to video subfolder. 2023-06-07 11:31:24 -05:00
BearOso
5526a1d905 Add missing files. 2023-06-01 18:01:08 -05:00
BearOso
fa20cd2d19 Gtk: Use glad instead of epoxy. 2023-06-01 15:57:03 -05:00
BearOso
7f41685cf7 Add glad OpenGL loader. 2023-05-31 17:28:56 -05:00
BearOso
cb6df570a4 Gtk: Make wayland surfaces toolkit-agnostic. 2023-05-31 17:08:52 -05:00
Michael Buckley
02ebcb496a
Merge pull request #852 from socantre/master
Support Nimbus+ controller in mac-joypad.mm findControls()
2023-05-30 17:29:03 -07:00
Seth Cantrell
fa7922da4a Apply recommended changes from review
https://github.com/snes9xgit/snes9x/pull/852#pullrequestreview-1451205573
2023-05-30 16:39:19 -04:00
Seth Cantrell
24cb5ce657 Support Nimbus+ controller in mac-joypad.mm findControls()
The findControls() routine was not finding several of the buttons on my Nimbus+
controller so that I could not configure those buttons in

    Settings > Controls > Nimbus+

A, B, X, Y, and the L and R shoulder buttons are detected by the existing code.
The D-pad and the 'start' and 'select' standins are not.

One odd thing that happened the first time I played with this change was that
after configuring the new buttons and loading up a game the buttons seemed to
be initially in a random state and so the game seemed to behave like some
random buttons were held down. After pressing and releasing all the new buttons
the game seemed to behave more normally. I'm not sure of my diagnosis because
it only happened the one time and I didn't have an opportunity to reproduce or
debug whatever happened.
2023-05-29 23:09:55 -04:00
BearOso
8a498a11d4 snapshot: Store fixed string instead of ROMFilename. 2023-05-10 14:14:57 -05:00
bearoso
7b9cadb3a1
Merge pull request #848 from ds22x/master
Fix Libretro netplay immediately disconnecting
2023-05-09 17:45:08 -05:00
ds22x
000aa6fa78
Fix Libretro netplay immediately disconnecting 2023-05-09 22:53:34 +02:00
BearOso
53ebf7be8f Gtk: Fix ROM info dialog when name is JIS. 2023-05-08 14:19:03 -05:00
BearOso
2313ae00b7 imgui: Silence a few warnings. 2023-05-08 14:07:29 -05:00
BearOso
8719a4761e Only use JIS->UTF8 conversion with new overlay.
Fix pixel font display.
2023-05-08 13:55:54 -05:00
BearOso
ff35034cf7 Revert "Gtk: Disable ROM info at start."
This reverts commit 354f9cbcab.
2023-05-08 12:39:33 -05:00
BearOso
c44bc75030 memmap: Fix stupid change to sjis conversion. 2023-05-08 12:38:21 -05:00
OV2
c7e6e9c211 win32: only deinit ogl imgui when ogl is initialized 2023-05-08 19:06:14 +02:00
OV2
5481bb0436 win32: actually call function 2023-05-08 19:03:06 +02:00
OV2
f0f3c5502c win32: get rid of some warnings 2023-05-08 19:02:50 +02:00
BearOso
354f9cbcab Gtk: Disable ROM info at start. 2023-05-07 16:38:45 -05:00
BearOso
82f7658574 win32: Add imgui overlay to direct3d.
memmap: Use multiline rom info message.
2023-05-07 16:06:34 -05:00
BearOso
8d8e691c89 Gtk: Add OSD ImGui check back. 2023-05-06 10:16:10 -05:00
BearOso
66df6f4182 win32: Fix casting for ancient sensitive CI tools. 2023-05-04 17:48:35 -05:00
BearOso
bad8cfc209 win32: Wire imgui up to OpenGL and Vulkan. 2023-05-04 17:36:36 -05:00
BearOso
03ac459bb1 Gtk: Remove custom in-screen font code. 2023-05-04 16:07:48 -05:00
BearOso
ebf2e9981e win32: Get win32 working with variable font. 2023-05-04 15:55:11 -05:00
BearOso
8289c775b8 imgui: Don't use base85 because of MSVC.
MSVC can't handle strings larger than 65536.
2023-05-04 12:19:29 -05:00
BearOso
04a0fdc97e imgui: Add dx9 backend. 2023-05-04 11:56:04 -05:00
BearOso
b11ecf932a imgui: Update font. Test ROM status line. 2023-05-03 16:45:56 -05:00
BearOso
c9f1e8d79b win32/glsl: Fix crash. 2023-05-01 15:55:59 -05:00
BearOso
f83525490f win32: Fix ResourceLimits.cpp location. 2023-05-01 15:42:32 -05:00
BearOso
7726cc65d5 Remove some unused, misplaced, or ancient code. 2023-05-01 15:29:39 -05:00
BearOso
b2d92e86ac Update VulkanMemoryAllocator/Hpp 2023-05-01 12:24:38 -05:00
BearOso
54aae6f5cf vulkan-headers: Update to latest. 2023-05-01 12:00:06 -05:00
BearOso
7b7ce04f29 dsp: Fix typo by omission. 2023-05-01 11:52:12 -05:00
BearOso
2afe4a11d4 CI acting funky. See if this fixes it. 2023-05-01 11:38:54 -05:00
BearOso
b383b94c8c slang: Fix CI build. 2023-04-30 17:18:39 -05:00
BearOso
6c60368fc4 netplay: Fix warnings and crash with >5 clients. 2023-04-30 16:07:45 -05:00
BearOso
cbc14ee0df sdsp: Check for invalid scale in defined way. 2023-04-30 15:51:14 -05:00
BearOso
bc98b1dee1 memmap: Simplify a heuristic to avoid UB. 2023-04-30 15:50:03 -05:00
BearOso
92b7fb2e9f shaders: More cleaning. 2023-04-27 16:54:03 -05:00
BearOso
ed695f3776 shaders: Clean up some of the OpenGL/slang code. 2023-04-27 16:20:09 -05:00
BearOso
a74769c194 memmap: Report if ROM is patched instead of "bad checksum." 2023-04-26 16:26:44 -05:00
BearOso
7f032e5234 imgui: Fix wrapping. 2023-04-26 16:26:28 -05:00
BearOso
945cd27841 Gtk: Add UI option to configure size, indicators. 2023-04-25 17:36:48 -05:00
BearOso
909d899512 Gtk/OpenGL: Add imgui support. 2023-04-25 17:36:48 -05:00
BearOso
8f3595e5d3 gtk/vulkan: Use imgui. 2023-04-25 17:36:48 -05:00
BearOso
843c5ea4a9 controls: Revert wording to 1.61 version.
Whole filename is more information. I shouldn't have
changed this.
2023-04-20 10:20:43 -05:00
BearOso
5188dd3b7c controls: Use QuickSave000 as save origin instead of QuickLoad000. 2023-04-20 10:07:33 -05:00
BearOso
bfdbc28357 vulkan: Actually submit one-time command buffer. 2023-04-17 15:42:28 -05:00
OV2
3ab8f006cb win32: fix command line arguments (fixes #840) 2023-04-12 19:34:09 +02:00
BearOso
e55b13315b Gtk: Tweak sound drivers for better performance. 2023-04-02 12:47:08 -05:00
BearOso
f2be0cc11d win32: Increase width of Vulkan/OpenGL shader label. 2023-04-01 16:48:38 -05:00
OV2
b689d43471 win32: move backdrop hotkey to other visual hotkeys, move superscope
turbo to turbo hotkeys
2023-04-01 01:35:02 +02:00
BearOso
2cb8fd06e6 win32: Hide entry control if no hotkey binding is there. 2023-03-31 17:54:36 -05:00
BearOso
2a6b040f3e win32: Add Toggle Backdrop hotkey. 2023-03-31 17:39:14 -05:00
BearOso
79e150886a Add ability to force backdrop color for sprite extraction.
Only Gtk hotkey is hooked up so far.
2023-03-31 17:09:23 -05:00
OV2
d29098ca44 win32: increase sound buffer to allow at least one frame (fixes #794) 2023-03-31 17:04:44 +02:00
OV2
0b5bad6523 win32: add separator in recent menu 2023-03-31 17:04:44 +02:00
Michael Buckley
dcaac07d78 Mac: Update version number to 1.62.3 2023-03-31 07:56:09 -07:00
BearOso
4b345bd008 Gtk: Update appimage script. 2023-03-30 14:33:05 -05:00
BearOso
8b82d48793 Update version string to 1.62.3. 2023-03-30 11:18:33 -05:00
OV2
afe8dd9f01 Resampler: force even buffer sizes in resize 2023-03-29 12:27:22 +02:00
BearOso
59c48e35a7 Gtk: Simplify makeappimage.sh 2023-03-28 12:41:15 -05:00
BearOso
835ad39026 Resampler: Allow only even buffer sizes. 2023-03-28 12:34:35 -05:00
OV2
65d07dc838 Wrap around when pushing single samples 2023-03-28 18:11:27 +02:00
OV2
33d84d31b8 libretro: remove unused S9xGetFilename, pass original rom filename when
loading (#836)
2023-03-28 18:10:18 +02:00
OV2
fb3c00460d Allow passing optional rom filename 2023-03-28 18:10:18 +02:00
BearOso
af70c62343 Gtk: Add script to make appimage. 2023-03-28 11:05:54 -05:00
OV2
4e52321a53 Copy internal registers to correct position (fixes #838) 2023-03-28 12:55:37 +02:00
BearOso
691bc9fb49 Update version to 1.62.2. 2023-03-27 17:16:39 -05:00
BearOso
d5400f77bb Fix snapshot bugs. 2023-03-27 17:16:39 -05:00
Michael Buckley
97dd443e63 Mac: Watchpoint bug fixes 2023-03-25 14:14:28 -07:00
Michael Buckley
07e456729f Mac: Fix merge issue with menu items 2023-03-25 14:08:28 -07:00
Michael Buckley
f9ecc89098 Mac: Update coyright year and version to 1.62.1 2023-03-25 14:01:32 -07:00
Michael Buckley
14677f647d Mac: Open blank window by default. This isn't how the Mac client has ever worked, but it's confused enough people recently that the only thing that changes when you launche Snes9x is the system-wide menu bar. 2023-03-25 13:59:49 -07:00
Michael Buckley
51a7c207a6 Mac: Restore Open MultiCart feature 2023-03-25 13:58:25 -07:00
Michael Buckley
7d37196f72 Mac: Fix makepath bugs introduced in cea5148 2023-03-25 13:54:13 -07:00
Michael Buckley
2e568452c0 Mac: Rebuilt Cheat Finder 2023-03-25 12:26:30 -07:00
394 changed files with 141926 additions and 18810 deletions

View file

@ -49,7 +49,7 @@ snes9x_linux-x11-amd64_task:
snes9x_freebsd-x11-amd64_task:
freebsd_instance:
image: freebsd-12-1-release-amd64
image: freebsd-13-2-release-amd64
setup_script:
- pkg install -y gmake pkgconf minizip libX11 libXext

3
.gitmodules vendored
View file

@ -13,3 +13,6 @@
[submodule "external/vulkan-headers"]
path = external/vulkan-headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "external/cubeb"]
path = external/cubeb
url = https://github.com/mozilla/cubeb.git

View file

@ -1,4 +1,4 @@
version: 1.62.1-{build}
version: 1.63-{build}
image: Visual Studio 2022

View file

@ -41,7 +41,7 @@ void S9xLandSamples (void);
void S9xClearSamples (void);
bool8 S9xMixSamples (uint8 *, int);
void S9xSetSamplesAvailableCallback (apu_callback, void *);
void S9xUpdateDynamicRate (int, int);
void S9xUpdateDynamicRate (int empty = 1, int buffer_size = 2);
#define DSP_INTERPOLATION_NONE 0
#define DSP_INTERPOLATION_LINEAR 1

View file

@ -672,9 +672,10 @@ inline void SPC_DSP::decode_brr( voice_t* v )
// Shift sample based on header
int const shift = header >> 4;
s = (s << shift) >> 1;
if ( shift >= 0xD ) // handle invalid range
s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0)
if (shift <= 12)
s = (s << shift) >> 1;
else
s &= ~0x7ff;
// Apply IIR filter (8 is the most commonly used)
int const filter = header & 0x0C;
@ -1244,6 +1245,8 @@ void SPC_DSP::load( uint8_t const regs [register_count] )
{
memcpy( m.external_regs, regs, sizeof m.regs );
memset( m.regs, 0, sizeof m.regs);
m.regs[66] = 0x01;
m.regs[82] = 0x01;
m.regs[r_flg] = 0xE0;
memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );

View file

@ -9,19 +9,15 @@
#include <cstring>
#include <cassert>
#if __cplusplus >= 201103L
#include <cstdint>
#else
#include <stdint.h>
#endif
#include <cmath>
class Resampler
{
public:
int size;
volatile int end;
int buffer_size;
int start;
volatile int start;
int16_t *buffer;
float r_step;
@ -65,10 +61,9 @@ class Resampler
Resampler(int num_samples)
{
this->buffer_size = num_samples;
buffer = new int16_t[this->buffer_size];
buffer = NULL;
resize(num_samples);
r_step = 1.0;
clear();
}
~Resampler()
@ -88,7 +83,7 @@ class Resampler
return;
start = 0;
size = 0;
end = 0;
memset(buffer, 0, buffer_size * 2);
r_frac = 0.0;
@ -96,18 +91,42 @@ class Resampler
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
}
inline void dump(int num_samples)
{
if (num_samples > 0 && space_filled() >= num_samples)
start = (start + num_samples) % buffer_size;
}
inline void add_silence(int num_samples)
{
if (num_samples > 0 && space_empty() < num_samples)
return;
int first_block_size = min(num_samples, buffer_size - end);
memset(buffer + end, 0, first_block_size * 2);
if (num_samples > first_block_size)
memset(buffer, 0, (num_samples - first_block_size) * 2);
end = (end + num_samples) % buffer_size;
return;
}
inline bool pull(int16_t *dst, int num_samples)
{
if (space_filled() < num_samples)
return false;
memcpy(dst, buffer + start, min(num_samples, buffer_size - start) * 2);
int first_block_size = buffer_size - start;
if (num_samples > (buffer_size - start))
memcpy(dst + (buffer_size - start), buffer, (num_samples - (buffer_size - start)) * 2);
memcpy(dst, buffer + start, min(num_samples, first_block_size) * 2);
if (num_samples > first_block_size)
memcpy(dst + first_block_size, buffer, (num_samples - first_block_size) * 2);
start = (start + num_samples) % buffer_size;
size -= num_samples;
return true;
}
@ -116,12 +135,9 @@ class Resampler
{
if (space_empty() >= 2)
{
int end = start + size;
if (end >= buffer_size)
end -= buffer_size;
buffer[end] = l;
buffer[end + 1] = r;
size += 2;
end = (end + 2) % buffer_size;
}
}
@ -130,17 +146,14 @@ class Resampler
if (space_empty() < num_samples)
return false;
int end = start + size;
if (end > buffer_size)
end -= buffer_size;
int first_write_size = min(num_samples, buffer_size - end);
int first_block_size = min(num_samples, buffer_size - end);
memcpy(buffer + end, src, first_write_size * 2);
memcpy(buffer + end, src, first_block_size * 2);
if (num_samples > first_write_size)
memcpy(buffer, src + first_write_size, (num_samples - first_write_size) * 2);
if (num_samples > first_block_size)
memcpy(buffer, src + first_block_size, (num_samples - first_block_size) * 2);
size += num_samples;
end = (end + num_samples) % buffer_size;
return true;
}
@ -157,7 +170,7 @@ class Resampler
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
int o_position = 0;
while (o_position < num_samples && size > 0)
while (o_position < num_samples && space_filled() >= 2)
{
int s_left = buffer[start];
int s_right = buffer[start + 1];
@ -192,23 +205,26 @@ class Resampler
start += 2;
if (start >= buffer_size)
start -= buffer_size;
size -= 2;
}
}
}
inline int space_empty(void) const
{
return buffer_size - size;
return buffer_size - 2 - space_filled();
}
inline int space_filled(void) const
{
int size = end - start;
if (size < 0)
size += buffer_size;
return size;
}
inline int avail(void)
{
int size = space_filled();
//If we are outputting the exact same ratio as the input, find out directly from the input buffer
if (r_step == 1.0)
return size;
@ -220,6 +236,9 @@ class Resampler
{
if (buffer)
delete[] buffer;
// Only allow even buffer sizes
if (num_samples & 1)
num_samples++;
buffer_size = num_samples;
buffer = new int16_t[buffer_size];
clear();

View file

@ -7,17 +7,20 @@
#ifndef _CHEATS_H_
#define _CHEATS_H_
#include "port.h"
#include <cstdint>
#include <string>
#include <vector>
using bool8 = uint8_t;
struct SCheat
{
uint32 address;
uint8 byte;
uint8 saved_byte;
uint32_t address;
uint8_t byte;
uint8_t saved_byte;
bool8 conditional;
bool8 cond_true;
uint8 cond_byte;
uint8_t cond_byte;
bool8 enabled;
};
@ -32,14 +35,14 @@ struct SCheatData
{
std::vector<struct SCheatGroup> group;
bool8 enabled;
uint8 CWRAM[0x20000];
uint8 CSRAM[0x80000];
uint8 CIRAM[0x2000];
uint8 *RAM;
uint8 *FillRAM;
uint8 *SRAM;
uint32 ALL_BITS[0x32000 >> 5];
uint8 CWatchRAM[0x32000];
uint8_t CWRAM[0x20000];
uint8_t CSRAM[0x80000];
uint8_t CIRAM[0x2000];
uint8_t *RAM;
uint8_t *FillRAM;
uint8_t *SRAM;
uint32_t ALL_BITS[0x32000 >> 5];
uint8_t CWatchRAM[0x32000];
};
struct Watch
@ -47,7 +50,7 @@ struct Watch
bool on;
int size;
int format;
uint32 address;
uint32_t address;
char buf[12];
char desc[32];
};
@ -74,12 +77,13 @@ extern SCheatData Cheat;
extern Watch watches[16];
int S9xAddCheatGroup(const std::string &name, const std::string &cheat);
int S9xModifyCheatGroup(uint32 index, const std::string &name, const std::string &cheat);
void S9xEnableCheatGroup(uint32 index);
void S9xDisableCheatGroup(uint32 index);
int S9xModifyCheatGroup(uint32_t index, const std::string &name, const std::string &cheat);
void S9xEnableCheatGroup(uint32_t index);
void S9xDisableCheatGroup(uint32_t index);
void S9xDeleteCheats(void);
std::string S9xCheatGroupToText(uint32 index);
void S9xDeleteCheatGroup(uint32 index);
std::string S9xCheatGroupToText(const SCheatGroup &g);
std::string S9xCheatGroupToText(uint32_t index);
void S9xDeleteCheatGroup(uint32_t index);
bool8 S9xLoadCheatFile(const std::string &filename);
bool8 S9xSaveCheatFile(const std::string &filename);
void S9xUpdateCheatsInMemory(void);
@ -92,8 +96,8 @@ void S9xInitCheatData (void);
void S9xInitWatchedAddress (void);
void S9xStartCheatSearch (SCheatData *);
void S9xSearchForChange (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, bool8, bool8);
void S9xSearchForValue (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32, bool8, bool8);
void S9xSearchForAddress (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32, bool8);
void S9xSearchForValue (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32_t, bool8, bool8);
void S9xSearchForAddress (SCheatData *, S9xCheatComparisonType, S9xCheatDataSize, uint32_t, bool8);
void S9xOutputCheatSearchResults (SCheatData *);
#endif

View file

@ -8,6 +8,7 @@
#include "cheats.h"
#include "snes9x.h"
#include "memmap.h"
#include <cassert>
static inline uint8 S9xGetByteFree(uint32 Address)
{
@ -322,6 +323,8 @@ void S9xEnableCheat(SCheat &c)
void S9xEnableCheatGroup(uint32 num)
{
assert(num < Cheat.group.size());
for (auto &c : Cheat.group[num].cheat)
S9xEnableCheat(c);
@ -434,6 +437,9 @@ SCheat S9xTextToCheat(const std::string &text)
{
c.conditional = true;
}
else if (sscanf(text.c_str(), "%x : %x", &c.address, &byte) == 2)
{
}
else if (sscanf(text.c_str(), "%x / %x", &c.address, &byte) == 2)
{
}
@ -512,10 +518,14 @@ int S9xModifyCheatGroup(uint32 num, const std::string &name, const std::string &
if (num >= Cheat.group.size())
return -1;
bool enabled = Cheat.group[num].enabled;
S9xDisableCheatGroup(num);
Cheat.group[num] = S9xCreateCheatGroup(name, cheat);
if (enabled)
S9xEnableCheatGroup(num);
return num;
}
@ -531,7 +541,7 @@ std::string S9xCheatToText(const SCheat &c)
return std::string(output);
}
std::string S9xCheatGroupToText(SCheatGroup &g)
std::string S9xCheatGroupToText(const SCheatGroup &g)
{
std::string text = "";

View file

@ -4,10 +4,11 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_SOUND_DRIVER_H
#define __GTK_SOUND_DRIVER_H
#ifndef __S9X_SOUND_DRIVER_HPP
#define __S9X_SOUND_DRIVER_HPP
#include "gtk_s9x.h"
#include <cstdint>
#include <tuple>
class S9xSoundDriver
{
@ -15,11 +16,14 @@ class S9xSoundDriver
virtual ~S9xSoundDriver()
{
}
virtual bool write_samples(int16_t *data, int samples) = 0;
virtual int space_free() = 0;
virtual std::pair<int, int> buffer_level() = 0;
virtual void init() = 0;
virtual void terminate() = 0;
virtual bool open_device() = 0;
virtual void deinit() = 0;
virtual bool open_device(int playback_rate, int buffer_size) = 0;
virtual void start() = 0;
virtual void stop() = 0;
};
#endif /* __GTK_SOUND_DRIVER_H */
#endif /* __S9X_SOUND_DRIVER_HPP */

View file

@ -4,10 +4,7 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_sound_driver_alsa.h"
#include "gtk_s9x.h"
#include "snes9x.h"
#include "apu/apu.h"
#include "s9x_sound_driver_alsa.hpp"
#include <fcntl.h>
#include <sys/ioctl.h>
@ -16,27 +13,22 @@
S9xAlsaSoundDriver::S9xAlsaSoundDriver()
{
pcm = {};
sound_buffer.clear();
}
void S9xAlsaSoundDriver::init()
{
}
void S9xAlsaSoundDriver::terminate()
void S9xAlsaSoundDriver::deinit()
{
stop();
S9xSetSamplesAvailableCallback(nullptr, nullptr);
if (pcm)
{
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
pcm = nullptr;
}
sound_buffer.clear();
}
void S9xAlsaSoundDriver::start()
@ -47,16 +39,17 @@ void S9xAlsaSoundDriver::stop()
{
}
bool S9xAlsaSoundDriver::open_device()
bool S9xAlsaSoundDriver::open_device(int playback_rate, int buffer_size_ms)
{
int err;
unsigned int periods = 8;
unsigned int buffer_size = gui_config->sound_buffer_size * 1000;
unsigned int buffer_size = buffer_size_ms * 1000;
snd_pcm_sw_params_t *sw_params;
snd_pcm_hw_params_t *hw_params;
snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
unsigned int min = 0;
unsigned int max = 0;
unsigned int playback_rate_param = 0;
printf("ALSA sound driver initializing...\n");
printf(" --> (Device: default)...\n");
@ -69,8 +62,8 @@ bool S9xAlsaSoundDriver::open_device()
}
printf(" --> (16-bit Stereo, %dhz, %d ms)...\n",
Settings.SoundPlaybackRate,
gui_config->sound_buffer_size);
playback_rate,
buffer_size_ms);
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_hw_params_any(pcm, hw_params);
@ -82,12 +75,14 @@ bool S9xAlsaSoundDriver::open_device()
snd_pcm_hw_params_get_rate_min(hw_params, &min, nullptr);
snd_pcm_hw_params_get_rate_max(hw_params, &max, nullptr);
printf(" --> Available rates: %d to %d\n", min, max);
if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min)
if (playback_rate > (int)max || playback_rate < (int)min)
{
printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
Settings.SoundPlaybackRate = max;
printf(" Rate %d not available. Using %d instead.\n", playback_rate, max);
playback_rate = max;
}
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, nullptr);
playback_rate_param = playback_rate;
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &playback_rate_param, nullptr);
snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, nullptr);
snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, nullptr);
@ -121,7 +116,7 @@ bool S9xAlsaSoundDriver::open_device()
snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (alsa_buffer_size / 2));
err = snd_pcm_sw_params(pcm, sw_params);
output_buffer_size = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);
output_buffer_size_bytes = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);
if (err < 0)
{
@ -131,10 +126,6 @@ bool S9xAlsaSoundDriver::open_device()
printf("OK\n");
S9xSetSamplesAvailableCallback([](void *userdata) {
((decltype(this)) userdata)->samples_available();;
}, this);
return true;
close_fail:
@ -148,7 +139,7 @@ fail:
return false;
}
void S9xAlsaSoundDriver::samples_available()
bool S9xAlsaSoundDriver::write_samples(int16_t *data, int samples)
{
snd_pcm_sframes_t frames_written, frames;
size_t bytes;
@ -158,56 +149,28 @@ void S9xAlsaSoundDriver::samples_available()
if (frames < 0)
{
frames = snd_pcm_recover(pcm, frames, 1);
return;
return false;
}
if (Settings.DynamicRateControl)
bool result = true;
snd_pcm_nonblock(pcm, 0);
if (frames > samples / 2)
{
S9xUpdateDynamicRate(snd_pcm_frames_to_bytes(pcm, frames),
output_buffer_size);
}
int snes_frames_available = S9xGetSampleCount() >> 1;
if (Settings.DynamicRateControl && !Settings.SoundSync)
{
// Using rate control, we should always keep the emulator's sound buffers empty to
// maintain an accurate measurement.
if (frames < snes_frames_available)
{
S9xClearSamples();
return;
}
}
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
snd_pcm_nonblock(pcm, 0);
frames = snes_frames_available;
}
else
{
snd_pcm_nonblock(pcm, 1);
frames = MIN(frames, snes_frames_available);
frames = samples / 2;
result = false;
}
bytes = snd_pcm_frames_to_bytes(pcm, frames);
if (bytes <= 0)
return;
if (sound_buffer.size() < bytes)
sound_buffer.resize(bytes);
S9xMixSamples(sound_buffer.data(), frames * 2);
return false;
frames_written = 0;
while (frames_written < frames)
{
int result;
result = snd_pcm_writei(pcm,
&sound_buffer[snd_pcm_frames_to_bytes(pcm, frames_written)],
&data[snd_pcm_frames_to_bytes(pcm, frames_written) / 2],
frames - frames_written);
if (result < 0)
@ -224,4 +187,16 @@ void S9xAlsaSoundDriver::samples_available()
frames_written += result;
}
}
return result;
}
int S9xAlsaSoundDriver::space_free()
{
return snd_pcm_avail(pcm) * 2;
}
std::pair<int, int> S9xAlsaSoundDriver::buffer_level()
{
return { snd_pcm_avail(pcm), output_buffer_size_bytes / 4 };
}

View file

@ -4,28 +4,28 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_SOUND_DRIVER_ALSA_H
#define __GTK_SOUND_DRIVER_ALSA_H
#ifndef __S9X_SOUND_DRIVER_ALSA_HPP
#define __S9X_SOUND_DRIVER_ALSA_HPP
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
#include "s9x_sound_driver.hpp"
#include "alsa/asoundlib.h"
class S9xAlsaSoundDriver : public S9xSoundDriver
{
public:
S9xAlsaSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size_ms) override;
void start() override;
void stop() override;
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
private:
snd_pcm_t *pcm;
std::vector<uint8_t> sound_buffer;
int output_buffer_size;
int output_buffer_size_bytes;
};
#endif /* __GTK_SOUND_DRIVER_ALSA_H */
#endif /* __S9X_SOUND_DRIVER_ALSA_HPP */

View file

@ -0,0 +1,138 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "s9x_sound_driver_cubeb.hpp"
#include <cstdio>
bool S9xCubebSoundDriver::write_samples(int16_t *data, int samples)
{
bool retval = true;
auto empty = buffer.space_empty();
if (samples > empty)
{
retval = false;
buffer.dump(buffer.buffer_size / 2 - empty);
}
buffer.push(data, samples);
return retval;
}
S9xCubebSoundDriver::S9xCubebSoundDriver()
{
}
S9xCubebSoundDriver::~S9xCubebSoundDriver()
{
deinit();
}
void S9xCubebSoundDriver::init()
{
if (!context)
cubeb_init(&context, "Snes9x", nullptr);
stop();
}
void S9xCubebSoundDriver::deinit()
{
stop();
if (stream)
{
cubeb_stream_destroy(stream);
stream = nullptr;
}
if (context)
{
cubeb_destroy(context);
context = nullptr;
}
}
void S9xCubebSoundDriver::start()
{
if (stream)
cubeb_stream_start(stream);
}
void S9xCubebSoundDriver::stop()
{
if (stream)
cubeb_stream_stop(stream);
}
void state_callback(cubeb_stream *stream, void *user_ptr, cubeb_state state)
{
}
long data_callback(cubeb_stream *stream, void *user_ptr,
void const *input_buffer,
void *output_buffer, long nframes)
{
return ((S9xCubebSoundDriver *)user_ptr)->data_callback(stream, input_buffer, output_buffer, nframes);
}
long S9xCubebSoundDriver::data_callback(cubeb_stream *stream, void const *input_buffer, void *output_buffer, long nframes)
{
auto avail = buffer.avail();
if (avail < nframes * 2)
{
auto zeroed_samples = nframes * 2 - avail;
memset(output_buffer, 0, zeroed_samples);
buffer.read((int16_t *)output_buffer + zeroed_samples, nframes * 2 - zeroed_samples);
buffer.add_silence(buffer.buffer_size / 2);
}
else
{
buffer.read((int16_t *)output_buffer, nframes * 2);
}
return nframes;
}
bool S9xCubebSoundDriver::open_device(int playback_rate, int buffer_size)
{
cubeb_stream_params params{};
params.channels = 2;
params.format = CUBEB_SAMPLE_S16LE;
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.rate = playback_rate;
params.prefs = CUBEB_STREAM_PREF_NONE;
uint32_t suggested_latency = playback_rate * buffer_size / 1000;
uint32_t min_latency;
cubeb_get_min_latency(context, &params, &min_latency);
auto retval = cubeb_stream_init(context, &stream, "Snes9x",
nullptr, nullptr,
nullptr, &params,
min_latency,
&::data_callback,
&state_callback,
this);
if (retval != CUBEB_OK)
{
printf("Failed to start stream. Error: %d!\n", retval);
stream = nullptr;
return false;
}
buffer.resize(suggested_latency * 2);
return true;
}
int S9xCubebSoundDriver::space_free()
{
return buffer.space_empty();
}
std::pair<int, int> S9xCubebSoundDriver::buffer_level()
{
return { buffer.space_empty(), buffer.buffer_size };
}

View file

@ -0,0 +1,36 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __S9X_SOUND_DRIVER_CUBEB_HPP
#define __S9X_SOUND_DRIVER_CUBEB_HPP
#include "s9x_sound_driver.hpp"
#include <cstdint>
#include "cubeb/cubeb.h"
#include "../../apu/resampler.h"
class S9xCubebSoundDriver : public S9xSoundDriver
{
public:
S9xCubebSoundDriver();
~S9xCubebSoundDriver();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size) override;
void start() override;
void stop() override;
long data_callback(cubeb_stream *stream, void const *input_buffer, void *output_buffer, long nframes);
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
private:
Resampler buffer;
cubeb *context = nullptr;
cubeb_stream *stream = nullptr;
};
#endif /* __S9X_SOUND_DRIVER_SDL_HPP */

View file

@ -4,48 +4,47 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_sound_driver_oss.h"
#include "gtk_s9x.h"
#include "snes9x.h"
#include "apu/apu.h"
#include "s9x_sound_driver_oss.hpp"
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <sys/time.h>
#include <unistd.h>
#include <cstdio>
static void oss_samples_available(void *data)
static inline int log2(int num)
{
((S9xOSSSoundDriver *)data)->samples_available();
int power;
if (num < 1)
return 0;
for (power = 0; num > 1; power++)
{
num >>= 1;
}
return power;
}
S9xOSSSoundDriver::S9xOSSSoundDriver()
{
filedes = -1;
sound_buffer = NULL;
sound_buffer_size = 0;
}
void S9xOSSSoundDriver::init()
{
}
void S9xOSSSoundDriver::terminate()
void S9xOSSSoundDriver::deinit()
{
stop();
S9xSetSamplesAvailableCallback(NULL, NULL);
if (filedes >= 0)
{
close(filedes);
}
if (sound_buffer)
{
free(sound_buffer);
sound_buffer = NULL;
}
}
void S9xOSSSoundDriver::start()
@ -56,16 +55,16 @@ void S9xOSSSoundDriver::stop()
{
}
bool S9xOSSSoundDriver::open_device()
bool S9xOSSSoundDriver::open_device(int playback_rate, int buffer_size_ms)
{
int temp;
audio_buf_info info;
output_buffer_size = (gui_config->sound_buffer_size * Settings.SoundPlaybackRate) / 1000;
output_buffer_size_bytes = (buffer_size_ms * playback_rate) / 1000;
output_buffer_size *= 4;
if (output_buffer_size < 256)
output_buffer_size = 256;
output_buffer_size_bytes *= 4;
if (output_buffer_size_bytes < 256)
output_buffer_size_bytes = 256;
printf("OSS sound driver initializing...\n");
@ -115,31 +114,29 @@ bool S9xOSSSoundDriver::open_device()
printf("OK\n");
printf(" --> (Frequency: %d)...", Settings.SoundPlaybackRate);
if (ioctl(filedes, SNDCTL_DSP_SPEED, &Settings.SoundPlaybackRate) < 0)
printf(" --> (Frequency: %d)...", playback_rate);
if (ioctl(filedes, SNDCTL_DSP_SPEED, &playback_rate) < 0)
goto close_fail;
printf("OK\n");
/* OSS requires a power-of-two buffer size, first 16 bits are the number
* of fragments to generate, second 16 are the respective power-of-two. */
temp = (4 << 16) | (S9xSoundBase2log(output_buffer_size / 4));
temp = (4 << 16) | (log2(output_buffer_size_bytes / 4));
if (ioctl(filedes, SNDCTL_DSP_SETFRAGMENT, &temp) < 0)
goto close_fail;
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
output_buffer_size = info.fragsize * info.fragstotal;
output_buffer_size_bytes = info.fragsize * info.fragstotal;
printf(" --> (Buffer size: %d bytes, %dms latency)...",
output_buffer_size,
(output_buffer_size * 250) / Settings.SoundPlaybackRate);
output_buffer_size_bytes,
(output_buffer_size_bytes * 250) / playback_rate);
printf("OK\n");
S9xSetSamplesAvailableCallback(oss_samples_available, this);
return true;
close_fail:
@ -152,68 +149,41 @@ fail:
return false;
}
void S9xOSSSoundDriver::samples_available()
int S9xOSSSoundDriver::space_free()
{
audio_buf_info info;
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
return info.bytes / 2;
}
std::pair<int, int> S9xOSSSoundDriver::buffer_level()
{
return { space_free(), output_buffer_size_bytes / 2};
}
bool S9xOSSSoundDriver::write_samples(int16_t *data, int samples)
{
audio_buf_info info;
int samples_to_write;
int bytes_to_write;
int bytes_written;
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate(info.bytes, output_buffer_size);
}
if (samples > info.bytes / 2)
samples = info.bytes / 2;
samples_to_write = S9xGetSampleCount();
if (Settings.DynamicRateControl && !Settings.SoundSync)
{
// Using rate control, we should always keep the emulator's sound buffers empty to
// maintain an accurate measurement.
if (samples_to_write > (info.bytes >> 1))
{
S9xClearSamples();
return;
}
}
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
while (info.bytes >> 1 < samples_to_write)
{
int usec_to_sleep = ((samples_to_write >> 1) - (info.bytes >> 2)) * 10000 /
(Settings.SoundPlaybackRate / 100);
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
ioctl(filedes, SNDCTL_DSP_GETOSPACE, &info);
}
}
else
{
samples_to_write = MIN(info.bytes >> 1, samples_to_write) & ~1;
}
if (samples_to_write < 0)
return;
if (sound_buffer_size < samples_to_write * 2)
{
sound_buffer = (uint8 *)realloc(sound_buffer, samples_to_write * 2);
sound_buffer_size = samples_to_write * 2;
}
S9xMixSamples(sound_buffer, samples_to_write);
if (samples == 0)
return false;
bytes_written = 0;
bytes_to_write = samples_to_write * 2;
bytes_to_write = samples * 2;
while (bytes_to_write > bytes_written)
{
int result;
result = write(filedes,
((char *)sound_buffer) + bytes_written,
((char *)data) + bytes_written,
bytes_to_write - bytes_written);
if (result < 0)
@ -221,4 +191,6 @@ void S9xOSSSoundDriver::samples_available()
bytes_written += result;
}
return true;
}

View file

@ -4,28 +4,27 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_SOUND_DRIVER_OSS_H
#define __GTK_SOUND_DRIVER_OSS_H
#ifndef __S9X_SOUND_DRIVER_OSS_HPP
#define __S9X_SOUND_DRIVER_OSS_HPP
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
#include "s9x_sound_driver.hpp"
class S9xOSSSoundDriver : public S9xSoundDriver
{
public:
S9xOSSSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size_ms) override;
void start() override;
void stop() override;
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
private:
int filedes;
uint8 *sound_buffer;
int sound_buffer_size;
int output_buffer_size;
int output_buffer_size_bytes;
};
#endif /* __GTK_SOUND_DRIVER_OSS_H */
#endif /* __S9X_SOUND_DRIVER_OSS_HPP */

View file

@ -0,0 +1,215 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "s9x_sound_driver_portaudio.hpp"
#include <cstring>
#include <cstdio>
#include <cstdint>
#include <vector>
#include <string>
S9xPortAudioSoundDriver::S9xPortAudioSoundDriver()
{
audio_stream = NULL;
}
S9xPortAudioSoundDriver::~S9xPortAudioSoundDriver()
{
deinit();
}
void S9xPortAudioSoundDriver::init()
{
PaError err;
err = Pa_Initialize();
if (err != paNoError)
fprintf(stderr,
"Couldn't initialize PortAudio: %s\n",
Pa_GetErrorText(err));
}
void S9xPortAudioSoundDriver::deinit()
{
stop();
Pa_Terminate();
}
void S9xPortAudioSoundDriver::start()
{
PaError err;
if (audio_stream != NULL)
{
if ((Pa_IsStreamActive(audio_stream)))
return;
err = Pa_StartStream(audio_stream);
if (err != paNoError)
{
fprintf(stderr, "Error: %s\n", Pa_GetErrorText(err));
}
}
}
void S9xPortAudioSoundDriver::stop()
{
if (audio_stream != NULL)
{
Pa_StopStream(audio_stream);
}
}
bool S9xPortAudioSoundDriver::tryHostAPI(int index)
{
auto hostapi_info = Pa_GetHostApiInfo(index);
if (!hostapi_info)
{
printf("Host API #%d has no info\n", index);
return false;
}
auto device_info = Pa_GetDeviceInfo(hostapi_info->defaultOutputDevice);
if (!device_info)
{
printf("(%s)...No device info available.\n", hostapi_info->name);
return false;
}
PaStreamParameters param{};
param.device = hostapi_info->defaultOutputDevice;
param.suggestedLatency = buffer_size_ms * 0.001;
param.channelCount = 2;
param.sampleFormat = paInt16;
param.hostApiSpecificStreamInfo = NULL;
printf("(%s : %s, latency %dms)...\n",
hostapi_info->name,
device_info->name,
(int)(param.suggestedLatency * 1000.0));
auto err = Pa_OpenStream(&audio_stream,
NULL,
&param,
playback_rate,
0,
paNoFlag,
NULL,
NULL);
int frames = Pa_GetStreamWriteAvailable(audio_stream);
if (frames < 0)
{
Pa_Sleep(10);
frames = Pa_GetStreamWriteAvailable(audio_stream);
}
printf("PortAudio set buffer size to %d frames.\n", frames);
output_buffer_size = frames;
if (err == paNoError)
{
printf("OK\n");
return true;
}
else
{
printf("Failed (%s)\n", Pa_GetErrorText(err));
return false;
}
}
bool S9xPortAudioSoundDriver::open_device(int playback_rate, int buffer_size_ms)
{
const PaDeviceInfo *device_info;
const PaHostApiInfo *hostapi_info;
PaError err = paNoError;
if (audio_stream != NULL)
{
printf("Shutting down sound for reset\n");
err = Pa_CloseStream(audio_stream);
if (err != paNoError)
{
fprintf(stderr, "Couldn't reset audio stream.\nError: %s\n", Pa_GetErrorText(err));
return true;
}
audio_stream = NULL;
}
this->playback_rate = playback_rate;
this->buffer_size_ms = buffer_size_ms;
printf("PortAudio sound driver initializing...\n");
int host = Pa_GetDefaultHostApi();
#ifdef _WIN32
// Look for WASAPI
for (int i = 0; i < Pa_GetHostApiCount(); i++)
{
auto hostapi_info = Pa_GetHostApiInfo(i);
std::string str(hostapi_info->name);
if (str == "Windows WASAPI")
{
host = i;
break;
}
}
#endif
if (tryHostAPI(host))
return true;
for (int i = 0; i < Pa_GetHostApiCount(); i++)
{
if (host != i)
if (tryHostAPI(i))
return true;
}
fprintf(stderr, "Couldn't initialize sound\n");
return false;
}
int S9xPortAudioSoundDriver::space_free()
{
return Pa_GetStreamWriteAvailable(audio_stream) * 2;
}
std::pair<int, int> S9xPortAudioSoundDriver::buffer_level()
{
return { Pa_GetStreamWriteAvailable(audio_stream), output_buffer_size };
}
bool S9xPortAudioSoundDriver::write_samples(int16_t *data, int samples)
{
int frames;
frames = Pa_GetStreamWriteAvailable(audio_stream);
if (frames == output_buffer_size)
{
// Prime the stream
std::vector<int16_t> tmp(output_buffer_size);
memset(tmp.data(), 0, output_buffer_size << 1);
Pa_WriteStream(audio_stream, tmp.data(), output_buffer_size >> 1);
frames -= output_buffer_size >> 1;
}
bool retval = true;
if (frames > samples / 2)
{
retval = false;
frames = samples / 2;
}
Pa_WriteStream(audio_stream, data, frames);
return retval;
}

View file

@ -4,32 +4,35 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_SOUND_DRIVER_PORTAUDIO_H
#define __GTK_SOUND_DRIVER_PORTAUDIO_H
#ifndef __S9X_SOUND_DRIVER_PORTAUDIO_HPP
#define __S9X_SOUND_DRIVER_PORTAUDIO_HPP
#include <errno.h>
#include <portaudio.h>
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
#include "s9x_sound_driver.hpp"
class S9xPortAudioSoundDriver : public S9xSoundDriver
{
public:
S9xPortAudioSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
~S9xPortAudioSoundDriver();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size) override;
void start() override;
void stop() override;
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
void samples_available();
bool tryHostAPI(int index);
private:
void set_buffer_min(int frames);
PaStream *audio_stream;
int sound_buffer_size;
uint8 *sound_buffer;
int playback_rate;
int buffer_size_ms;
int output_buffer_size;
};
#endif /* __GTK_SOUND_DRIVER_PORTAUDIO_H */
#endif /* __S9X_SOUND_DRIVER_PORTAUDIO_HPP */

View file

@ -4,14 +4,16 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_sound_driver_pulse.h"
#include "gtk_s9x.h"
#include "snes9x.h"
#include "apu/apu.h"
#include "s9x_sound_driver_pulse.hpp"
#include <cstring>
#include <cstdio>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "fmt/format.h"
#include "messages.h"
#include "snes9x.h"
S9xPulseSoundDriver::S9xPulseSoundDriver()
{
@ -26,10 +28,8 @@ void S9xPulseSoundDriver::init()
buffer_size = {};
}
void S9xPulseSoundDriver::terminate()
void S9xPulseSoundDriver::deinit()
{
S9xSetSamplesAvailableCallback(nullptr, nullptr);
if (mainloop)
pa_threaded_mainloop_stop(mainloop);
@ -104,27 +104,27 @@ static void stream_state_callback(pa_stream *p, void *userdata)
}
}
bool S9xPulseSoundDriver::open_device()
bool S9xPulseSoundDriver::open_device(int playback_rate, int buffer_size_ms)
{
init();
pa_sample_spec ss;
ss.channels = 2;
ss.format = PA_SAMPLE_S16NE;
ss.rate = Settings.SoundPlaybackRate;
ss.rate = playback_rate;
pa_buffer_attr buffer_attr;
buffer_attr.fragsize = -1;
buffer_attr.tlength = pa_usec_to_bytes(gui_config->sound_buffer_size * 1000, &ss);
buffer_attr.tlength = 2 * pa_usec_to_bytes(buffer_size_ms * 1000, &ss);
buffer_attr.maxlength = buffer_attr.tlength * 2;
buffer_attr.minreq = -1;
buffer_attr.prebuf = -1;
buffer_attr.minreq = pa_usec_to_bytes(3000, &ss);
buffer_attr.prebuf = buffer_attr.tlength / 2;
printf("PulseAudio sound driver initializing...\n");
S9xMessage(S9X_INFO, S9X_NO_INFO, "Initializing PulseAudio sound driver…");
printf(" --> (%dhz, 16-bit Stereo, %dms)...",
Settings.SoundPlaybackRate,
gui_config->sound_buffer_size);
S9xMessage(S9X_INFO, S9X_NO_INFO,
fmt::format(" --> ({0:Ld} Hz, 16bit stereo, {1:Ld} ms)…",
playback_rate,
buffer_size_ms).c_str());
int err = PA_ERR_UNKNOWN;
mainloop = pa_threaded_mainloop_new();
@ -146,7 +146,7 @@ bool S9xPulseSoundDriver::open_device()
if (pa_stream_connect_playback(stream,
nullptr,
&buffer_attr,
PA_STREAM_ADJUST_LATENCY,
PA_STREAM_EARLY_REQUESTS,
nullptr,
nullptr) < 0)
{
@ -163,77 +163,78 @@ bool S9xPulseSoundDriver::open_device()
buffer_size = actual_buffer_attr->tlength;
printf("OK\n");
S9xSetSamplesAvailableCallback([](void *userdata) {
((decltype(this)) userdata)->samples_available();;
}, this);
S9xMessage(S9X_INFO, S9X_NO_INFO, "OK");
return true;
}
void S9xPulseSoundDriver::samples_available()
int S9xPulseSoundDriver::space_free()
{
lock();
size_t bytes = pa_stream_writable_size(stream);
unlock();
return bytes / 2;
}
std::pair<int, int> S9xPulseSoundDriver::buffer_level()
{
lock();
size_t bytes = pa_stream_writable_size(stream);
auto buffer_attr = pa_stream_get_buffer_attr(stream);
unlock();
buffer_size = buffer_attr->tlength;
return { bytes, buffer_size };
}
if (Settings.DynamicRateControl)
{
S9xUpdateDynamicRate(bytes, buffer_size);
}
size_t samples = S9xGetSampleCount();
int frames_available = samples / 2;
int frames_writable = bytes / 4;
if (frames_writable < frames_available)
{
if (Settings.DynamicRateControl && !Settings.SoundSync)
{
S9xClearSamples();
return;
}
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
{
int usec_to_sleep = (frames_available - frames_writable) * 10000 /
(Settings.SoundPlaybackRate / 100);
usleep(usec_to_sleep > 0 ? usec_to_sleep : 0);
lock();
bytes = pa_stream_writable_size(stream);
unlock();
}
}
bytes = MIN(bytes, samples * 2) & ~1;
if (!bytes)
return;
bool S9xPulseSoundDriver::write_samples(int16_t *data, int samples)
{
bool retval = true;
lock();
size_t bytes = pa_stream_writable_size(stream);
unlock();
void *output_buffer;;
if (draining)
{
if (bytes > (size_t)buffer_size / 2)
{
return false;
}
else
{
draining = false;
}
}
if ((size_t)samples * 2 > bytes)
{
draining = true;
return false;
}
if ((size_t)samples * 2 < bytes)
bytes = samples * 2;
if (bytes == 0)
return false;
lock();
void *output_buffer;
if (pa_stream_begin_write(stream, &output_buffer, &bytes) != 0)
{
pa_stream_flush(stream, nullptr, nullptr);
unlock();
return;
return false;
}
if (bytes <= 0 || !output_buffer)
{
unlock();
return;
return false;
}
S9xMixSamples((uint8_t *)output_buffer, bytes >> 1);
std::memcpy(output_buffer, data, bytes);
pa_stream_write(stream, output_buffer, bytes, nullptr, 0, PA_SEEK_RELATIVE);
unlock();
return retval;
}

View file

@ -4,33 +4,35 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_SOUND_DRIVER_PULSE_H
#define __GTK_SOUND_DRIVER_PULSE_H
#ifndef __S9X_SOUND_DRIVER_PULSE_HPP
#define __S9X_SOUND_DRIVER_PULSE_HPP
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
#include "s9x_sound_driver.hpp"
#include "pulse/pulseaudio.h"
class S9xPulseSoundDriver : public S9xSoundDriver
{
public:
S9xPulseSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void samples_available();
void lock();
void unlock();
void wait();
void init() override;
void deinit() override;
bool write_samples(int16_t *data, int samples) override;
bool open_device(int playback_rate, int buffer_size) override;
void start() override;
void stop() override;
int space_free() override;
std::pair<int, int> buffer_level() override;
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
private:
void lock();
void unlock();
void wait();
int buffer_size;
bool draining = false;
};
#endif /* __GTK_SOUND_DRIVER_PULSE_H */
#endif /* __S9X_SOUND_DRIVER_PULSE_HPP */

View file

@ -4,58 +4,50 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "gtk_sound_driver_sdl.h"
#include "s9x_sound_driver_sdl.hpp"
#include "SDL_audio.h"
#include "gtk_s9x.h"
#include "apu/apu.h"
#include "snes9x.h"
void S9xSDLSoundDriver::samples_available()
bool S9xSDLSoundDriver::write_samples(int16_t *data, int samples)
{
int snes_samples_available = S9xGetSampleCount();
S9xMixSamples((uint8 *)temp, snes_samples_available);
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
bool retval = true;
auto empty = buffer.space_empty();
if (samples > empty)
{
mutex.lock();
int samples = buffer.space_empty();
mutex.unlock();
while (samples < snes_samples_available)
{
usleep(100);
mutex.lock();
samples = buffer.space_empty();
mutex.unlock();
}
retval = false;
buffer.dump(buffer.buffer_size / 2 - empty);
}
buffer.push(data, samples);
mutex.lock();
buffer.push(temp, snes_samples_available);
if (Settings.DynamicRateControl)
S9xUpdateDynamicRate(buffer.space_empty(), buffer.buffer_size);
mutex.unlock();
return retval;
}
void S9xSDLSoundDriver::mix(unsigned char *output, int bytes)
{
mutex.lock();
if (buffer.avail() >= bytes >> 1)
buffer.read((int16_t *)output, bytes >> 1);
mutex.unlock();
else
{
buffer.read((int16_t *)output, buffer.avail());
buffer.add_silence(buffer.buffer_size / 2);
}
}
S9xSDLSoundDriver::S9xSDLSoundDriver()
{
}
S9xSDLSoundDriver::~S9xSDLSoundDriver()
{
deinit();
}
void S9xSDLSoundDriver::init()
{
SDL_InitSubSystem(SDL_INIT_AUDIO);
stop();
}
void S9xSDLSoundDriver::terminate()
void S9xSDLSoundDriver::deinit()
{
stop();
SDL_CloseAudio();
@ -64,10 +56,7 @@ void S9xSDLSoundDriver::terminate()
void S9xSDLSoundDriver::start()
{
if (!gui_config->mute_sound)
{
SDL_PauseAudio(0);
}
SDL_PauseAudio(0);
}
void S9xSDLSoundDriver::stop()
@ -75,13 +64,13 @@ void S9xSDLSoundDriver::stop()
SDL_PauseAudio(1);
}
bool S9xSDLSoundDriver::open_device()
bool S9xSDLSoundDriver::open_device(int playback_rate, int buffer_size)
{
audiospec = {};
audiospec.freq = Settings.SoundPlaybackRate;
audiospec.freq = playback_rate;
audiospec.channels = 2;
audiospec.format = AUDIO_S16SYS;
audiospec.samples = (gui_config->sound_buffer_size * audiospec.freq / 1000) >> 2;
audiospec.samples = audiospec.freq * buffer_size / 8 / 1000; // 1/8th buffer per callback
audiospec.callback = [](void *userdata, uint8_t *stream, int len) {
((S9xSDLSoundDriver *)userdata)->mix((unsigned char *)stream, len);
};
@ -100,12 +89,22 @@ bool S9xSDLSoundDriver::open_device()
}
printf("OK\n");
if (buffer_size < 32)
buffer_size = 32;
buffer.resize(gui_config->sound_buffer_size * audiospec.freq / 500);
S9xSetSamplesAvailableCallback([](void *userdata) {
((decltype(this)) userdata)->samples_available();;
}, this);
buffer.resize(buffer_size * 4 * audiospec.freq / 1000);
return true;
}
int S9xSDLSoundDriver::space_free()
{
auto space_empty = buffer.space_empty();
return space_empty;
}
std::pair<int, int> S9xSDLSoundDriver::buffer_level()
{
std::pair<int, int> level = { buffer.space_empty(), buffer.buffer_size };
return level;
}

View file

@ -4,16 +4,15 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_SOUND_DRIVER_SDL_H
#define __GTK_SOUND_DRIVER_SDL_H
#ifndef __S9X_SOUND_DRIVER_SDL_HPP
#define __S9X_SOUND_DRIVER_SDL_HPP
#include "SDL.h"
// SDL.h may include altivec.h which redefines vector and bool
#undef vector
#undef bool
#include "gtk_sound.h"
#include "gtk_sound_driver.h"
#include "s9x_sound_driver.hpp"
#include "../../apu/resampler.h"
#include <mutex>
@ -23,19 +22,23 @@ class S9xSDLSoundDriver : public S9xSoundDriver
{
public:
S9xSDLSoundDriver();
void init();
void terminate();
bool open_device();
void start();
void stop();
void mix(unsigned char *output, int bytes);
void samples_available();
~S9xSDLSoundDriver();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size) override;
void start() override;
void stop() override;
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
private:
void mix(unsigned char *output, int bytes);
SDL_AudioSpec audiospec;
Resampler buffer;
std::mutex mutex;
int16_t temp[512];
};
#endif /* __GTK_SOUND_DRIVER_SDL_H */
#endif /* __S9X_SOUND_DRIVER_SDL_HPP */

View file

@ -0,0 +1,120 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "s9x_sound_driver_sdl3.hpp"
#include "SDL3/SDL_audio.h"
#include <cstdio>
#include <vector>
bool S9xSDL3SoundDriver::write_samples(int16_t *data, int samples)
{
bool retval = true;
mutex.lock();
auto empty = buffer.space_empty();
if (samples > empty)
{
retval = false;
buffer.dump(buffer.buffer_size / 2 - empty);
}
buffer.push(data, samples);
mutex.unlock();
return retval;
}
void S9xSDL3SoundDriver::mix(int req, int total)
{
if (tmp.size() < req / 2)
tmp.resize(req / 2);
mutex.lock();
if (buffer.avail() >= req / 2)
{
buffer.read((int16_t *)(tmp.data()), req / 2);
mutex.unlock();
SDL_PutAudioStreamData(stream, tmp.data(), req);
}
else
{
auto avail = buffer.avail();
buffer.read((int16_t *)(tmp.data()), avail);
buffer.add_silence(buffer.buffer_size / 2);
mutex.unlock();
SDL_PutAudioStreamData(stream, tmp.data(), avail * 2);
}
}
S9xSDL3SoundDriver::S9xSDL3SoundDriver()
{
stream = nullptr;
}
S9xSDL3SoundDriver::~S9xSDL3SoundDriver()
{
deinit();
}
void S9xSDL3SoundDriver::init()
{
SDL_InitSubSystem(SDL_INIT_AUDIO);
stop();
}
void S9xSDL3SoundDriver::deinit()
{
stop();
SDL_DestroyAudioStream(stream);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
void S9xSDL3SoundDriver::start()
{
SDL_ResumeAudioStreamDevice(stream);
}
void S9xSDL3SoundDriver::stop()
{
SDL_PauseAudioStreamDevice(stream);
}
bool S9xSDL3SoundDriver::open_device(int playback_rate, int buffer_size)
{
audiospec = {};
audiospec.freq = playback_rate;
audiospec.channels = 2;
audiospec.format = SDL_AUDIO_S16;
printf("SDL sound driver initializing...\n");
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK,
&audiospec,
[](void *userdata, SDL_AudioStream *stream, int addl, int total) {
((S9xSDL3SoundDriver *)userdata)->mix(addl, total);
}, this);
printf("OK\n");
if (buffer_size < 32)
buffer_size = 32;
buffer.resize(buffer_size * 2 * audiospec.freq / 1000);
return true;
}
int S9xSDL3SoundDriver::space_free()
{
auto space_empty = buffer.space_empty();
return space_empty;
}
std::pair<int, int> S9xSDL3SoundDriver::buffer_level()
{
std::pair<int, int> level = { buffer.space_empty(), buffer.buffer_size };
return level;
}

View file

@ -0,0 +1,46 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __S9X_SOUND_DRIVER_SDL3_HPP
#define __S9X_SOUND_DRIVER_SDL3_HPP
#include "SDL3/SDL.h"
// SDL.h may include altivec.h which redefines vector and bool
#undef vector
#undef bool
#include "s9x_sound_driver.hpp"
#include "../../apu/resampler.h"
#include <mutex>
#include <cstdint>
#include <vector>
class S9xSDL3SoundDriver : public S9xSoundDriver
{
public:
S9xSDL3SoundDriver();
~S9xSDL3SoundDriver();
void init() override;
void deinit() override;
bool open_device(int playback_rate, int buffer_size) override;
void start() override;
void stop() override;
bool write_samples(int16_t *data, int samples) override;
int space_free() override;
std::pair<int, int> buffer_level() override;
private:
void mix(int req, int total);
SDL_AudioStream *stream;
SDL_AudioSpec audiospec;
Resampler buffer;
std::mutex mutex;
std::vector<int16_t> tmp;
};
#endif /* __S9X_SOUND_DRIVER_SDL3_HPP */

View file

@ -4,12 +4,12 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "gtk_s9x.h"
#include "gtk_glx_context.h"
#include "glx_context.hpp"
GTKGLXContext::GTKGLXContext()
{
@ -18,6 +18,8 @@ GTKGLXContext::GTKGLXContext()
version_major = -1;
version_minor = -1;
gladLoaderLoadGLX(nullptr, 0);
}
GTKGLXContext::~GTKGLXContext()
@ -92,6 +94,8 @@ bool GTKGLXContext::create_context()
return false;
}
resize();
return true;
}
@ -127,12 +131,12 @@ void GTKGLXContext::make_current()
void GTKGLXContext::swap_interval(int frames)
{
if (epoxy_has_glx_extension(display, screen, "GLX_EXT_swap_control"))
if (GLAD_GLX_EXT_swap_control)
glXSwapIntervalEXT(display, xid, frames);
else if (epoxy_has_glx_extension(display, screen, "GLX_SGI_swap_control"))
else if (GLAD_GLX_SGI_swap_control)
glXSwapIntervalSGI(frames);
#ifdef GLX_MESA_swap_control
else if (epoxy_has_glx_extension(display, screen, "GLX_MESA_swap_control"))
else if (GLAD_GLX_MESA_swap_control)
glXSwapIntervalMESA(frames);
#endif
}

View file

@ -4,12 +4,12 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_GLX_CONTEXT_H
#define __GTK_GLX_CONTEXT_H
#ifndef __GLX_CONTEXT_HPP
#define __GLX_CONTEXT_HPP
#include "gtk_opengl_context.h"
#include "opengl_context.hpp"
#include <epoxy/glx.h>
#include <glad/glx.h>
class GTKGLXContext : public OpenGLContext
{

View file

@ -8,10 +8,14 @@
#include <string>
#include <sstream>
#include <fstream>
#include <map>
#include "glsl.h"
#include "../../conffile.h"
#include "shader_helpers.h"
#include "common/video/vulkan/slang_helpers.hpp"
#include "shader_platform.h"
#ifndef _MSC_VER
#include <unistd.h>
#endif
static const GLfloat tex_coords[16] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f };
@ -20,22 +24,19 @@ static const GLfloat mvp_ortho[16] = { 2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f };
static int scale_string_to_enum(const char *string)
static int scale_string_to_enum(std::string string)
{
if (!strcasecmp(string, "source"))
const struct { const char *string; int value; } map[] =
{
return GLSL_SOURCE;
}
else if (!strcasecmp(string, "viewport"))
{
return GLSL_VIEWPORT;
}
else if (!strcasecmp(string, "absolute"))
{
return GLSL_ABSOLUTE;
}
else
return GLSL_NONE;
{ "source", GLSL_SOURCE },
{ "viewport", GLSL_VIEWPORT },
{ "absolute", GLSL_ABSOLUTE }
};
for (size_t i = 0; i < 3; i++)
if (string == map[i].string)
return map[i].value;
return GLSL_NONE;
}
static const char *scale_enum_to_string(int val)
@ -53,17 +54,17 @@ static const char *scale_enum_to_string(int val)
}
}
static int wrap_mode_string_to_enum(const char *string)
static int wrap_mode_string_to_enum(std::string string)
{
if (!strcasecmp(string, "repeat"))
if (string == "repeat")
{
return GL_REPEAT;
}
else if (!strcasecmp(string, "clamp_to_edge"))
else if (string == "clamp_to_edge")
{
return GL_CLAMP_TO_EDGE;
}
else if (!strcasecmp(string, "clamp"))
else if (string == "clamp")
{
return GL_CLAMP;
}
@ -89,17 +90,8 @@ static const char *wrap_mode_enum_to_string(int val)
bool GLSLShader::load_shader_preset_file(const char *filename)
{
char key[256];
int length = strlen(filename);
bool singlepass = false;
if (length > 6 && (!strcasecmp(&filename[length - 5], ".glsl") ||
!strcasecmp(&filename[length - 6], ".slang")))
singlepass = true;
this->using_slang = false;
if (length > 7 && (!strcasecmp(&filename[length - 6], ".slang") ||
!strcasecmp(&filename[length - 7], ".slangp")))
if (ends_with(filename, ".slang") || ends_with(filename, ".slangp"))
{
#ifdef USE_SLANG
this->using_slang = true;
@ -108,7 +100,7 @@ bool GLSLShader::load_shader_preset_file(const char *filename)
#endif
}
if (singlepass)
if (ends_with(filename, ".glsl") || ends_with(filename, ".slang"))
{
GLSLPass pass;
this->pass.push_back(GLSLPass());
@ -126,12 +118,10 @@ bool GLSLShader::load_shader_preset_file(const char *filename)
return true;
}
else
{
conf.LoadFile(filename);
}
int shader_count = conf.GetInt("::shaders", 0);
ini.load_file(filename);
int shader_count = ini.get_int("shaders", 0);
if (shader_count < 1)
return false;
@ -142,20 +132,18 @@ bool GLSLShader::load_shader_preset_file(const char *filename)
{
GLSLPass pass;
snprintf(key, 256, "::filter_linear%u", i);
pass.filter = conf.Exists(key) ? (conf.GetBool(key) ? GL_LINEAR : GL_NEAREST) : GLSL_UNDEFINED;
std::string num = std::to_string(i);
std::string key = "filter_linear" + num;
pass.filter = ini.exists(key) ? (ini.get_bool(key, true) ? GL_LINEAR : GL_NEAREST) : GLSL_UNDEFINED;
sprintf(key, "::scale_type%u", i);
const char* scaleType = conf.GetString(key, "");
std::string scaleType = ini.get_string("scale_type" + num, "");
if (!strcasecmp(scaleType, ""))
if (scaleType == "")
{
sprintf(key, "::scale_type_x%u", i);
const char* scaleTypeX = conf.GetString(key, "");
std::string scaleTypeX = ini.get_string("scale_type_x" + num, "");
pass.scale_type_x = scale_string_to_enum(scaleTypeX);
sprintf(key, "::scale_type_y%u", i);
const char* scaleTypeY = conf.GetString(key, "");
std::string scaleTypeY = ini.get_string("scale_type_y" + num, "");
pass.scale_type_y = scale_string_to_enum(scaleTypeY);
}
else
@ -165,89 +153,51 @@ bool GLSLShader::load_shader_preset_file(const char *filename)
pass.scale_type_y = scale_type;
}
sprintf(key, "::scale%u", i);
const char* scaleFloat = conf.GetString(key, "");
if (!strcasecmp(scaleFloat, ""))
{
sprintf(key, "::scale_x%u", i);
const char* scaleFloatX = conf.GetString(key, "1.0");
pass.scale_x = atof(scaleFloatX);
sprintf(key, "::scale_y%u", i);
const char* scaleFloatY = conf.GetString(key, "1.0");
pass.scale_y = atof(scaleFloatY);
}
else
{
pass.scale_x = pass.scale_y = atof(scaleFloat);
}
auto scaleFloat = ini.get_float("scale" + num, 1.0f);
pass.scale_x = ini.get_float("scale_x" + num, scaleFloat);
pass.scale_y = ini.get_float("scale_y" + num, scaleFloat);
sprintf(key, "::shader%u", i);
strcpy(pass.filename, conf.GetString(key, ""));
strcpy(pass.filename, ini.get_string("shader" + num, "").c_str());
sprintf(key, "::wrap_mode%u", i);
pass.wrap_mode = wrap_mode_string_to_enum (conf.GetString (key ,""));
sprintf(key, "::mipmap_input%u", i);
pass.mipmap_input = conf.GetBool (key);
sprintf(key, "::frame_count_mod%u", i);
pass.frame_count_mod = conf.GetInt(key, 0);
pass.wrap_mode = wrap_mode_string_to_enum(ini.get_string("wrap_mode" + num, ""));
pass.mipmap_input = ini.get_bool("mipmap_input" + num, true);
pass.frame_count_mod = ini.get_int("frame_count_mod" + num, 1);
pass.fp = false;
if (gl_float_texture_available())
{
sprintf(key, "::float_framebuffer%u", i);
pass.fp = conf.GetBool(key);
pass.fp = ini.get_bool("float_framebuffer" + num, false);
}
else
pass.fp = false;
pass.srgb = false;
if (gl_srgb_available())
{
sprintf(key, "::srgb_framebuffer%u", i);
pass.srgb = conf.GetBool(key);
}
else
pass.srgb = false;
pass.srgb = ini.get_bool("srgb_framebuffer" + num, false);
sprintf(key, "::alias%u", i);
strcpy(pass.alias, conf.GetString(key, ""));
strcpy(pass.alias, ini.get_string("alias" + num, "").c_str());
this->pass.push_back(pass);
}
char* ids = conf.GetStringDup("::textures", "");
auto ids_string = ini.get_string("textures", "");
auto ids = split_string(ids_string, ';');
char* id = strtok(ids, ";");
while (id != NULL)
for (auto &id : ids)
{
GLSLLut lut;
sprintf(key, "::%s", id);
strcpy(lut.id, id);
strcpy(lut.filename, conf.GetString(key, ""));
strcpy(lut.id, id.c_str());
strcpy(lut.filename, ini.get_string(id, "").c_str());
sprintf(key, "::%s_wrap_mode", id);
lut.wrap_mode = wrap_mode_string_to_enum (conf.GetString (key, ""));
sprintf(key, "::%s_mipmap", id);
lut.mipmap = conf.GetBool (key);
sprintf(key, "::%s_linear", id);
lut.filter = (conf.GetBool(key, false)) ? GL_LINEAR : GL_NEAREST;
lut.wrap_mode = wrap_mode_string_to_enum(ini.get_string(id + "_wrap_mode", ""));
lut.mipmap = ini.get_bool(id + "_mipmap", false);
lut.filter = (ini.get_bool(id + "_linear", false)) ? GL_LINEAR : GL_NEAREST;
if (lut.mipmap)
{
lut.filter = (lut.filter == GL_LINEAR) ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
}
this->lut.push_back(lut);
id = strtok(NULL, ";");
}
free(ids);
return true;
}
@ -271,40 +221,43 @@ static std::string canonicalize(const std::string &noncanonical)
#ifdef USE_SLANG
static GLuint string_to_format(char *format)
{
#define MATCH(s, f) \
if (!strcmp(format, s)) \
return f;
MATCH("R8_UNORM", GL_R8);
MATCH("R8_UINT", GL_R8UI);
MATCH("R8_SINT", GL_R8I);
MATCH("R8G8_UNORM", GL_RG8);
MATCH("R8G8_UINT", GL_RG8UI);
MATCH("R8G8_SINT", GL_RG8I);
MATCH("R8G8B8A8_UNORM", GL_RGBA8);
MATCH("R8G8B8A8_UINT", GL_RGBA8UI);
MATCH("R8G8B8A8_SINT", GL_RGBA8I);
MATCH("R8G8B8A8_SRGB", GL_SRGB8_ALPHA8);
const std::map<const char *, int> map =
{
{ "R8_UNORM", GL_R8 },
{ "R8_UINT", GL_R8UI },
{ "R8_SINT", GL_R8I },
{ "R8G8_UNORM", GL_RG8 },
{ "R8G8_UINT", GL_RG8UI },
{ "R8G8_SINT", GL_RG8I },
{ "R8G8B8A8_UNORM", GL_RGBA8 },
{ "R8G8B8A8_UINT", GL_RGBA8UI },
{ "R8G8B8A8_SINT", GL_RGBA8I },
{ "R8G8B8A8_SRGB", GL_SRGB8_ALPHA8 },
MATCH("R16_UINT", GL_R16UI);
MATCH("R16_SINT", GL_R16I);
MATCH("R16_SFLOAT", GL_R16F);
MATCH("R16G16_UINT", GL_RG16UI);
MATCH("R16G16_SINT", GL_RG16I);
MATCH("R16G16_SFLOAT", GL_RG16F);
MATCH("R16G16B16A16_UINT", GL_RGBA16UI);
MATCH("R16G16B16A16_SINT", GL_RGBA16I);
MATCH("R16G16B16A16_SFLOAT", GL_RGBA16F);
{ "R16_UINT", GL_R16UI },
{ "R16_SINT", GL_R16I },
{ "R16_SFLOAT", GL_R16F },
{ "R16G16_UINT", GL_RG16UI },
{ "R16G16_SINT", GL_RG16I },
{ "R16G16_SFLOAT", GL_RG16F },
{ "R16G16B16A16_UINT", GL_RGBA16UI },
{ "R16G16B16A16_SINT", GL_RGBA16I },
{ "R16G16B16A16_SFLOAT", GL_RGBA16F },
MATCH("R32_UINT", GL_R32UI);
MATCH("R32_SINT", GL_R32I);
MATCH("R32_SFLOAT", GL_R32F);
MATCH("R32G32_UINT", GL_RG32UI);
MATCH("R32G32_SINT", GL_RG32I);
MATCH("R32G32_SFLOAT", GL_RG32F);
MATCH("R32G32B32A32_UINT", GL_RGBA32UI);
MATCH("R32G32B32A32_SINT", GL_RGBA32I);
MATCH("R32G32B32A32_SFLOAT", GL_RGBA32F);
{ "R32_UINT", GL_R32UI },
{ "R32_SINT", GL_R32I },
{ "R32_SFLOAT", GL_R32F },
{ "R32G32_UINT", GL_RG32UI },
{ "R32G32_SINT", GL_RG32I },
{ "R32G32_SFLOAT", GL_RG32F },
{ "R32G32B32A32_UINT", GL_RGBA32UI },
{ "R32G32B32A32_SINT", GL_RGBA32I },
{ "R32G32B32A32_SFLOAT", GL_RGBA32F }
};
auto iter = map.find(format);
if (iter != map.end())
return iter->second;
return GL_RGBA;
}
#endif
@ -652,18 +605,11 @@ bool GLSLShader::load_shader(const char *filename)
// Check for parameters specified in file
for (unsigned int i = 0; i < param.size(); i++)
{
char key[266];
const char *value;
snprintf (key, 266, "::%s", param[i].id.c_str());
value = conf.GetString (key, NULL);
if (value)
{
param[i].val = atof(value);
if (param[i].val < param[i].min)
param[i].val = param[i].min;
if (param[i].val > param[i].max)
param[i].val = param[i].max;
}
param[i].val = ini.get_float(param[i].id, param[i].val);
if (param[i].val < param[i].min)
param[i].val = param[i].min;
if (param[i].val > param[i].max)
param[i].val = param[i].max;
}
glActiveTexture(GL_TEXTURE0);
@ -1309,5 +1255,4 @@ void GLSLShader::destroy()
pass.clear();
lut.clear();
prev_frame.clear();
conf.Clear();
}

View file

@ -7,7 +7,8 @@
#ifndef __GLSL_H
#define __GLSL_H
#include "../../conffile.h"
#include "snes9x.h"
#include "common/video/vulkan/slang_preset_ini.hpp"
#include "shader_platform.h"
#include <deque>
#include <limits.h>
@ -172,7 +173,7 @@ struct GLSLShader
void destroy();
void register_uniforms();
ConfigFile conf;
IniFile ini;
std::vector<GLSLPass> pass;
std::vector<GLSLLut> lut;

View file

@ -0,0 +1,35 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __SHADER_PLATFORM_H
#define __SHADER_PLATFORM_H
#include "port.h"
#if defined(SNES9X_QT)
#include <glad/gl.h>
#if defined(_WIN32)
#define realpath(src, resolved) _fullpath(resolved, src, PATH_MAX)
#endif
#elif defined(SNES9X_GTK)
#include <glad/gl.h>
#elif defined(_WIN32)
#include <windows.h>
#include <stdlib.h>
#include "gl_core_3_1.h"
#include <direct.h>
#ifdef UNICODE
#define chdir(dir) _wchdir(Utf8ToWide(dir))
#define realpath(src, resolved) _twfullpath(resolved, src, PATH_MAX)
#else
#define chdir(dir) _chdir(dir)
#define realpath(src, resolved) _fullpath(resolved, src, PATH_MAX)
#endif
#endif
#endif /* __SHADER_PLATFORM_H */

View file

@ -7,8 +7,7 @@
#include <stdio.h>
#include <string.h>
#include "gtk_s9x.h"
#include "gtk_wayland_egl_context.h"
#include "wayland_egl_context.hpp"
WaylandEGLContext::WaylandEGLContext()
{
@ -33,15 +32,10 @@ WaylandEGLContext::~WaylandEGLContext()
wl_egl_window_destroy(egl_window);
}
bool WaylandEGLContext::attach(GtkWidget *widget)
bool WaylandEGLContext::attach(wl_display *display, wl_surface *surface, WaylandSurface::Metrics m)
{
GdkWindow *window = gtk_widget_get_window(widget);
if (!GDK_IS_WAYLAND_WINDOW(window))
return false;
wayland_surface = std::make_unique<WaylandSurface>();
wayland_surface->attach(widget);
wayland_surface->attach(display, surface, m);
return true;
}
@ -53,6 +47,7 @@ bool WaylandEGLContext::create_context()
EGL_RED_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_ALPHA_SIZE, 0,
EGL_NONE
};
@ -70,15 +65,27 @@ bool WaylandEGLContext::create_context()
EGLint num_configs = 0;
if (!gladLoaderLoadEGL(nullptr))
{
printf("Couldn't load EGL.\n");
return false;
}
egl_display = eglGetDisplay((EGLNativeDisplayType)wayland_surface->display);
eglInitialize(egl_display, NULL, NULL);
int major, minor;
eglInitialize(egl_display, &major, &minor);
// Load the rest of the functions only after calling eglInitialize.
if (!gladLoaderLoadEGL(egl_display))
{
printf("Couldn't load EGL functions.\n");
}
if (!eglChooseConfig(egl_display, surface_attribs, &egl_config, 1, &num_configs))
{
printf("Couldn't find matching config.\n");
return false;
}
eglBindAPI(EGL_OPENGL_API);
std::tie(width, height) = wayland_surface->get_size();
egl_window = wl_egl_window_create(wayland_surface->child, width, height);
@ -95,6 +102,7 @@ bool WaylandEGLContext::create_context()
return false;
}
eglBindAPI(EGL_OPENGL_API);
egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, core_context_attribs);
if (!egl_context)
{
@ -109,13 +117,11 @@ bool WaylandEGLContext::create_context()
return true;
}
void WaylandEGLContext::resize()
void WaylandEGLContext::resize(WaylandSurface::Metrics m)
{
wayland_surface->resize();
std::tie(width, height) = wayland_surface->get_size();
std::tie(width, height) = wayland_surface->get_size_for_metrics(m);
wl_egl_window_resize(egl_window, width, height, 0, 0);
wayland_surface->resize(m);
make_current();
}
@ -139,3 +145,13 @@ void WaylandEGLContext::swap_interval(int frames)
{
eglSwapInterval(egl_display, frames);
}
void WaylandEGLContext::shrink()
{
wayland_surface->shrink();
}
void WaylandEGLContext::regrow()
{
wayland_surface->regrow();
}

View file

@ -4,14 +4,13 @@
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __GTK_WAYLAND_EGL_CONTEXT_H
#define __GTK_WAYLAND_EGL_CONTEXT_H
#ifndef __WAYLAND_EGL_CONTEXT_H
#define __WAYLAND_EGL_CONTEXT_H
#include "gtk_opengl_context.h"
#include "gtk_wayland_surface.h"
#include "gtk_compat.h"
#include "opengl_context.hpp"
#include "common/video/wayland/wayland_surface.hpp"
#include <epoxy/egl.h>
#include "glad/egl.h"
#include <memory>
#include <wayland-egl.h>
@ -20,16 +19,17 @@ class WaylandEGLContext : public OpenGLContext
public:
WaylandEGLContext();
~WaylandEGLContext();
bool attach(GtkWidget *widget);
bool attach(wl_display *display, wl_surface *surface, WaylandSurface::Metrics m);
bool create_context();
void resize();
void resize() {};
void resize(WaylandSurface::Metrics m);
void swap_buffers();
void swap_interval(int frames);
void make_current();
void shrink();
void regrow();
bool ready();
GdkWindow *gdk_window;
EGLDisplay egl_display;
EGLSurface egl_surface;
EGLContext egl_context;

View file

@ -0,0 +1,104 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <cstdlib>
#include <cstdio>
#include "wgl_context.hpp"
WGLContext::WGLContext()
{
hwnd = nullptr;
hdc = nullptr;
hglrc = nullptr;
version_major = -1;
version_minor = -1;
}
WGLContext::~WGLContext()
{
deinit();
}
void WGLContext::deinit()
{
wglMakeCurrent(nullptr, nullptr);
if (hglrc)
wglDeleteContext(hglrc);
if (hdc)
ReleaseDC(hwnd, hdc);
}
bool WGLContext::attach(HWND target)
{
hwnd = target;
hdc = GetDC(hwnd);
PIXELFORMATDESCRIPTOR pfd{};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
auto pfdIndex = ChoosePixelFormat(hdc, &pfd);
if (!pfdIndex)
return false;
if (!SetPixelFormat(hdc, pfdIndex, &pfd))
{
// Shouldn't happen
}
return true;
}
bool WGLContext::create_context()
{
hglrc = wglCreateContext(hdc);
if (!hglrc)
return false;
if (!wglMakeCurrent(hdc, hglrc))
return false;
gladLoaderLoadWGL(hdc);
resize();
return true;
}
void WGLContext::resize()
{
RECT rect;
GetClientRect(hwnd, &rect);
this->width = rect.right - rect.left;
this->height = rect.bottom - rect.top;
make_current();
}
void WGLContext::swap_buffers()
{
SwapBuffers(hdc);
}
bool WGLContext::ready()
{
return true;
}
void WGLContext::make_current()
{
wglMakeCurrent(hdc, hglrc);
}
void WGLContext::swap_interval(int frames)
{
wglSwapIntervalEXT(frames);
}

View file

@ -0,0 +1,36 @@
/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#ifndef __WGL_CONTEXT_HPP
#define __WGL_CONTEXT_HPP
#include "opengl_context.hpp"
#include <glad/wgl.h>
class WGLContext : public OpenGLContext
{
public:
WGLContext();
~WGLContext();
bool attach(HWND xid);
bool create_context() override;
void resize() override;
void swap_buffers() override;
void swap_interval(int frames) override;
void make_current() override;
bool ready() override;
void deinit();
HWND hwnd;
HDC hdc;
HGLRC hglrc;
int version_major;
int version_minor;
};
#endif

View file

@ -0,0 +1,118 @@
#include "std_chrono_throttle.hpp"
#include <thread>
using namespace std::chrono;
void Throttle::set_frame_rate(double frame_rate)
{
max_frame_rate = frame_rate;
frame_duration = 1.0 / max_frame_rate;
frame_duration_us = microseconds(int64_t(frame_duration * 1000000.0));
}
microseconds Throttle::remaining()
{
auto now = time_point_cast<microseconds>(steady_clock::now());
auto diff = (now - then);
return frame_duration_us - diff;
}
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void Throttle::wait_for_frame()
{
static UINT (WINAPI *NtDelayExecution)(BOOL, LARGE_INTEGER *) = nullptr;
static UINT (WINAPI *NtQueryTimerResolution) (ULONG *, ULONG *, ULONG *) = nullptr;
static UINT (WINAPI *NtSetTimerResolution) (ULONG, BOOL, ULONG *) = nullptr;
static int timer_resolution = 12500;
if (NtDelayExecution == nullptr)
{
HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
if (ntdll)
{
NtDelayExecution = reinterpret_cast<typeof NtDelayExecution>(GetProcAddress(ntdll, "NtDelayExecution"));
NtQueryTimerResolution = reinterpret_cast<typeof NtQueryTimerResolution>(GetProcAddress(ntdll, "NtQueryTimerResolution"));
NtSetTimerResolution = reinterpret_cast<typeof NtSetTimerResolution>(GetProcAddress(ntdll, "NtSetTimerResolution"));
}
if (NtQueryTimerResolution)
{
ULONG min, max, current;
NtQueryTimerResolution(&min, &max, &current);
if (NtSetTimerResolution)
NtSetTimerResolution(max, true, &current);
timer_resolution = current * 5 / 4;
}
}
auto time_to_wait = remaining();
if (time_to_wait < -(frame_duration_us / 8))
{
reset();
return;
}
if (NtDelayExecution)
{
if (time_to_wait.count() * 10 > timer_resolution)
{
LARGE_INTEGER li;
li.QuadPart = -(time_to_wait.count() * 10 - timer_resolution);
NtDelayExecution(false, &li);
}
}
time_to_wait = remaining();
while (time_to_wait.count() > 0)
{
std::this_thread::yield();
time_to_wait = remaining();
}
}
#endif
#if !defined(_WIN32)
void Throttle::wait_for_frame()
{
auto time_to_wait = remaining();
if (time_to_wait < -frame_duration_us)
{
reset();
return;
}
if (time_to_wait.count() > 1000)
std::this_thread::sleep_for(time_to_wait - 1000us);
time_to_wait = remaining();
while (time_to_wait.count() > 0)
{
std::this_thread::yield();
time_to_wait = remaining();
}
}
#endif
void Throttle::wait_for_frame_and_rebase_time()
{
wait_for_frame();
advance();
}
void Throttle::advance()
{
then += frame_duration_us;
}
void Throttle::reset()
{
auto now = time_point_cast<microseconds>(steady_clock::now());
then = now;
}

View file

@ -1,5 +1,5 @@
#include "slang_preset.hpp"
#include "../external/SPIRV-Cross/spirv.hpp"
#include "external/SPIRV-Cross/spirv.hpp"
#include "slang_helpers.hpp"
#include "slang_preset_ini.hpp"
@ -8,25 +8,15 @@
#include <cstdio>
#include <vector>
#include <cctype>
#include <iostream>
#include <fstream>
#include <filesystem>
#include <future>
#include "../external/SPIRV-Cross/spirv_cross.hpp"
#include "../external/SPIRV-Cross/spirv_glsl.hpp"
#include "external/SPIRV-Cross/spirv_cross.hpp"
#include "external/SPIRV-Cross/spirv_glsl.hpp"
#include "slang_shader.hpp"
using std::string;
using std::to_string;
SlangPreset::SlangPreset()
{
}
SlangPreset::~SlangPreset()
{
}
bool SlangPreset::load_preset_file(string filename)
{
if (!ends_with(filename, ".slangp"))

View file

@ -7,9 +7,6 @@
struct SlangPreset
{
SlangPreset();
~SlangPreset();
void print();
bool load_preset_file(std::string filename);
bool introspect();

View file

@ -2,15 +2,6 @@
#include "slang_helpers.hpp"
#include <fstream>
#include <cstring>
#include <charconv>
IniFile::IniFile()
{
}
IniFile::~IniFile()
{
}
static std::string trim_comments(std::string str)
{

View file

@ -4,8 +4,6 @@
struct IniFile
{
IniFile();
~IniFile();
bool load_file(std::string filename);
std::string get_string(std::string key, std::string default_string);
int get_int(std::string key, int default_int);

View file

@ -7,22 +7,13 @@
#include <sstream>
#include <vector>
#include <fstream>
#include "../external/glslang/glslang/Public/ShaderLang.h"
#include "../external/glslang/SPIRV/GlslangToSpv.h"
#include "../external/glslang/glslang/Public/ResourceLimits.h"
#include "external/glslang/glslang/Public/ShaderLang.h"
#include "external/glslang/SPIRV/GlslangToSpv.h"
#include "external/glslang/glslang/Public/ResourceLimits.h"
using std::string;
using std::vector;
SlangShader::SlangShader()
{
ubo_size = 0;
}
SlangShader::~SlangShader()
{
}
/*
Recursively load shader file and included files into memory, applying
#include and #pragma directives. Will strip all directives except

View file

@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <vector>
#include <cstdint>
struct SlangShader
{
@ -64,9 +65,6 @@ struct SlangShader
Fragment
};
SlangShader();
~SlangShader();
bool preprocess_shader_file(std::string filename, std::vector<std::string> &lines);
void set_base_path(std::string filename);
bool load_file(std::string new_filename = "");
@ -101,4 +99,4 @@ struct SlangShader
int ubo_binding;
std::vector<Uniform> uniforms;
std::vector<Sampler> samplers;
};
};

View file

@ -1,2 +1,4 @@
#define VMA_IMPLEMENTATION
#define VMA_NULLABLE
#define VMA_NOT_NULL
#include "vulkan_context.hpp"

View file

@ -0,0 +1,164 @@
/* Based on code from Vulkan-Samples:
https://github.com/KhronosGroup/Vulkan-Samples
*/
/* Copyright (c) 2018-2024, Arm Limited and Contributors
* Copyright (c) 2019-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vulkan_hpp_wrapper.hpp"
namespace Vulkan {
vk::AccessFlags get_access_flags(vk::ImageLayout layout)
{
switch (layout) {
case vk::ImageLayout::eUndefined:
case vk::ImageLayout::ePresentSrcKHR:
return vk::AccessFlagBits::eNone;
case vk::ImageLayout::ePreinitialized:
return vk::AccessFlagBits::eHostWrite;
case vk::ImageLayout::eColorAttachmentOptimal:
return vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
case vk::ImageLayout::eDepthAttachmentOptimal:
return vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
case vk::ImageLayout::eFragmentShadingRateAttachmentOptimalKHR:
return vk::AccessFlagBits::eFragmentShadingRateAttachmentReadKHR;
case vk::ImageLayout::eShaderReadOnlyOptimal:
return vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
case vk::ImageLayout::eTransferSrcOptimal:
return vk::AccessFlagBits::eTransferRead;
case vk::ImageLayout::eTransferDstOptimal:
return vk::AccessFlagBits::eTransferWrite;
case vk::ImageLayout::eGeneral:
return vk::AccessFlagBits::eNone;
default:
return vk::AccessFlagBits::eNone;
}
}
vk::PipelineStageFlags get_pipeline_stage_flags(vk::ImageLayout layout)
{
switch (layout) {
case vk::ImageLayout::eUndefined:
return vk::PipelineStageFlagBits::eTopOfPipe;
case vk::ImageLayout::ePreinitialized:
return vk::PipelineStageFlagBits::eHost;
case vk::ImageLayout::eTransferDstOptimal:
case vk::ImageLayout::eTransferSrcOptimal:
return vk::PipelineStageFlagBits::eTransfer;
case vk::ImageLayout::eColorAttachmentOptimal:
return vk::PipelineStageFlagBits::eColorAttachmentOutput;
case vk::ImageLayout::eDepthAttachmentOptimal:
return vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
case vk::ImageLayout::eFragmentShadingRateAttachmentOptimalKHR:
return vk::PipelineStageFlagBits::eFragmentShadingRateAttachmentKHR;
case vk::ImageLayout::eShaderReadOnlyOptimal:
return vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eFragmentShader;
case vk::ImageLayout::ePresentSrcKHR:
return vk::PipelineStageFlagBits::eBottomOfPipe;
case vk::ImageLayout::eGeneral:
return vk::PipelineStageFlagBits::eNone;
default:
return vk::PipelineStageFlagBits::eNone;
}
}
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::PipelineStageFlags src_stage_mask,
vk::PipelineStageFlags dst_stage_mask,
vk::AccessFlags src_access_mask,
vk::AccessFlags dst_access_mask,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range)
{
auto image_memory_barrier = vk::ImageMemoryBarrier{}
.setSrcAccessMask(src_access_mask)
.setDstAccessMask(dst_access_mask)
.setOldLayout(old_layout)
.setNewLayout(new_layout)
.setImage(image)
.setSubresourceRange(subresource_range);
command_buffer.pipelineBarrier(src_stage_mask,
dst_stage_mask,
{}, {}, {}, image_memory_barrier);
}
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range)
{
vk::PipelineStageFlags src_stage_mask = get_pipeline_stage_flags(old_layout);
vk::PipelineStageFlags dst_stage_mask = get_pipeline_stage_flags(new_layout);
vk::AccessFlags src_access_mask = get_access_flags(old_layout);
vk::AccessFlags dst_access_mask = get_access_flags(new_layout);
image_layout_transition(command_buffer, image, src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask, old_layout, new_layout, subresource_range);
}
// Fixed sub resource on first mip level and layer
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout)
{
auto subresource_range = vk::ImageSubresourceRange{}
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setBaseMipLevel(0)
.setLevelCount(1)
.setBaseArrayLayer(0)
.setLayerCount(1);
image_layout_transition(command_buffer, image, old_layout, new_layout, subresource_range);
}
void image_layout_transition(vk::CommandBuffer command_buffer,
std::vector<std::pair<vk::Image, vk::ImageSubresourceRange>> const &imagesAndRanges,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout)
{
vk::PipelineStageFlags src_stage_mask = get_pipeline_stage_flags(old_layout);
vk::PipelineStageFlags dst_stage_mask = get_pipeline_stage_flags(new_layout);
vk::AccessFlags src_access_mask = get_access_flags(old_layout);
vk::AccessFlags dst_access_mask = get_access_flags(new_layout);
// Create image barrier objects
std::vector<vk::ImageMemoryBarrier> image_memory_barriers;
for (size_t i = 0; i < imagesAndRanges.size(); i++) {
image_memory_barriers.push_back(vk::ImageMemoryBarrier(
src_access_mask,
dst_access_mask,
old_layout,
new_layout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
imagesAndRanges[i].first,
imagesAndRanges[i].second));
}
// Put barriers inside setup command buffer
command_buffer.pipelineBarrier(src_stage_mask,
dst_stage_mask,
{}, {}, {}, image_memory_barriers);
}
} // namespace Vulkan

View file

@ -0,0 +1,28 @@
#pragma once
#include "vulkan_hpp_wrapper.hpp"
namespace Vulkan {
vk::AccessFlags get_access_flags(vk::ImageLayout layout);
vk::PipelineStageFlags get_pipeline_stage_flags(vk::ImageLayout layout);
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::PipelineStageFlags src_stage_mask,
vk::PipelineStageFlags dst_stage_mask,
vk::AccessFlags src_access_mask,
vk::AccessFlags dst_access_mask,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range);
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range);
void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout);
} // namespace Vulkan

View file

@ -0,0 +1,368 @@
#include <cstring>
#include <vector>
#include <string>
#include <optional>
#include "vulkan_context.hpp"
namespace Vulkan
{
static std::unique_ptr<vk::DynamicLoader> dl;
Context::Context()
{
swapchain = std::make_unique<Swapchain>(*this);
}
Context::~Context()
{
if (!device)
return;
wait_idle();
swapchain.reset();
command_pool.reset();
allocator.destroy();
surface.reset();
wait_idle();
device.destroy();
}
static bool load_loader()
{
if (dl)
return true;
dl = std::make_unique<vk::DynamicLoader>();
if (!dl->success())
{
dl.reset();
return false;
}
auto vkGetInstanceProcAddr =
dl->getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
return true;
}
static vk::UniqueInstance create_instance_preamble(const char *wsi_extension)
{
load_loader();
if (!dl || !dl->success())
return {};
std::vector<const char *> extensions = {
wsi_extension,
VK_KHR_SURFACE_EXTENSION_NAME
};
vk::ApplicationInfo application_info({}, {}, {}, {}, VK_API_VERSION_1_1);
vk::InstanceCreateInfo instance_create_info({}, &application_info, {}, extensions);
auto [result, instance] = vk::createInstanceUnique(instance_create_info);
if (result != vk::Result::eSuccess)
{
instance.reset();
return {};
}
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance.get());
return std::move(instance);
}
std::vector<std::string> Vulkan::Context::get_device_list()
{
std::vector<std::string> device_names;
auto instance = create_instance_preamble(VK_KHR_SURFACE_EXTENSION_NAME);
if (!instance)
return {};
auto [result, device_list] = instance->enumeratePhysicalDevices();
for (auto &d : device_list)
{
auto props = d.getProperties();
std::string device_name((const char *)props.deviceName);
device_name += " (" + vk::to_string(props.deviceType) + ")";
device_names.push_back(device_name);
}
return device_names;
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool Context::init_win32()
{
instance = create_instance_preamble(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
if (!instance)
return false;
return init();
}
bool Context::create_win32_surface(HINSTANCE hinstance, HWND hwnd)
{
auto win32_surface_create_info = vk::Win32SurfaceCreateInfoKHR{}
.setHinstance(hinstance)
.setHwnd(hwnd);
auto retval = instance->createWin32SurfaceKHRUnique(win32_surface_create_info);
if (retval.result != vk::Result::eSuccess)
return false;
surface = std::move(retval.value);
return true;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool Context::init_Xlib()
{
instance = create_instance_preamble(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
if (!instance)
return false;
platform_name = "xlib";
return init();
}
bool Context::create_Xlib_surface(Display *dpy, Window xid)
{
auto retval = instance->createXlibSurfaceKHRUnique({ {}, dpy, xid });
if (retval.result != vk::Result::eSuccess)
return false;
surface = std::move(retval.value);
return true;
}
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool Context::init_wayland()
{
instance = create_instance_preamble(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
if (!instance)
return false;
platform_name = "wayland";
return init();
}
bool Context::create_wayland_surface(wl_display *dpy, wl_surface *parent)
{
auto wayland_surface_create_info = vk::WaylandSurfaceCreateInfoKHR{}
.setSurface(parent)
.setDisplay(dpy);
auto [result, new_surface] = instance->createWaylandSurfaceKHRUnique(wayland_surface_create_info);
if (result != vk::Result::eSuccess)
return false;
surface = std::move(new_surface);
return true;
}
#endif
bool Context::destroy_surface()
{
wait_idle();
if (swapchain)
swapchain->uncreate();
surface.reset();
return true;
}
bool Context::init()
{
init_device();
init_vma();
init_command_pool();
wait_idle();
return true;
}
bool Context::init_command_pool()
{
vk::CommandPoolCreateInfo cpci({}, graphics_queue_family_index);
cpci.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer);
auto retval = device.createCommandPoolUnique(cpci);
command_pool = std::move(retval.value);
return true;
}
static bool find_extension(std::vector<vk::ExtensionProperties> &props, const char *extension_string)
{
return std::find_if(props.begin(),
props.end(),
[&](vk::ExtensionProperties &ext) {
return (std::string(ext.extensionName.data()) == extension_string);
}) != props.end();
};
static std::optional<uint32_t> find_graphics_queue(vk::PhysicalDevice &device)
{
auto queue_props = device.getQueueFamilyProperties();
for (size_t i = 0; i < queue_props.size(); i++)
{
if (queue_props[i].queueFlags & vk::QueueFlagBits::eGraphics)
{
return i;
}
}
return std::nullopt;
}
static bool check_extensions(std::vector<const char *> &required_extensions, vk::PhysicalDevice &device)
{
auto props = device.enumerateDeviceExtensionProperties().value;
for (const auto &extension : required_extensions)
{
if (!find_extension(props, extension))
return false;
}
return true;
};
bool Context::init_device()
{
std::vector<const char *> required_extensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
std::vector<const char *> present_wait_extensions =
{
VK_KHR_PRESENT_ID_EXTENSION_NAME,
VK_KHR_PRESENT_WAIT_EXTENSION_NAME
};
auto device_list = instance->enumeratePhysicalDevices().value;
bool device_chosen = false;
physical_device = vk::PhysicalDevice();
if (preferred_device > -1 &&
(size_t)preferred_device < device_list.size() &&
check_extensions(required_extensions, device_list[preferred_device]))
{
physical_device = device_list[preferred_device];
device_chosen = true;
}
if (!device_chosen)
{
for (auto &device : device_list)
{
if (check_extensions(required_extensions, device))
{
physical_device = device;
device_chosen = true;
break;
}
}
}
if (!device_chosen)
return false;
if (check_extensions(present_wait_extensions, physical_device))
{
for (auto &ext : present_wait_extensions)
required_extensions.push_back(ext);
have_present_wait = true;
}
else
{
have_present_wait = false;
}
if (auto index = find_graphics_queue(physical_device))
graphics_queue_family_index = *index;
else
return false;
std::vector<float> priorities = { 1.0f };
vk::DeviceQueueCreateInfo dqci({}, graphics_queue_family_index, priorities);
vk::DeviceCreateInfo dci({}, dqci, {}, required_extensions);
vk::PhysicalDevicePresentWaitFeaturesKHR physical_device_present_wait_feature(true);
vk::PhysicalDevicePresentIdFeaturesKHR physical_device_present_id_feature(true);
if (have_present_wait)
{
dci.setPNext(&physical_device_present_wait_feature);
physical_device_present_wait_feature.setPNext(&physical_device_present_id_feature);
}
device = physical_device.createDevice(dci).value;
queue = device.getQueue(graphics_queue_family_index, 0);
return true;
}
bool Context::init_vma()
{
auto vulkan_functions = vma::VulkanFunctions{}
.setVkGetInstanceProcAddr(VULKAN_HPP_DEFAULT_DISPATCHER.vkGetInstanceProcAddr)
.setVkGetDeviceProcAddr(VULKAN_HPP_DEFAULT_DISPATCHER.vkGetDeviceProcAddr);
auto allocator_create_info = vma::AllocatorCreateInfo{}
.setDevice(device)
.setInstance(instance.get())
.setPhysicalDevice(physical_device)
.setPVulkanFunctions(&vulkan_functions);
allocator = vma::createAllocator(allocator_create_info).value;
return true;
}
bool Context::create_swapchain()
{
wait_idle();
return swapchain->create();
}
bool Context::recreate_swapchain()
{
return swapchain->recreate();
}
void Context::wait_idle()
{
if (device)
device.waitIdle();
}
vk::CommandBuffer Context::begin_cmd_buffer()
{
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool.get(), vk::CommandBufferLevel::ePrimary, 1);
auto command_buffer = device.allocateCommandBuffers(command_buffer_allocate_info).value;
one_time_use_cmd = command_buffer[0];
one_time_use_cmd.begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
return one_time_use_cmd;
}
void Context::hard_barrier(vk::CommandBuffer cmd)
{
vk::MemoryBarrier barrier(vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite);
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eAllCommands,
{}, barrier, {}, {});
}
void Context::end_cmd_buffer()
{
one_time_use_cmd.end();
vk::SubmitInfo submit_info{};
submit_info.setCommandBuffers(one_time_use_cmd);
queue.submit(submit_info);
queue.waitIdle();
device.freeCommandBuffers(command_pool.get(), one_time_use_cmd);
one_time_use_cmd = nullptr;
}
} // namespace Vulkan

View file

@ -7,11 +7,11 @@
#undef WINVER
#define WINVER 0x599
#endif
#include "vk_mem_alloc.hpp"
#include "vulkan/vulkan.hpp"
#include <cstdint>
#include "vulkan_hpp_wrapper.hpp"
#include "external/VulkanMemoryAllocator-Hpp/include/vk_mem_alloc.hpp"
#include "vulkan_swapchain.hpp"
#include <memory>
#include <optional>
namespace Vulkan
{
@ -22,48 +22,54 @@ class Context
Context();
~Context();
#ifdef VK_USE_PLATFORM_XLIB_KHR
bool init_Xlib(Display *dpy, Window xid, int preferred_device = 0);
bool init_Xlib();
bool create_Xlib_surface(Display *dpy, Window xid);
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
bool init_wayland(wl_display *dpy, wl_surface *parent, int width, int height, int preferred_device = 0);
bool init_wayland();
bool create_wayland_surface(wl_display *dpy, wl_surface *parent);
#endif
#ifdef VK_USE_PLATFORM_WIN32_KHR
bool init_win32(HINSTANCE hinstance, HWND hwnd, int preferred_device = 0);
bool init_win32();
bool create_win32_surface(HINSTANCE hinstance, HWND hwnd);
#endif
bool init(int preferred_device = 0);
bool create_swapchain(int width = -1, int height = -1);
bool recreate_swapchain(int width = -1, int height = -1);
bool init();
bool create_swapchain();
bool recreate_swapchain();
bool destroy_surface();
void wait_idle();
vk::CommandBuffer begin_cmd_buffer();
void end_cmd_buffer();
void hard_barrier(vk::CommandBuffer cmd);
static std::vector<std::string> get_device_list();
void set_preferred_device(int device) { preferred_device = device; };
void unset_preferred_device() { preferred_device = -1; };
vma::Allocator allocator;
vk::Device device;
uint32_t graphics_queue_family_index;
vk::Queue queue;
vk::UniqueCommandPool command_pool;
vk::UniqueDescriptorPool descriptor_pool;
std::unique_ptr<Swapchain> swapchain;
vk::UniqueInstance instance;
vk::PhysicalDevice physical_device;
vk::PhysicalDeviceProperties physical_device_props;
vk::UniqueSurfaceKHR surface;
std::string platform_name;
bool have_present_wait;
private:
bool init_vma();
bool init_device(int preferred_device = 0);
bool init_device();
bool init_command_pool();
bool init_descriptor_pool();
int preferred_device;
#ifdef VK_USE_PLATFORM_XLIB_KHR
Display *xlib_display;
Window xlib_window;
#endif
vk::UniqueInstance instance;
vk::PhysicalDevice physical_device;
vk::PhysicalDeviceProperties physical_device_props;
vk::UniqueSurfaceKHR surface;
uint32_t graphics_queue_family_index;
vk::CommandBuffer one_time_use_cmd;
};
} // namespace Vulkan
} // namespace Vulkan

View file

@ -1,2 +1,2 @@
#include "vulkan/vulkan.hpp"
#include "vulkan_hpp_wrapper.hpp"
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE

View file

@ -0,0 +1,5 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
#define VULKAN_HPP_ASSERT(x)
#include "vulkan/vulkan.hpp"

View file

@ -0,0 +1,209 @@
#include <cassert>
#include "vulkan_pipeline_image.hpp"
#include "slang_helpers.hpp"
#include "vulkan_common.hpp"
namespace Vulkan
{
PipelineImage::~PipelineImage()
{
destroy();
}
void PipelineImage::init(vk::Device device_, vk::CommandPool command_, vk::Queue queue_, vma::Allocator allocator_)
{
device = device_;
command_pool = command_;
allocator = allocator_;
queue = queue_;
}
void PipelineImage::init(Context *context)
{
device = context->device;
command_pool = context->command_pool.get();
allocator = context->allocator;
queue = context->queue;
}
void PipelineImage::destroy()
{
if (!device || !allocator)
return;
if (image_width != 0 || image_height != 0)
{
framebuffer.reset();
device.destroyImageView(image_view);
device.destroyImageView(mipless_view);
allocator.destroyImage(image, image_allocation);
image_width = image_height = 0;
image_view = nullptr;
image = nullptr;
image_allocation = nullptr;
current_layout = vk::ImageLayout::eUndefined;
}
}
void PipelineImage::generate_mipmaps(vk::CommandBuffer cmd)
{
if (!mipmap)
return;
auto range = [](unsigned int i) { return vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, i, 1, 0, 1); };
auto level = [](unsigned int i) { return vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1); };
auto mipmap_levels = mipmap ? mipmap_levels_for_size(image_width, image_height) : 1;
image_layout_transition(cmd, image, vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eTransferSrcOptimal);
// Transition base layer to readable format.
int base_width = image_width;
int base_height = image_height;
int base_level = 0;
for (; base_level + 1 < mipmap_levels; base_level++)
{
// Transition base layer to readable format.
if (base_level > 0)
{
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eTransferSrcOptimal,
range(base_level));
}
// Transition mipmap layer to writable
image_layout_transition(cmd,
image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
range(base_level + 1));
// Blit base layer to mipmap layer
int mipmap_width = std::max(base_width >> 1, 1);
int mipmap_height = std::max(base_height >> 1, 1);
auto blit = vk::ImageBlit{}
.setSrcOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(base_width, base_height, 1) })
.setDstOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(mipmap_width, mipmap_height, 1) })
.setSrcSubresource(level(base_level))
.setDstSubresource(level(base_level + 1));
base_width = mipmap_width;
base_height = mipmap_height;
cmd.blitImage(image,
vk::ImageLayout::eTransferSrcOptimal,
image,
vk::ImageLayout::eTransferDstOptimal,
blit,
vk::Filter::eLinear);
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
void PipelineImage::barrier(vk::CommandBuffer cmd)
{
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eBottomOfPipe,
vk::PipelineStageFlagBits::eFragmentShader,
{}, {}, {}, {});
}
void PipelineImage::clear(vk::CommandBuffer cmd)
{
vk::ImageSubresourceRange subresource_range(vk::ImageAspectFlagBits::eColor, 0, VK_REMAINING_MIP_LEVELS, 0, 1);
image_layout_transition(cmd,
image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
subresource_range);
vk::ClearColorValue color{};
color.setFloat32({ 0.0f, 0.0f, 0.0f, 1.0f });
cmd.clearColorImage(image,
vk::ImageLayout::eTransferDstOptimal,
color,
subresource_range);
image_layout_transition(cmd,
image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
subresource_range);
current_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
}
void PipelineImage::create(int width, int height, vk::Format fmt, vk::RenderPass renderpass, bool mipmap)
{
assert(width + height);
assert(device && allocator);
this->mipmap = mipmap;
int mipmap_levels = mipmap ? mipmap_levels_for_size(width, height): 1;
format = fmt;
auto allocation_create_info = vma::AllocationCreateInfo{}
.setUsage(vma::MemoryUsage::eAuto);
auto image_create_info = vk::ImageCreateInfo{}
.setUsage(vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled)
.setImageType(vk::ImageType::e2D)
.setExtent(vk::Extent3D(width, height, 1))
.setMipLevels(mipmap_levels)
.setArrayLayers(1)
.setFormat(format)
.setInitialLayout(vk::ImageLayout::eUndefined)
.setSamples(vk::SampleCountFlagBits::e1)
.setSharingMode(vk::SharingMode::eExclusive);
std::tie(image, image_allocation) = allocator.createImage(image_create_info, allocation_create_info).value;
auto subresource_range = vk::ImageSubresourceRange{}
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setBaseArrayLayer(0)
.setBaseMipLevel(0)
.setLayerCount(1)
.setLevelCount(mipmap_levels);
auto image_view_create_info = vk::ImageViewCreateInfo{}
.setImage(image)
.setViewType(vk::ImageViewType::e2D)
.setFormat(format)
.setComponents(vk::ComponentMapping())
.setSubresourceRange(subresource_range);
image_view = device.createImageView(image_view_create_info).value;
image_view_create_info.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
mipless_view = device.createImageView(image_view_create_info).value;
image_width = width;
image_height = height;
auto framebuffer_create_info = vk::FramebufferCreateInfo{}
.setAttachments(mipless_view)
.setWidth(width)
.setHeight(height)
.setRenderPass(renderpass)
.setLayers(1);
framebuffer = device.createFramebufferUnique(framebuffer_create_info).value;
}
} // namespace Vulkan

View file

@ -6,7 +6,7 @@ namespace Vulkan
struct PipelineImage
{
PipelineImage();
PipelineImage() = default;
void init(vk::Device device, vk::CommandPool command, vk::Queue queue, vma::Allocator allocator);
void init(Vulkan::Context *context);
~PipelineImage();

View file

@ -1,7 +1,10 @@
#include <cassert>
#include "vulkan_shader_chain.hpp"
#include "slang_helpers.hpp"
#include "stb_image.h"
#include "vulkan/vulkan_enums.hpp"
#include "vulkan_common.hpp"
namespace Vulkan
{
@ -24,6 +27,7 @@ ShaderChain::~ShaderChain()
{
if (context && context->device)
{
context->wait_idle();
if (vertex_buffer)
context->allocator.destroyBuffer(vertex_buffer, vertex_buffer_allocation);
vertex_buffer = nullptr;
@ -40,7 +44,7 @@ void ShaderChain::construct_buffer_objects()
uint8_t *ubo_memory = nullptr;
if (pipeline.shader->ubo_size > 0)
ubo_memory = (uint8_t *)context->allocator.mapMemory(pipeline.uniform_buffer_allocation);
ubo_memory = (uint8_t *)context->allocator.mapMemory(pipeline.uniform_buffer_allocation).value;
for (auto &uniform : pipeline.shader->uniforms)
{
@ -50,16 +54,13 @@ void ShaderChain::construct_buffer_objects()
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
std::string block;
switch (uniform.block)
{
case SlangShader::Uniform::UBO:
location = &ubo_memory[uniform.offset];
block = "uniform";
break;
case SlangShader::Uniform::PushConstant:
location = &pipeline.push_constants[uniform.offset];
block = "push constant";
break;
}
@ -248,7 +249,7 @@ bool ShaderChain::load_shader_preset(std::string filename)
.setMaxSets(pipelines.size() * queue_size)
.setFlags(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet);
descriptor_pool = context->device.createDescriptorPoolUnique(descriptor_pool_create_info);
descriptor_pool = context->device.createDescriptorPoolUnique(descriptor_pool_create_info).value;
for (auto &p : pipelines)
p->generate_frame_resources(descriptor_pool.get());
@ -267,9 +268,9 @@ bool ShaderChain::load_shader_preset(std::string filename)
.setFlags(vma::AllocationCreateFlagBits::eHostAccessSequentialWrite)
.setRequiredFlags(vk::MemoryPropertyFlagBits::eHostVisible);
std::tie(vertex_buffer, vertex_buffer_allocation) = context->allocator.createBuffer(buffer_create_info, allocation_create_info);
std::tie(vertex_buffer, vertex_buffer_allocation) = context->allocator.createBuffer(buffer_create_info, allocation_create_info).value;
auto vertex_buffer_memory = context->allocator.mapMemory(vertex_buffer_allocation);
auto vertex_buffer_memory = context->allocator.mapMemory(vertex_buffer_allocation).value;
memcpy(vertex_buffer_memory, vertex_data, sizeof(vertex_data));
context->allocator.unmapMemory(vertex_buffer_allocation);
context->allocator.flushAllocation(vertex_buffer_allocation, 0, sizeof(vertex_data));
@ -402,8 +403,6 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
if (!context->swapchain->begin_frame())
return false;
current_frame_index = context->swapchain->get_current_frame();
auto cmd = context->swapchain->get_cmd();
update_and_propagate_sizes(width, height, viewport_width, viewport_height);
@ -418,6 +417,7 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
{
auto &pipe = *pipelines[i];
auto &frame = pipe.frame[current_frame_index];
bool is_last_pass = (i == pipelines.size() - 1);
update_descriptor_set(cmd, i, current_frame_index);
@ -430,7 +430,7 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
.setRenderArea(vk::Rect2D({}, vk::Extent2D(frame.image.image_width, frame.image.image_height)))
.setClearValues(value);
if (i == pipelines.size() - 1)
if (is_last_pass)
context->swapchain->begin_render_pass();
else
cmd.beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
@ -441,52 +441,36 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
if (pipe.push_constants.size() > 0)
cmd.pushConstants(pipe.pipeline_layout.get(), vk::ShaderStageFlagBits::eAllGraphics, 0, pipe.push_constants.size(), pipe.push_constants.data());
if (i < pipelines.size() - 1)
{
cmd.setViewport(0, vk::Viewport(0, 0, pipe.destination_width, pipe.destination_height, 0.0f, 1.0f));
cmd.setScissor(0, vk::Rect2D({}, vk::Extent2D(pipe.destination_width, pipe.destination_height)));
}
else
if (is_last_pass)
{
cmd.setViewport(0, vk::Viewport(viewport_x, viewport_y, viewport_width, viewport_height, 0.0f, 1.0f));
cmd.setScissor(0, vk::Rect2D(vk::Offset2D(viewport_x, viewport_y), vk::Extent2D(viewport_width, viewport_height)));
}
cmd.draw(3, 1, 0, 0);
if (i < pipelines.size() - 1)
{
cmd.endRenderPass();
}
else
{
context->swapchain->end_render_pass();
cmd.setViewport(0, vk::Viewport(0, 0, pipe.destination_width, pipe.destination_height, 0.0f, 1.0f));
cmd.setScissor(0, vk::Rect2D({}, vk::Extent2D(pipe.destination_width, pipe.destination_height)));
}
cmd.draw(3, 1, 0, 0);
if (is_last_pass)
context->swapchain->end_render_pass();
else
cmd.endRenderPass();
frame.image.barrier(cmd);
if (i < pipelines.size() - 1)
if (!is_last_pass)
frame.image.generate_mipmaps(cmd);
if (preset->last_pass_uses_feedback && i == pipelines.size() - 1)
if (preset->last_pass_uses_feedback && is_last_pass)
{
std::array<vk::ImageMemoryBarrier, 2> image_memory_barrier{};
image_memory_barrier[0]
.setImage(frame.image.image)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
image_memory_barrier[1]
.setImage(context->swapchain->get_image())
.setOldLayout(vk::ImageLayout::ePresentSrcKHR)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, image_memory_barrier);
image_layout_transition(cmd, frame.image.image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal);
image_layout_transition(cmd, context->swapchain->get_image(),
vk::ImageLayout::ePresentSrcKHR,
vk::ImageLayout::eTransferSrcOptimal);
auto image_blit = vk::ImageBlit{}
.setSrcOffsets({ vk::Offset3D(viewport_x, viewport_y, 0), vk::Offset3D(viewport_x + viewport_width, viewport_y + viewport_height, 1) })
@ -496,22 +480,12 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
cmd.blitImage(context->swapchain->get_image(), vk::ImageLayout::eTransferSrcOptimal, frame.image.image, vk::ImageLayout::eTransferDstOptimal, image_blit, vk::Filter::eNearest);
image_memory_barrier[0]
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
image_memory_barrier[1]
.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eMemoryRead)
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eAllGraphics,
{}, {}, {}, image_memory_barrier);
image_layout_transition(cmd, frame.image.image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal);
image_layout_transition(cmd, context->swapchain->get_image(),
vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::ePresentSrcKHR);
frame.image.current_layout = vk::ImageLayout::eTransferDstOptimal;
}
@ -519,6 +493,7 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
context->swapchain->end_frame_without_swap();
last_frame_index = current_frame_index;
current_frame_index = (current_frame_index + 1) % queue_size;
frame_count++;
return true;
}
@ -526,33 +501,34 @@ bool ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, in
void ShaderChain::upload_original(vk::CommandBuffer cmd, uint8_t *data, int width, int height, int stride, vk::Format format)
{
std::unique_ptr<Texture> texture;
auto create_texture = [&]() {
texture->create(width,
height,
format,
wrap_mode_from_string(pipelines[0]->shader->wrap_mode),
pipelines[0]->shader->filter_linear,
pipelines[0]->shader->mipmap_input);
};
bool create_texture = false;
if (original.size() > original_history_size)
{
texture = std::move(original.back());
original.pop_back();
if (texture->image_width != width || texture->image_height != height || texture->format != format)
{
texture->destroy();
create_texture();
create_texture = true;
}
}
else
{
texture = std::make_unique<Texture>();
texture->init(context);
create_texture();
create_texture = true;
}
if (create_texture)
texture->create(width,
height,
format,
wrap_mode_from_string(pipelines[0]->shader->wrap_mode),
pipelines[0]->shader->filter_linear,
pipelines[0]->shader->mipmap_input);
if (cmd)
texture->from_buffer(cmd, data, width, height, stride);
else

View file

@ -48,6 +48,7 @@ SimpleOutput::~SimpleOutput()
context->wait_idle();
textures.clear();
descriptors.clear();
descriptor_pool.reset();
device.destroySampler(linear_sampler);
device.destroySampler(nearest_sampler);
}
@ -55,19 +56,25 @@ SimpleOutput::~SimpleOutput()
void SimpleOutput::create_objects()
{
descriptors.clear();
for (size_t i = 0; i < swapchain->get_num_frames(); i++)
descriptor_pool.reset();
vk::DescriptorPoolSize descriptor_pool_size(vk::DescriptorType::eCombinedImageSampler, 20);
vk::DescriptorPoolCreateInfo dpci(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, 20,
descriptor_pool_size);
descriptor_pool = device.createDescriptorPoolUnique(dpci).value;
for (int i = 0; i < queue_size; i++)
{
vk::DescriptorSetAllocateInfo dsai{};
dsai
.setDescriptorPool(context->descriptor_pool.get())
auto dsai = vk::DescriptorSetAllocateInfo{}
.setDescriptorPool(descriptor_pool.get())
.setDescriptorSetCount(1)
.setSetLayouts(descriptor_set_layout.get());
auto descriptor = device.allocateDescriptorSetsUnique(dsai);
auto descriptor = device.allocateDescriptorSetsUnique(dsai).value;
descriptors.push_back(std::move(descriptor[0]));
}
textures.clear();
textures.resize(swapchain->get_num_frames());
textures.resize(queue_size);
for (auto &t : textures)
{
t.init(context);
@ -87,11 +94,11 @@ void SimpleOutput::create_objects()
.setMaxLod(1.0f)
.setMipLodBias(0.0)
.setCompareEnable(false);
linear_sampler = device.createSampler(sci);
linear_sampler = device.createSampler(sci).value;
sci.setMinFilter(vk::Filter::eNearest)
.setMagFilter(vk::Filter::eNearest);
nearest_sampler = device.createSampler(sci);
nearest_sampler = device.createSampler(sci).value;
}
void SimpleOutput::create_pipeline()
@ -99,8 +106,8 @@ void SimpleOutput::create_pipeline()
auto vertex_spirv = SlangShader::generate_spirv(vertex_shader, "vertex");
auto fragment_spirv = SlangShader::generate_spirv(fragment_shader, "fragment");
auto vertex_module = device.createShaderModuleUnique({ {}, vertex_spirv });
auto fragment_module = device.createShaderModuleUnique({ {}, fragment_spirv });
auto vertex_module = device.createShaderModuleUnique({ {}, vertex_spirv }).value;
auto fragment_module = device.createShaderModuleUnique({ {}, fragment_spirv }).value;
vk::PipelineShaderStageCreateInfo vertex_ci;
vertex_ci.setStage(vk::ShaderStageFlagBits::eVertex)
@ -182,14 +189,14 @@ void SimpleOutput::create_pipeline()
.setDescriptorType(vk::DescriptorType::eCombinedImageSampler);
vk::DescriptorSetLayoutCreateInfo dslci{};
dslci.setBindings(dslb);
descriptor_set_layout = device.createDescriptorSetLayoutUnique(dslci);
descriptor_set_layout = device.createDescriptorSetLayoutUnique(dslci).value;
vk::PipelineLayoutCreateInfo pipeline_layout_info;
pipeline_layout_info.setSetLayoutCount(0)
.setPushConstantRangeCount(0)
.setSetLayouts(descriptor_set_layout.get());
pipeline_layout = device.createPipelineLayoutUnique(pipeline_layout_info);
pipeline_layout = device.createPipelineLayoutUnique(pipeline_layout_info).value;
vk::GraphicsPipelineCreateInfo pipeline_create_info;
pipeline_create_info.setStageCount(2)
@ -223,10 +230,10 @@ bool SimpleOutput::do_frame_without_swap(uint8_t *buffer, int width, int height,
if (!swapchain->begin_frame())
return false;
auto &tex = textures[swapchain->get_current_frame()];
auto &tex = textures[current_frame];
auto &cmd = swapchain->get_cmd();
auto extents = swapchain->get_extents();
auto &dstset = descriptors[swapchain->get_current_frame()].get();
auto &dstset = descriptors[current_frame].get();
tex.from_buffer(cmd, (uint8_t *)buffer, width, height, byte_stride);
@ -254,6 +261,8 @@ bool SimpleOutput::do_frame_without_swap(uint8_t *buffer, int width, int height,
swapchain->end_render_pass();
swapchain->end_frame_without_swap();
current_frame = (current_frame + 1) % queue_size;
return true;
}

View file

@ -15,6 +15,9 @@ class SimpleOutput
void set_filter(bool on);
private:
const int queue_size = 3;
int current_frame = 0;
void create_pipeline();
void create_objects();
@ -22,6 +25,7 @@ class SimpleOutput
vk::Device device;
Vulkan::Swapchain *swapchain;
vk::UniqueDescriptorPool descriptor_pool;
vk::UniqueDescriptorSetLayout descriptor_set_layout;
vk::UniquePipelineLayout pipeline_layout;
vk::UniquePipeline pipeline;

View file

@ -1,6 +1,4 @@
#include "vulkan_slang_pipeline.hpp"
#include "slang_helpers.hpp"
#include <unordered_map>
namespace Vulkan
{
@ -65,18 +63,6 @@ vk::SamplerAddressMode wrap_mode_from_string(std::string s)
return vk::SamplerAddressMode::eClampToBorder;
}
SlangPipeline::SlangPipeline()
{
device = nullptr;
shader = nullptr;
uniform_buffer = nullptr;
uniform_buffer_allocation = nullptr;
source_width = 0;
source_height = 0;
destination_width = 0;
destination_height = 0;
}
void SlangPipeline::init(Context *context_, SlangShader *shader_)
{
this->context = context_;
@ -156,10 +142,10 @@ bool SlangPipeline::generate_pipeline(bool lastpass)
.setDependencies(subpass_dependency)
.setAttachments(attachment_description);
render_pass = device.createRenderPassUnique(render_pass_create_info);
render_pass = device.createRenderPassUnique(render_pass_create_info).value;
auto vertex_module = device.createShaderModuleUnique({ {}, shader->vertex_shader_spirv });
auto fragment_module = device.createShaderModuleUnique({ {}, shader->fragment_shader_spirv });
auto vertex_module = device.createShaderModuleUnique({ {}, shader->vertex_shader_spirv }).value;
auto fragment_module = device.createShaderModuleUnique({ {}, shader->fragment_shader_spirv }).value;
auto vertex_ci = vk::PipelineShaderStageCreateInfo{}
.setStage(vk::ShaderStageFlagBits::eVertex)
@ -278,7 +264,7 @@ bool SlangPipeline::generate_pipeline(bool lastpass)
auto dslci = vk::DescriptorSetLayoutCreateInfo{}
.setBindings(descriptor_set_layout_bindings);
descriptor_set_layout = device.createDescriptorSetLayoutUnique(dslci);
descriptor_set_layout = device.createDescriptorSetLayoutUnique(dslci).value;
vk::PushConstantRange pcr(vk::ShaderStageFlagBits::eAllGraphics, 0, shader->push_constant_block_size);
@ -289,7 +275,7 @@ bool SlangPipeline::generate_pipeline(bool lastpass)
if (shader->push_constant_block_size > 0)
pipeline_layout_info.setPushConstantRanges(pcr);
pipeline_layout = device.createPipelineLayoutUnique(pipeline_layout_info);
pipeline_layout = device.createPipelineLayoutUnique(pipeline_layout_info).value;
auto pipeline_create_info = vk::GraphicsPipelineCreateInfo{}
.setStageCount(2)
@ -343,11 +329,11 @@ bool SlangPipeline::generate_frame_resources(vk::DescriptorPool pool)
vk::DescriptorSetAllocateInfo descriptor_set_allocate_info(pool, descriptor_set_layout.get());
auto result = device.allocateDescriptorSetsUnique(descriptor_set_allocate_info);
auto result = device.allocateDescriptorSetsUnique(descriptor_set_allocate_info).value;
f.descriptor_set = std::move(result[0]);
}
semaphore = device.createSemaphoreUnique({});
semaphore = device.createSemaphoreUnique({}).value;
push_constants.resize(shader->push_constant_block_size);
@ -361,7 +347,7 @@ bool SlangPipeline::generate_frame_resources(vk::DescriptorPool pool)
.setFlags(vma::AllocationCreateFlagBits::eHostAccessSequentialWrite)
.setRequiredFlags(vk::MemoryPropertyFlagBits::eHostVisible);
std::tie(uniform_buffer, uniform_buffer_allocation) = context->allocator.createBuffer(buffer_create_info, allocation_create_info);
std::tie(uniform_buffer, uniform_buffer_allocation) = context->allocator.createBuffer(buffer_create_info, allocation_create_info).value;
}
else
{
@ -385,7 +371,7 @@ bool SlangPipeline::generate_frame_resources(vk::DescriptorPool pool)
.setMaxLod(VK_LOD_CLAMP_NONE)
.setAnisotropyEnable(false);
sampler = device.createSamplerUnique(sampler_create_info);
sampler = device.createSamplerUnique(sampler_create_info).value;
return true;
}

View file

@ -1,5 +1,5 @@
#pragma once
#include "vulkan/vulkan.hpp"
#include "vulkan_hpp_wrapper.hpp"
#include "slang_shader.hpp"
#include "vulkan_context.hpp"
#include "vulkan_pipeline_image.hpp"
@ -10,7 +10,7 @@ namespace Vulkan
class SlangPipeline
{
public:
SlangPipeline();
SlangPipeline() = default;
void init(Context *context_, SlangShader *shader_);
~SlangPipeline();

View file

@ -1,31 +1,36 @@
#include "vulkan_swapchain.hpp"
#include "vulkan/vulkan_structs.hpp"
#include <thread>
#include "vulkan_context.hpp"
namespace Vulkan
{
Swapchain::Swapchain(vk::Device device_, vk::PhysicalDevice physical_device_, vk::Queue queue_, vk::SurfaceKHR surface_, vk::CommandPool command_pool_)
: surface(surface_),
command_pool(command_pool_),
physical_device(physical_device_),
queue(queue_)
Swapchain::Swapchain(Context &context_)
: context(context_),
device(context.device),
queue(context.queue),
physical_device(context.physical_device),
command_pool(context.command_pool.get()),
surface(context.surface.get())
{
device = device_;
create_render_pass();
}
Swapchain::~Swapchain()
{
}
bool Swapchain::set_vsync(bool new_setting)
void Swapchain::set_vsync(bool new_setting)
{
if (new_setting == vsync)
return false;
if (vsync != new_setting)
{
vsync = new_setting;
if (swapchain_object)
recreate();
}
}
vsync = new_setting;
return true;
void Swapchain::on_render_pass_end(std::function<void ()> function)
{
end_render_pass_function = function;
}
void Swapchain::create_render_pass()
@ -70,52 +75,142 @@ void Swapchain::create_render_pass()
.setDependencies(subpass_dependency)
.setAttachments(attachment_description);
render_pass = device.createRenderPassUnique(render_pass_create_info);
render_pass = device.createRenderPassUnique(render_pass_create_info).value;
}
bool Swapchain::recreate(int new_width, int new_height)
bool Swapchain::recreate()
{
device.waitIdle();
return create(num_swapchain_images, new_width, new_height);
if (swapchain_object)
{
device.waitIdle();
}
return create();
}
vk::Image Swapchain::get_image()
{
return imageviewfbs[current_swapchain_image].image;
return image_data[current_swapchain_image].image;
}
bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, int new_height)
template<typename T>
static bool vector_find(std::vector<T> haystack, T&& needle)
{
for (auto &elem : haystack)
if (elem == needle)
return true;
return false;
}
vk::PresentModeKHR Swapchain::get_present_mode() {
auto present_mode = vk::PresentModeKHR::eFifo;
if (context.platform_name == "wayland")
{
if (supports_mailbox)
present_mode = vk::PresentModeKHR::eMailbox;
}
if (!vsync) {
if (supports_immediate)
present_mode = vk::PresentModeKHR::eImmediate;
}
return present_mode;
}
bool Swapchain::check_and_resize(int width, int height)
{
vk::SurfaceCapabilitiesKHR surface_capabilities;
if (width == -1 && height == -1)
{
surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface).value;
width = surface_capabilities.currentExtent.width;
height = surface_capabilities.currentExtent.height;
}
if (width < 1 || height < 1)
return false;
if (extents.width != (uint32_t)width || extents.height != (uint32_t)height)
{
set_desired_size(width, height);
recreate();
return true;
}
return false;
}
bool Swapchain::uncreate()
{
frames.clear();
imageviewfbs.clear();
image_data.clear();
swapchain_object.reset();
auto surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface);
return true;
}
if (surface_capabilities.minImageCount > desired_num_swapchain_images)
bool Swapchain::create()
{
if (!render_pass)
create_render_pass();
frames.clear();
image_data.clear();
auto present_modes = physical_device.getSurfacePresentModesKHR(surface).value;
for (auto &mode : present_modes)
{
if (mode == vk::PresentModeKHR::eMailbox)
supports_mailbox = true;
}
auto surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface).value;
if (desired_latency == - 1 || (int)surface_capabilities.minImageCount > desired_latency)
num_swapchain_images = surface_capabilities.minImageCount;
else
num_swapchain_images = desired_num_swapchain_images;
num_swapchain_images = desired_latency;
extents = surface_capabilities.currentExtent;
// If extents aren't reported (Wayland), we have to rely on Wayland to report
// the size, so keep current extent.
if (surface_capabilities.currentExtent.width != UINT32_MAX)
extents = surface_capabilities.currentExtent;
if (new_width > 0 && new_height > 0)
uint32_t graphics_queue_index = 0;
auto queue_properties = physical_device.getQueueFamilyProperties();
for (size_t i = 0; i < queue_properties.size(); i++)
{
if (queue_properties[i].queueFlags & vk::QueueFlagBits::eGraphics)
{
graphics_queue_index = i;
break;
}
}
if (desired_width > 0 && desired_height > 0)
{
// No buffer is allocated for surface yet
extents.width = new_width;
extents.height = new_height;
extents.width = desired_width;
extents.height = desired_height;
}
else if (extents.width < 1 || extents.height < 1)
extents.width = std::clamp(extents.width,
surface_capabilities.minImageExtent.width,
surface_capabilities.maxImageExtent.width);
extents.height = std::clamp(extents.height,
surface_capabilities.minImageExtent.height,
surface_capabilities.maxImageExtent.height);
if (extents.width < 1 || extents.height < 1)
{
// Surface is likely hidden
printf("Extents too small.\n");
swapchain_object.reset();
return false;
}
else if (extents.width > 8192 || extents.height > 8192)
{
extents.width = 512;
extents.height = 512;
}
auto swapchain_create_info = vk::SwapchainCreateInfoKHR{}
.setMinImageCount(num_swapchain_images)
@ -126,44 +221,54 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc)
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
.setClipped(true)
.setPresentMode(vsync ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eImmediate)
.setPresentMode(get_present_mode())
.setSurface(surface)
.setImageArrayLayers(1);
.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity)
.setImageArrayLayers(1)
.setQueueFamilyIndices(graphics_queue_index);
if (swapchain_object)
swapchain_create_info.setOldSwapchain(swapchain_object.get());
swapchain_object = device.createSwapchainKHRUnique(swapchain_create_info);
if (!swapchain_object)
swapchain_object.reset();
auto resval = device.createSwapchainKHRUnique(swapchain_create_info);
if (resval.result != vk::Result::eSuccess && resval.result != vk::Result::eSuboptimalKHR)
{
swapchain_object.reset();
return false;
}
swapchain_object = std::move(resval.value);
auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get());
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, max_latency);
auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info);
create_resources();
if (imageviewfbs.size() > num_swapchain_images)
num_swapchain_images = imageviewfbs.size();
return true;
}
frames.resize(max_latency);
imageviewfbs.resize(num_swapchain_images);
bool Swapchain::create_resources()
{
auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get()).value;
if (swapchain_images.size() > num_swapchain_images)
num_swapchain_images = swapchain_images.size();
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, num_swapchain_images);
auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info).value;
frames.resize(num_swapchain_images);
image_data.resize(num_swapchain_images);
vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled);
for (int i = 0; i < max_latency; i++)
for (unsigned int i = 0; i < num_swapchain_images; i++)
{
// Create frame queue resources
auto &frame = frames[i];
frame.command_buffer = std::move(command_buffers[i]);
frame.fence = device.createFenceUnique(fence_create_info);
frame.acquire = device.createSemaphoreUnique({});
frame.complete = device.createSemaphoreUnique({});
frame.fence = device.createFenceUnique(fence_create_info).value;
frame.acquire = device.createSemaphoreUnique({}).value;
frame.complete = device.createSemaphoreUnique({}).value;
}
current_frame = 0;
for (unsigned int i = 0; i < num_swapchain_images; i++)
{
// Create resources associated with swapchain images
auto &image = imageviewfbs[i];
auto &image = image_data[i];
image.image = swapchain_images[i];
auto image_view_create_info = vk::ImageViewCreateInfo{}
.setImage(swapchain_images[i])
@ -171,7 +276,7 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
.setFormat(vk::Format::eB8G8R8A8Unorm)
.setComponents(vk::ComponentMapping())
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
image.image_view = device.createImageViewUnique(image_view_create_info);
image.image_view = device.createImageViewUnique(image_view_create_info).value;
auto framebuffer_create_info = vk::FramebufferCreateInfo{}
.setAttachments(image.image_view.get())
@ -179,12 +284,11 @@ bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width,
.setHeight(extents.height)
.setLayers(1)
.setRenderPass(render_pass.get());
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info);
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info).value;
}
device.waitIdle();
current_swapchain_image = 0;
current_frame = 0;
return true;
}
@ -199,22 +303,29 @@ bool Swapchain::begin_frame()
auto &frame = frames[current_frame];
auto result = device.waitForFences(frame.fence.get(), true, UINT64_MAX);
auto result = device.waitForFences({ frame.fence.get() }, true, 33333333);
if (result != vk::Result::eSuccess)
{
printf("Failed fence\n");
printf("Timed out waiting for fence.\n");
return false;
}
auto result_value = device.acquireNextImageKHR(swapchain_object.get(), UINT64_MAX, frame.acquire.get());
vk::ResultValue<uint32_t> result_value(vk::Result::eSuccess, 0);
result_value = device.acquireNextImageKHR(swapchain_object.get(), 33333333, frame.acquire.get());
if (result_value.result == vk::Result::eErrorOutOfDateKHR ||
result_value.result == vk::Result::eSuboptimalKHR)
{
printf("Out of date\n");
recreate();
return begin_frame();
}
if (result_value.result == vk::Result::eTimeout)
{
printf("Timed out waiting for swapchain.\n");
return false;
}
if (result_value.result != vk::Result::eSuccess)
{
printf("Unable to acquire swapchain image: %s\n", vk::to_string(result_value.result).c_str());
@ -253,11 +364,24 @@ bool Swapchain::swap()
.setSwapchains(swapchain_object.get())
.setImageIndices(current_swapchain_image);
auto result = queue.presentKHR(present_info);
vk::PresentIdKHR present_id;
if (context.have_present_wait)
{
presentation_id++;
present_id.setPresentIds(presentation_id);
present_info.setPNext(&present_id);
}
current_frame = (current_frame + 1) % max_latency;
vk::Result result = queue.presentKHR(present_info);
if (result == vk::Result::eErrorOutOfDateKHR)
{
// NVIDIA binary drivers will set OutOfDate between acquire and
// present. Ignore this and fix it on the next swapchain acquire.
}
if (result != vk::Result::eSuccess)
current_frame = (current_frame + 1) % num_swapchain_images;
if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR)
return false;
return true;
}
@ -270,7 +394,7 @@ bool Swapchain::end_frame()
vk::Framebuffer Swapchain::get_framebuffer()
{
return imageviewfbs[current_swapchain_image].framebuffer.get();
return image_data[current_swapchain_image].framebuffer.get();
}
vk::CommandBuffer &Swapchain::get_cmd()
@ -287,7 +411,7 @@ void Swapchain::begin_render_pass()
auto render_pass_begin_info = vk::RenderPassBeginInfo{}
.setRenderPass(render_pass.get())
.setFramebuffer(imageviewfbs[current_swapchain_image].framebuffer.get())
.setFramebuffer(image_data[current_swapchain_image].framebuffer.get())
.setRenderArea(vk::Rect2D({}, extents))
.setClearValues(value);
get_cmd().beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
@ -295,20 +419,15 @@ void Swapchain::begin_render_pass()
void Swapchain::end_render_pass()
{
if (end_render_pass_function)
{
end_render_pass_function();
end_render_pass_function = nullptr;
}
get_cmd().endRenderPass();
}
unsigned int Swapchain::get_current_frame()
{
return current_frame;
}
bool Swapchain::wait_on_frame(int frame_num)
{
auto result = device.waitForFences(frames[frame_num].fence.get(), true, 33000000);
return (result == vk::Result::eSuccess);
}
vk::Extent2D Swapchain::get_extents()
{
return extents;
@ -319,9 +438,12 @@ vk::RenderPass &Swapchain::get_render_pass()
return render_pass.get();
}
unsigned int Swapchain::get_num_frames()
void Swapchain::present_wait()
{
return max_latency;
if (context.have_present_wait && context.platform_name != "wayland")
{
device.waitForPresentKHR(swapchain_object.get(), presentation_id, 16666666);
}
}
} // namespace Vulkan
} // namespace Vulkan

View file

@ -0,0 +1,90 @@
#pragma once
#include "vulkan_hpp_wrapper.hpp"
#include <functional>
namespace Vulkan
{
class Context;
class Swapchain
{
public:
Swapchain(Context &);
~Swapchain();
bool create();
bool uncreate();
bool recreate();
bool create_resources();
bool check_and_resize(int width = -1, int height = -1);
Swapchain &set_desired_size(int width, int height) { desired_width = width; desired_height = height; return *this; }
void unset_desired_size() { desired_width = -1; desired_height = -1; }
Swapchain &set_desired_latency(int latency) { desired_latency = latency; return *this; }
void unset_desired_latency() { desired_latency = -1; }
bool begin_frame();
void begin_render_pass();
void end_render_pass();
bool end_frame();
void end_frame_without_swap();
bool swap();
void present_wait();
void set_vsync(bool on);
void on_render_pass_end(std::function<void()> function);
int get_num_frames() { return num_swapchain_images; }
vk::PresentModeKHR get_present_mode();
vk::Image get_image();
vk::Framebuffer get_framebuffer();
vk::CommandBuffer &get_cmd();
vk::Extent2D get_extents();
vk::RenderPass &get_render_pass();
private:
std::function<void()> end_render_pass_function = nullptr;
void create_render_pass();
struct Frame
{
vk::UniqueFence fence;
vk::UniqueSemaphore acquire;
vk::UniqueSemaphore complete;
vk::UniqueCommandBuffer command_buffer;
};
struct ImageData
{
vk::Image image;
vk::UniqueImageView image_view;
vk::UniqueFramebuffer framebuffer;
};
vk::UniqueSwapchainKHR swapchain_object;
vk::Extent2D extents;
vk::UniqueRenderPass render_pass;
unsigned int current_frame = 0;
unsigned int current_swapchain_image = 0;
unsigned int num_swapchain_images = 0;
int desired_width = -1;
int desired_height = -1;
int desired_latency = -1;
uint64_t presentation_id = 0;
bool vsync = true;
bool supports_immediate = false;
bool supports_mailbox = false;
bool supports_relaxed = false;
std::vector<Frame> frames;
std::vector<ImageData> image_data;
Vulkan::Context &context;
vk::Device &device;
vk::Queue &queue;
vk::PhysicalDevice &physical_device;
vk::CommandPool &command_pool;
vk::SurfaceKHR &surface;
};
} // namespace Vulkan

View file

@ -1,24 +1,12 @@
#include <cassert>
#include "vulkan_texture.hpp"
#include "vulkan/vulkan_enums.hpp"
#include "slang_helpers.hpp"
#include "vulkan_common.hpp"
namespace Vulkan
{
Texture::Texture()
{
image_width = 0;
image_height = 0;
buffer_size = 0;
device = nullptr;
command_pool = nullptr;
allocator = nullptr;
queue = nullptr;
buffer = nullptr;
image = nullptr;
sampler = nullptr;
}
Texture::~Texture()
{
destroy();
@ -91,7 +79,7 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
byte_stride = pixel_size * width;
}
auto map = allocator.mapMemory(buffer_allocation);
auto map = allocator.mapMemory(buffer_allocation).value;
for (int y = 0; y < height; y++)
{
auto src = buffer + byte_stride * y;
@ -101,20 +89,13 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
allocator.unmapMemory(buffer_allocation);
allocator.flushAllocation(buffer_allocation, 0, width * height * pixel_size);
auto srr = [](unsigned int i) { return vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, i, 1, 0, 1); };
auto srl = [](unsigned int i) { return vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1); };
auto range = [](unsigned int i) { return vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, i, 1, 0, 1); };
auto level = [](unsigned int i) { return vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, i, 0, 1); };
auto barrier = vk::ImageMemoryBarrier{}
.setImage(image)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eShaderRead)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setSubresourceRange(srr(0));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eFragmentShader,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
range(0));
auto buffer_image_copy = vk::BufferImageCopy{}
.setBufferOffset(0)
@ -122,7 +103,7 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
.setBufferImageHeight(height)
.setImageExtent(vk::Extent3D(width, height, 1))
.setImageOffset(vk::Offset3D(0, 0, 0))
.setImageSubresource(srl(0));
.setImageSubresource(level(0));
cmd.copyBufferToImage(this->buffer, image, vk::ImageLayout::eTransferDstOptimal, buffer_image_copy);
auto mipmap_levels = mipmap ? mipmap_levels_for_size(image_width, image_height) : 1;
@ -133,30 +114,16 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
for (; base_level + 1 < mipmap_levels; base_level++)
{
// Transition base layer to readable format.
barrier
.setImage(image)
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setSubresourceRange(srr(base_level));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eTransferSrcOptimal,
range(base_level));
// Transition mipmap layer to writable
barrier
.setImage(image)
.setOldLayout(vk::ImageLayout::eUndefined)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferRead)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setSubresourceRange(srr(base_level + 1));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
range(base_level + 1));
// Blit base layer to mipmap layer
int mipmap_width = base_width >> 1;
@ -169,8 +136,8 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
auto blit = vk::ImageBlit{}
.setSrcOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(base_width, base_height, 1) })
.setDstOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(mipmap_width, mipmap_height, 1)})
.setSrcSubresource(srl(base_level))
.setDstSubresource(srl(base_level + 1));
.setSrcSubresource(level(base_level))
.setDstSubresource(level(base_level + 1));
base_width = mipmap_width;
base_height = mipmap_height;
@ -178,35 +145,23 @@ void Texture::from_buffer(vk::CommandBuffer cmd,
cmd.blitImage(image, vk::ImageLayout::eTransferSrcOptimal, image, vk::ImageLayout::eTransferDstOptimal, blit, vk::Filter::eLinear);
// Transition base layer to shader readable
barrier
.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
.setSubresourceRange(srr(base_level));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eTransferSrcOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
// Transition final layer to shader readable
barrier
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal)
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eShaderRead)
.setSubresourceRange(srr(base_level));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{}, {}, {}, barrier);
image_layout_transition(cmd, image,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal,
range(base_level));
}
void Texture::from_buffer(uint8_t *buffer, int width, int height, int byte_stride)
{
vk::CommandBufferAllocateInfo cbai(command_pool, vk::CommandBufferLevel::ePrimary, 1);
auto command_buffer_vector = device.allocateCommandBuffersUnique(cbai);
auto command_buffer_vector = device.allocateCommandBuffersUnique(cbai).value;
auto &cmd = command_buffer_vector[0];
cmd->begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
from_buffer(cmd.get(), buffer, width, height, byte_stride);
@ -241,7 +196,7 @@ void Texture::create(int width, int height, vk::Format fmt, vk::SamplerAddressMo
.setSamples(vk::SampleCountFlagBits::e1)
.setSharingMode(vk::SharingMode::eExclusive);
std::tie(image, image_allocation) = allocator.createImage(ici, aci);
std::tie(image, image_allocation) = allocator.createImage(ici, aci).value;
buffer_size = width * height * 4;
if (format == vk::Format::eR5G6B5UnormPack16)
@ -251,9 +206,10 @@ void Texture::create(int width, int height, vk::Format fmt, vk::SamplerAddressMo
.setUsage(vk::BufferUsageFlagBits::eTransferSrc);
aci.setRequiredFlags(vk::MemoryPropertyFlagBits::eHostVisible)
.setFlags(vma::AllocationCreateFlagBits::eHostAccessSequentialWrite);
.setFlags(vma::AllocationCreateFlagBits::eHostAccessSequentialWrite)
.setUsage(vma::MemoryUsage::eAutoPreferHost);
std::tie(buffer, buffer_allocation) = allocator.createBuffer(bci, aci);
std::tie(buffer, buffer_allocation) = allocator.createBuffer(bci, aci).value;
auto isrr = vk::ImageSubresourceRange{}
.setAspectMask(vk::ImageAspectFlagBits::eColor)
@ -268,7 +224,7 @@ void Texture::create(int width, int height, vk::Format fmt, vk::SamplerAddressMo
.setComponents(vk::ComponentMapping())
.setSubresourceRange(isrr);
image_view = device.createImageView(ivci);
image_view = device.createImageView(ivci).value;
image_width = width;
image_height = height;
@ -292,7 +248,7 @@ void Texture::create(int width, int height, vk::Format fmt, vk::SamplerAddressMo
.setMaxLod(10000.0f)
.setMipmapMode(vk::SamplerMipmapMode::eLinear);
sampler = device.createSampler(sampler_create_info);
sampler = device.createSampler(sampler_create_info).value;
}
void Texture::discard_staging_buffer()

View file

@ -7,7 +7,7 @@ namespace Vulkan
struct Texture
{
Texture();
Texture() = default;
void init(vk::Device device, vk::CommandPool command, vk::Queue queue, vma::Allocator allocator);
void init(Context *context);
~Texture();

View file

@ -6,11 +6,11 @@
#include <stdio.h>
#include <string.h>
#include <tuple>
#include <wayland-util.h>
#include "fractional-scale-v1.h"
#include "gtk_s9x.h"
#include "gtk_wayland_surface.h"
#include "wayland_surface.hpp"
#include "wayland-idle-inhibit-unstable-v1.h"
#include "viewporter-client-protocol.h"
@ -23,15 +23,15 @@ static void wl_global(void *data,
auto wl = (WaylandSurface *)data;
if (!strcmp(interface, "wl_compositor"))
wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3);
wl->compositor = (struct wl_compositor *)wl_registry_bind(wl_registry, name, &wl_compositor_interface, version);
else if (!strcmp(interface, "wl_subcompositor"))
wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
wl->subcompositor = (struct wl_subcompositor *)wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, version);
else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name))
wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
wl->idle_inhibit_manager = (struct zwp_idle_inhibit_manager_v1 *)wl_registry_bind(wl_registry, name, &zwp_idle_inhibit_manager_v1_interface, version);
else if (!strcmp(interface, wp_viewporter_interface.name))
wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, 1);
wl->viewporter = (struct wp_viewporter *)wl_registry_bind(wl_registry, name, &wp_viewporter_interface, version);
else if (!strcmp(interface, wp_fractional_scale_manager_v1_interface.name))
wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, 1);
wl->fractional_scale_manager = (struct wp_fractional_scale_manager_v1 *)wl_registry_bind(wl_registry, name, &wp_fractional_scale_manager_v1_interface, version);
}
static void wl_global_remove(void *data,
@ -106,19 +106,11 @@ wp_fractional_scale_v1_listener fractional_scale_v1_listener =
preferred_scale
};
bool WaylandSurface::attach(GtkWidget *widget)
bool WaylandSurface::attach(wl_display *display, wl_surface *surface, Metrics m)
{
GdkWindow *window = gtk_widget_get_window(widget);
if (!GDK_IS_WAYLAND_WINDOW(window))
return false;
gdk_window = window;
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
gdk_scale = gdk_window_get_scale_factor(gdk_window);
display = gdk_wayland_display_get_wl_display(gdk_window_get_display(gdk_window));
parent = gdk_wayland_window_get_wl_surface(gdk_window);
metrics = m;
this->display = display;
parent = surface;
registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &wl_registry_listener, this);
@ -133,7 +125,7 @@ bool WaylandSurface::attach(GtkWidget *widget)
wl_surface_set_input_region(child, region);
wl_subsurface_set_desync(subsurface);
wl_subsurface_set_position(subsurface, x, y);
wl_subsurface_set_position(subsurface, m.x, m.y);
if (fractional_scale_manager)
{
@ -141,43 +133,75 @@ bool WaylandSurface::attach(GtkWidget *widget)
wp_fractional_scale_v1_add_listener(fractional_scale, &fractional_scale_v1_listener, this);
}
if (idle_inhibit_manager && gui_config->prevent_screensaver)
if (idle_inhibit_manager)
{
printf("Inhibiting screensaver.\n");
zwp_idle_inhibit_manager_v1_create_inhibitor(idle_inhibit_manager, child);
}
resize();
wl_display_roundtrip(display);
resize(m);
return true;
}
std::tuple<int, int> WaylandSurface::get_size()
{
gdk_window_get_geometry(gdk_window, &x, &y, &width, &height);
if (actual_scale == 0.0)
{
gdk_scale = gdk_window_get_scale_factor(gdk_window);
return { width * gdk_scale, height * gdk_scale };
}
return { width * actual_scale, height * actual_scale };
return get_size_for_metrics(metrics);
}
void WaylandSurface::resize()
std::tuple<int, int> WaylandSurface::get_size_for_metrics(Metrics m)
{
auto [w, h] = get_size();
wl_subsurface_set_position(subsurface, x, y);
if (actual_scale == 0.0)
{
return { m.width * m.scale, m.height * m.scale };
}
return { round(m.width * actual_scale), round(m.height * actual_scale) };
}
void WaylandSurface::shrink()
{
if (!viewport)
viewport = wp_viewporter_get_viewport(viewporter, child);
wp_viewport_set_source(viewport,
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1));
wp_viewport_set_destination(viewport, 2, 2);
wl_surface_commit(child);
wl_surface_commit(parent);
}
void WaylandSurface::regrow()
{
if (!viewport)
viewport = wp_viewporter_get_viewport(viewporter, child);
wp_viewport_set_source(viewport,
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1));
wp_viewport_set_destination(viewport, metrics.width, metrics.height);
wl_surface_commit(child);
wl_surface_commit(parent);
}
void WaylandSurface::resize(Metrics m)
{
metrics = m;
wl_subsurface_set_position(subsurface, m.x, m.y);
if (!viewport)
viewport = wp_viewporter_get_viewport(viewporter, child);
wp_viewport_set_source(viewport,
wl_fixed_from_int(0), wl_fixed_from_int(0),
wl_fixed_from_int(w), wl_fixed_from_int(h));
wp_viewport_set_destination(viewport, width, height);
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
wl_fixed_from_int(-1), wl_fixed_from_int(-1));
wp_viewport_set_destination(viewport, m.width, m.height);
wl_surface_commit(child);
wl_surface_commit(parent);

View file

@ -6,20 +6,26 @@
#pragma once
#include "gtk_compat.h"
#include "viewporter-client-protocol.h"
#include "fractional-scale-v1.h"
#include <tuple>
class WaylandSurface
{
public:
WaylandSurface();
~WaylandSurface();
bool attach(GtkWidget *widget);
void resize();
std::tuple<int, int> get_size();
GdkWindow *gdk_window;
struct Metrics {
int x, y, width, height, scale;
};
bool attach(wl_display *display, wl_surface *surface, Metrics source_metrics);
void resize(Metrics new_metrics);
void shrink();
void regrow();
std::tuple<int, int> get_size();
std::tuple<int, int> get_size_for_metrics(Metrics m);
struct wl_display *display;
struct wl_registry *registry;
@ -31,11 +37,7 @@ class WaylandSurface
struct wl_subsurface *subsurface;
struct wl_region *region;
int x;
int y;
int width;
int height;
int gdk_scale;
Metrics metrics;
double actual_scale;
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;

View file

@ -265,6 +265,7 @@ static const int ptrspeeds[4] = { 1, 1, 4, 8 };
S(ToggleBG1), \
S(ToggleBG2), \
S(ToggleBG3), \
S(ToggleBackdrop), \
S(ToggleEmuTurbo), \
S(ToggleSprites), \
S(ToggleTransparency) \
@ -2300,7 +2301,7 @@ void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2)
if (S9xUnfreezeGame(filename.c_str()))
{
snprintf(buf, 256, "Quick save-state %s loaded", ext.c_str());
snprintf(buf, 256, "%s loaded", S9xBasename(filename).c_str());
S9xSetInfoString(buf);
}
else
@ -2321,13 +2322,13 @@ void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2)
case QuickSave009:
case QuickSave010:
{
std::string ext = std::to_string(i - QuickLoad000);
std::string ext = std::to_string(i - QuickSave000);
while (ext.length() < 3)
ext = '0' + ext;
auto filename = S9xGetFilename(ext, SNAPSHOT_DIR);
snprintf(buf, 256, "Quick save-state %s saved", ext.c_str());
snprintf(buf, 256, "%s saved", S9xBasename(filename).c_str());
S9xSetInfoString(buf);
S9xFreezeGame(filename.c_str());
@ -2360,6 +2361,26 @@ void S9xApplyCommand (s9xcommand_t cmd, int16 data1, int16 data2)
S9xSetInfoString("All sound channels on");
break;
case ToggleBackdrop:
switch (Settings.ForcedBackdrop)
{
case 0:
Settings.ForcedBackdrop = 0xf81f;
break;
case 0xf81f:
Settings.ForcedBackdrop = 0x07e0;
break;
case 0x07e0:
Settings.ForcedBackdrop = 0x07ff;
break;
default:
Settings.ForcedBackdrop = 0;
break;
}
sprintf(buf, "Setting backdrop to 0x%04x", Settings.ForcedBackdrop);
S9xSetInfoString(buf);
break;
case ToggleBG0:
Settings.BG_Forced ^= 1;
DisplayStateChange("BG#0", !(Settings.BG_Forced & 1));

View file

@ -143,6 +143,14 @@ void S9xMainLoop (void)
Op = CPU.PCBase[Registers.PCw];
CPU.Cycles += CPU.MemSpeed;
Opcodes = ICPU.S9xOpcodes;
if (CPU.Cycles > 1000000)
{
Settings.StopEmulation = true;
CPU.Flags |= HALTED_FLAG;
S9xMessage(S9X_FATAL_ERROR, 0, "CPU is deadlocked");
return;
}
}
else
{

View file

@ -1,3 +1,33 @@
Snes9x 1.63
General:
- Added a shortcut to change the backdrop color for sprite extraction.
- Fixed QuickSave 0-9 slot shortcuts not working.
- Allow "Address:byte" form for cheat inputs.
- Fixed ZIP files not being closed after patch search.
- Various memmap fixes to allow unofficial mappings.
- Added usage of ImGui to draw things on top of the screen instead of inside.
Win32:
- Fixed AVI not recording audio.
- Fixed framerate throttling in turbo mode (now works during AVI recording).
- Fixed interlaced output speed being double.
- Fixed command line arguments not working.
- Fixed WaveOut device name display for names longer than 31 characters.
- Fixed Bank+/- hotkey saving.
- Added hotkeys for aspect ratio, cheat edit/search.
- Added multiselect for cheat edit dialog.
Gtk:
- Fixed config file location to never put files directly in $HOME and obey
$XDG_CONFIG_HOME.
- Updated translations from JakeSmarter and StanleyKid-22.
Mac:
- Added a new cheat finder.
- Added MultiCart support back.
- Create a blank window when starting the program, so the global menu change
doesn't go unnoticed.
Snes9x 1.62
- Fixed SA1 division with negative dividend again. (Atari2)
- Fixed timing on several instructions. (pi1541)

@ -1 +1 @@
Subproject commit 4e2fdb25671c742a9fbe93a6034eb1542244c7e1
Subproject commit bccaa94db814af33d8ef05c153e7c34d8bd4d685

File diff suppressed because it is too large Load diff

View file

@ -23,12 +23,12 @@ namespace VMA_HPP_NAMESPACE {
return VULKAN_HPP_NAMESPACE::UniqueHandle<T, Dispatcher>(t);
}
template<class T, class O>
VULKAN_HPP_NAMESPACE::UniqueHandle<T, Dispatcher> createUniqueHandle(const T& t, const O* o) VULKAN_HPP_NOEXCEPT {
VULKAN_HPP_NAMESPACE::UniqueHandle<T, Dispatcher> createUniqueHandle(const T& t, O o) VULKAN_HPP_NOEXCEPT {
return VULKAN_HPP_NAMESPACE::UniqueHandle<T, Dispatcher>(t, o);
}
template<class F, class S, class O>
std::pair<VULKAN_HPP_NAMESPACE::UniqueHandle<F, Dispatcher>, VULKAN_HPP_NAMESPACE::UniqueHandle<S, Dispatcher>>
createUniqueHandle(const std::pair<F, S>& t, const O* o) VULKAN_HPP_NOEXCEPT {
createUniqueHandle(const std::pair<F, S>& t, O o) VULKAN_HPP_NOEXCEPT {
return {
VULKAN_HPP_NAMESPACE::UniqueHandle<F, Dispatcher>(t.first, o),
VULKAN_HPP_NAMESPACE::UniqueHandle<S, Dispatcher>(t.second, o)
@ -37,7 +37,7 @@ namespace VMA_HPP_NAMESPACE {
template<class T, class UniqueVectorAllocator, class VectorAllocator, class O>
std::vector<VULKAN_HPP_NAMESPACE::UniqueHandle<T, Dispatcher>, UniqueVectorAllocator>
createUniqueHandleVector(const std::vector<T, VectorAllocator>& vector, const O* o,
createUniqueHandleVector(const std::vector<T, VectorAllocator>& vector, O o,
const UniqueVectorAllocator& vectorAllocator) VULKAN_HPP_NOEXCEPT {
std::vector<VULKAN_HPP_NAMESPACE::UniqueHandle<T, Dispatcher>, UniqueVectorAllocator> result(vectorAllocator);
result.reserve(vector.size());
@ -46,10 +46,10 @@ namespace VMA_HPP_NAMESPACE {
}
template<class T, class Owner> class Deleter {
const Owner* owner;
Owner owner;
public:
Deleter() = default;
Deleter(const Owner* owner) VULKAN_HPP_NOEXCEPT : owner(owner) {}
Deleter(Owner owner) VULKAN_HPP_NOEXCEPT : owner(owner) {}
protected:
void destroy(const T& t) VULKAN_HPP_NOEXCEPT; // Implemented manually for each handle type
};
@ -59,10 +59,12 @@ namespace VMA_HPP_NAMESPACE {
};
}
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<Buffer, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<Buffer, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<Buffer, VMA_HPP_NAMESPACE::Allocator>;
};
template<> struct UniqueHandleTraits<Image, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<Image, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<Image, VMA_HPP_NAMESPACE::Allocator>;
};
}
@ -83,11 +85,11 @@ namespace VMA_HPP_NAMESPACE {
# define VMA_HPP_DESTROY_IMPL(NAME) \
template<> VULKAN_HPP_INLINE void VULKAN_HPP_NAMESPACE::UniqueHandleTraits<NAME, Dispatcher>::deleter::destroy(const NAME& t) VULKAN_HPP_NOEXCEPT
VMA_HPP_DESTROY_IMPL(VULKAN_HPP_NAMESPACE::Buffer) { owner->destroyBuffer(t, nullptr); }
VMA_HPP_DESTROY_IMPL(VULKAN_HPP_NAMESPACE::Image) { owner->destroyImage(t, nullptr); }
VMA_HPP_DESTROY_IMPL(Pool) { owner->destroyPool(t); }
VMA_HPP_DESTROY_IMPL(Allocation) { owner->freeMemory(t); }
VMA_HPP_DESTROY_IMPL(VirtualAllocation) { owner->virtualFree(t); }
VMA_HPP_DESTROY_IMPL(VULKAN_HPP_NAMESPACE::Buffer) { owner.destroyBuffer(t, nullptr); }
VMA_HPP_DESTROY_IMPL(VULKAN_HPP_NAMESPACE::Image) { owner.destroyImage(t, nullptr); }
VMA_HPP_DESTROY_IMPL(Pool) { owner.destroyPool(t); }
VMA_HPP_DESTROY_IMPL(Allocation) { owner.freeMemory(t); }
VMA_HPP_DESTROY_IMPL(VirtualAllocation) { owner.virtualFree(t); }
# undef VMA_HPP_DESTROY_IMPL
#endif

View file

@ -10,9 +10,11 @@ namespace VMA_HPP_NAMESPACE {
eExtMemoryBudget = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT,
eAmdDeviceCoherentMemory = VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT,
eBufferDeviceAddress = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
eExtMemoryPriority = VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT
eExtMemoryPriority = VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT,
eKhrMaintenance4 = VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(AllocatorCreateFlagBits value) {
if (value == AllocatorCreateFlagBits::eExternallySynchronized) return "ExternallySynchronized";
if (value == AllocatorCreateFlagBits::eKhrDedicatedAllocation) return "KhrDedicatedAllocation";
@ -21,8 +23,10 @@ namespace VMA_HPP_NAMESPACE {
if (value == AllocatorCreateFlagBits::eAmdDeviceCoherentMemory) return "AmdDeviceCoherentMemory";
if (value == AllocatorCreateFlagBits::eBufferDeviceAddress) return "BufferDeviceAddress";
if (value == AllocatorCreateFlagBits::eExtMemoryPriority) return "ExtMemoryPriority";
if (value == AllocatorCreateFlagBits::eKhrMaintenance4) return "KhrMaintenance4";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VULKAN_HPP_NAMESPACE {
@ -35,7 +39,8 @@ namespace VULKAN_HPP_NAMESPACE {
| VMA_HPP_NAMESPACE::AllocatorCreateFlagBits::eExtMemoryBudget
| VMA_HPP_NAMESPACE::AllocatorCreateFlagBits::eAmdDeviceCoherentMemory
| VMA_HPP_NAMESPACE::AllocatorCreateFlagBits::eBufferDeviceAddress
| VMA_HPP_NAMESPACE::AllocatorCreateFlagBits::eExtMemoryPriority;
| VMA_HPP_NAMESPACE::AllocatorCreateFlagBits::eExtMemoryPriority
| VMA_HPP_NAMESPACE::AllocatorCreateFlagBits::eKhrMaintenance4;
};
}
@ -59,6 +64,7 @@ namespace VMA_HPP_NAMESPACE {
return ~(AllocatorCreateFlags(bits));
}
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(AllocatorCreateFlags value) {
if (!value) return "{}";
std::string result;
@ -69,8 +75,10 @@ namespace VMA_HPP_NAMESPACE {
if (value & AllocatorCreateFlagBits::eAmdDeviceCoherentMemory) result += "AmdDeviceCoherentMemory | ";
if (value & AllocatorCreateFlagBits::eBufferDeviceAddress) result += "BufferDeviceAddress | ";
if (value & AllocatorCreateFlagBits::eExtMemoryPriority) result += "ExtMemoryPriority | ";
if (value & AllocatorCreateFlagBits::eKhrMaintenance4) result += "KhrMaintenance4 | ";
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -88,6 +96,7 @@ namespace VMA_HPP_NAMESPACE {
eAutoPreferHost = VMA_MEMORY_USAGE_AUTO_PREFER_HOST
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(MemoryUsage value) {
if (value == MemoryUsage::eUnknown) return "Unknown";
if (value == MemoryUsage::eGpuOnly) return "GpuOnly";
@ -101,6 +110,7 @@ namespace VMA_HPP_NAMESPACE {
if (value == MemoryUsage::eAutoPreferHost) return "AutoPreferHost";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -124,6 +134,7 @@ namespace VMA_HPP_NAMESPACE {
eStrategyFirstFit = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(AllocationCreateFlagBits value) {
if (value == AllocationCreateFlagBits::eDedicatedMemory) return "DedicatedMemory";
if (value == AllocationCreateFlagBits::eNeverAllocate) return "NeverAllocate";
@ -143,6 +154,7 @@ namespace VMA_HPP_NAMESPACE {
if (value == AllocationCreateFlagBits::eStrategyFirstFit) return "StrategyFirstFit";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VULKAN_HPP_NAMESPACE {
@ -188,6 +200,7 @@ namespace VMA_HPP_NAMESPACE {
return ~(AllocationCreateFlags(bits));
}
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(AllocationCreateFlags value) {
if (!value) return "{}";
std::string result;
@ -209,6 +222,7 @@ namespace VMA_HPP_NAMESPACE {
if (value & AllocationCreateFlagBits::eStrategyFirstFit) result += "StrategyFirstFit | ";
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -218,11 +232,13 @@ namespace VMA_HPP_NAMESPACE {
eLinearAlgorithm = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(PoolCreateFlagBits value) {
if (value == PoolCreateFlagBits::eIgnoreBufferImageGranularity) return "IgnoreBufferImageGranularity";
if (value == PoolCreateFlagBits::eLinearAlgorithm) return "LinearAlgorithm";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VULKAN_HPP_NAMESPACE {
@ -254,6 +270,7 @@ namespace VMA_HPP_NAMESPACE {
return ~(PoolCreateFlags(bits));
}
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(PoolCreateFlags value) {
if (!value) return "{}";
std::string result;
@ -261,6 +278,7 @@ namespace VMA_HPP_NAMESPACE {
if (value & PoolCreateFlagBits::eLinearAlgorithm) result += "LinearAlgorithm | ";
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -272,6 +290,7 @@ namespace VMA_HPP_NAMESPACE {
eFlagAlgorithmExtensive = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(DefragmentationFlagBits value) {
if (value == DefragmentationFlagBits::eFlagAlgorithmFast) return "FlagAlgorithmFast";
if (value == DefragmentationFlagBits::eFlagAlgorithmBalanced) return "FlagAlgorithmBalanced";
@ -279,6 +298,7 @@ namespace VMA_HPP_NAMESPACE {
if (value == DefragmentationFlagBits::eFlagAlgorithmExtensive) return "FlagAlgorithmExtensive";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VULKAN_HPP_NAMESPACE {
@ -312,6 +332,7 @@ namespace VMA_HPP_NAMESPACE {
return ~(DefragmentationFlags(bits));
}
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(DefragmentationFlags value) {
if (!value) return "{}";
std::string result;
@ -321,6 +342,7 @@ namespace VMA_HPP_NAMESPACE {
if (value & DefragmentationFlagBits::eFlagAlgorithmExtensive) result += "FlagAlgorithmExtensive | ";
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -331,12 +353,14 @@ namespace VMA_HPP_NAMESPACE {
eDestroy = VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(DefragmentationMoveOperation value) {
if (value == DefragmentationMoveOperation::eCopy) return "Copy";
if (value == DefragmentationMoveOperation::eIgnore) return "Ignore";
if (value == DefragmentationMoveOperation::eDestroy) return "Destroy";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -345,10 +369,12 @@ namespace VMA_HPP_NAMESPACE {
eLinearAlgorithm = VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(VirtualBlockCreateFlagBits value) {
if (value == VirtualBlockCreateFlagBits::eLinearAlgorithm) return "LinearAlgorithm";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VULKAN_HPP_NAMESPACE {
@ -379,12 +405,14 @@ namespace VMA_HPP_NAMESPACE {
return ~(VirtualBlockCreateFlags(bits));
}
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(VirtualBlockCreateFlags value) {
if (!value) return "{}";
std::string result;
if (value & VirtualBlockCreateFlagBits::eLinearAlgorithm) result += "LinearAlgorithm | ";
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
}
# endif
}
namespace VMA_HPP_NAMESPACE {
@ -396,6 +424,7 @@ namespace VMA_HPP_NAMESPACE {
eStrategyMinOffset = VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT
};
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(VirtualAllocationCreateFlagBits value) {
if (value == VirtualAllocationCreateFlagBits::eUpperAddress) return "UpperAddress";
if (value == VirtualAllocationCreateFlagBits::eStrategyMinMemory) return "StrategyMinMemory";
@ -403,6 +432,7 @@ namespace VMA_HPP_NAMESPACE {
if (value == VirtualAllocationCreateFlagBits::eStrategyMinOffset) return "StrategyMinOffset";
return "invalid ( " + VULKAN_HPP_NAMESPACE::toHexString(static_cast<uint32_t>(value)) + " )";
}
# endif
}
namespace VULKAN_HPP_NAMESPACE {
@ -436,6 +466,7 @@ namespace VMA_HPP_NAMESPACE {
return ~(VirtualAllocationCreateFlags(bits));
}
# if !defined( VULKAN_HPP_NO_TO_STRING )
VULKAN_HPP_INLINE std::string to_string(VirtualAllocationCreateFlags value) {
if (!value) return "{}";
std::string result;
@ -445,6 +476,7 @@ namespace VMA_HPP_NAMESPACE {
if (value & VirtualAllocationCreateFlagBits::eStrategyMinOffset) result += "StrategyMinOffset | ";
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
}
# endif
}
#endif

View file

@ -160,7 +160,7 @@ namespace VMA_HPP_NAMESPACE {
Pool pool;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreatePool(m_allocator, reinterpret_cast<const VmaPoolCreateInfo*>(&createInfo), reinterpret_cast<VmaPool*>(&pool)) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::createPool");
return createResultValueType(result, createUniqueHandle(pool, this));
return createResultValueType(result, createUniqueHandle(pool, *this));
}
#endif
#endif
@ -257,7 +257,7 @@ namespace VMA_HPP_NAMESPACE {
Allocation allocation;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaAllocateMemory(m_allocator, reinterpret_cast<const VkMemoryRequirements*>(&vkMemoryRequirements), reinterpret_cast<const VmaAllocationCreateInfo*>(&createInfo), reinterpret_cast<VmaAllocation*>(&allocation), reinterpret_cast<VmaAllocationInfo*>(static_cast<AllocationInfo*>(allocationInfo))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::allocateMemory");
return createResultValueType(result, createUniqueHandle(allocation, this));
return createResultValueType(result, createUniqueHandle(allocation, *this));
}
#endif
#endif
@ -306,7 +306,7 @@ namespace VMA_HPP_NAMESPACE {
std::vector<Allocation> allocations(allocationCount);
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaAllocateMemoryPages(m_allocator, reinterpret_cast<const VkMemoryRequirements*>(vkMemoryRequirements.data()), reinterpret_cast<const VmaAllocationCreateInfo*>(createInfo.data()), allocationCount, reinterpret_cast<VmaAllocation*>(allocations.data()), reinterpret_cast<VmaAllocationInfo*>(allocationInfo.data())) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::allocateMemoryPages");
return createResultValueType(result, createUniqueHandleVector(allocations, this, vectorAllocator));
return createResultValueType(result, createUniqueHandleVector(allocations, *this, vectorAllocator));
}
template<typename VectorAllocator>
@ -317,7 +317,7 @@ namespace VMA_HPP_NAMESPACE {
std::vector<Allocation> allocations(allocationCount);
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaAllocateMemoryPages(m_allocator, reinterpret_cast<const VkMemoryRequirements*>(vkMemoryRequirements.data()), reinterpret_cast<const VmaAllocationCreateInfo*>(createInfo.data()), allocationCount, reinterpret_cast<VmaAllocation*>(allocations.data()), reinterpret_cast<VmaAllocationInfo*>(allocationInfo.data())) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::allocateMemoryPages");
return createResultValueType(result, createUniqueHandleVector(allocations, this, VectorAllocator()));
return createResultValueType(result, createUniqueHandleVector(allocations, *this, VectorAllocator()));
}
#endif
#endif
@ -346,7 +346,7 @@ namespace VMA_HPP_NAMESPACE {
Allocation allocation;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaAllocateMemoryForBuffer(m_allocator, static_cast<VkBuffer>(buffer), reinterpret_cast<const VmaAllocationCreateInfo*>(&createInfo), reinterpret_cast<VmaAllocation*>(&allocation), reinterpret_cast<VmaAllocationInfo*>(static_cast<AllocationInfo*>(allocationInfo))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::allocateMemoryForBuffer");
return createResultValueType(result, createUniqueHandle(allocation, this));
return createResultValueType(result, createUniqueHandle(allocation, *this));
}
#endif
#endif
@ -374,7 +374,7 @@ namespace VMA_HPP_NAMESPACE {
Allocation allocation;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaAllocateMemoryForImage(m_allocator, static_cast<VkImage>(image), reinterpret_cast<const VmaAllocationCreateInfo*>(&createInfo), reinterpret_cast<VmaAllocation*>(&allocation), reinterpret_cast<VmaAllocationInfo*>(static_cast<AllocationInfo*>(allocationInfo))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::allocateMemoryForImage");
return createResultValueType(result, createUniqueHandle(allocation, this));
return createResultValueType(result, createUniqueHandle(allocation, *this));
}
#endif
#endif
@ -387,12 +387,12 @@ namespace VMA_HPP_NAMESPACE {
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE void Allocator::freeMemory(const Allocation allocation) const {
vmaFreeMemory(m_allocator, static_cast<const VmaAllocation>(allocation));
VULKAN_HPP_INLINE void Allocator::freeMemory(Allocation allocation) const {
vmaFreeMemory(m_allocator, static_cast<VmaAllocation>(allocation));
}
#else
VULKAN_HPP_INLINE void Allocator::freeMemory(const Allocation allocation) const {
vmaFreeMemory(m_allocator, static_cast<const VmaAllocation>(allocation));
VULKAN_HPP_INLINE void Allocator::freeMemory(Allocation allocation) const {
vmaFreeMemory(m_allocator, static_cast<VmaAllocation>(allocation));
}
#endif
@ -419,6 +419,18 @@ namespace VMA_HPP_NAMESPACE {
vmaGetAllocationInfo(m_allocator, static_cast<VmaAllocation>(allocation), reinterpret_cast<VmaAllocationInfo*>(allocationInfo));
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE AllocationInfo2 Allocator::getAllocationInfo2(Allocation allocation) const {
AllocationInfo2 allocationInfo;
vmaGetAllocationInfo2(m_allocator, static_cast<VmaAllocation>(allocation), reinterpret_cast<VmaAllocationInfo2*>(&allocationInfo));
return allocationInfo;
}
#endif
VULKAN_HPP_INLINE void Allocator::getAllocationInfo2(Allocation allocation,
AllocationInfo2* allocationInfo) const {
vmaGetAllocationInfo2(m_allocator, static_cast<VmaAllocation>(allocation), reinterpret_cast<VmaAllocationInfo2*>(allocationInfo));
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE void Allocator::setAllocationUserData(Allocation allocation,
void* userData) const {
@ -549,6 +561,57 @@ namespace VMA_HPP_NAMESPACE {
return result;
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE typename VULKAN_HPP_NAMESPACE::ResultValueType<void>::type Allocator::copyMemoryToAllocation(const void* srcHostPointer,
Allocation dstAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize dstAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size) const {
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCopyMemoryToAllocation(m_allocator, srcHostPointer, static_cast<VmaAllocation>(dstAllocation), static_cast<VkDeviceSize>(dstAllocationLocalOffset), static_cast<VkDeviceSize>(size)) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::copyMemoryToAllocation");
return createResultValueType(result);
}
#else
VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result Allocator::copyMemoryToAllocation(const void* srcHostPointer,
Allocation dstAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize dstAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size) const {
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCopyMemoryToAllocation(m_allocator, srcHostPointer, static_cast<VmaAllocation>(dstAllocation), static_cast<VkDeviceSize>(dstAllocationLocalOffset), static_cast<VkDeviceSize>(size)) );
return result;
}
#endif
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
template<typename VectorAllocator,
typename B,
typename std::enable_if<std::is_same<typename B::value_type, void>::value, int>::type>
VULKAN_HPP_INLINE typename VULKAN_HPP_NAMESPACE::ResultValueType<std::vector<void, VectorAllocator>>::type Allocator::copyAllocationToMemory(Allocation srcAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize srcAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size,
VectorAllocator& vectorAllocator) const {
std::vector<void, VectorAllocator> dstHostPointer(size, vectorAllocator);
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCopyAllocationToMemory(m_allocator, static_cast<VmaAllocation>(srcAllocation), static_cast<VkDeviceSize>(srcAllocationLocalOffset), &dstHostPointer, static_cast<VkDeviceSize>(size)) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::copyAllocationToMemory");
return createResultValueType(result, dstHostPointer);
}
template<typename VectorAllocator>
VULKAN_HPP_INLINE typename VULKAN_HPP_NAMESPACE::ResultValueType<std::vector<void, VectorAllocator>>::type Allocator::copyAllocationToMemory(Allocation srcAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize srcAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size) const {
std::vector<void, VectorAllocator> dstHostPointer(size);
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCopyAllocationToMemory(m_allocator, static_cast<VmaAllocation>(srcAllocation), static_cast<VkDeviceSize>(srcAllocationLocalOffset), &dstHostPointer, static_cast<VkDeviceSize>(size)) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::copyAllocationToMemory");
return createResultValueType(result, dstHostPointer);
}
#endif
VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result Allocator::copyAllocationToMemory(Allocation srcAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize srcAllocationLocalOffset,
void* dstHostPointer,
VULKAN_HPP_NAMESPACE::DeviceSize size) const {
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCopyAllocationToMemory(m_allocator, static_cast<VmaAllocation>(srcAllocation), static_cast<VkDeviceSize>(srcAllocationLocalOffset), dstHostPointer, static_cast<VkDeviceSize>(size)) );
return result;
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE typename VULKAN_HPP_NAMESPACE::ResultValueType<void>::type Allocator::checkCorruption(uint32_t memoryTypeBits) const {
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCheckCorruption(m_allocator, memoryTypeBits) );
@ -703,7 +766,7 @@ namespace VMA_HPP_NAMESPACE {
Allocation& allocation = pair.second;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateBuffer(m_allocator, reinterpret_cast<const VkBufferCreateInfo*>(&bufferCreateInfo), reinterpret_cast<const VmaAllocationCreateInfo*>(&allocationCreateInfo), reinterpret_cast<VkBuffer*>(&buffer), reinterpret_cast<VmaAllocation*>(&allocation), reinterpret_cast<VmaAllocationInfo*>(static_cast<AllocationInfo*>(allocationInfo))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::createBuffer");
return createResultValueType(result, createUniqueHandle(pair, this));
return createResultValueType(result, createUniqueHandle(pair, *this));
}
#endif
#endif
@ -738,7 +801,7 @@ namespace VMA_HPP_NAMESPACE {
Allocation& allocation = pair.second;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateBufferWithAlignment(m_allocator, reinterpret_cast<const VkBufferCreateInfo*>(&bufferCreateInfo), reinterpret_cast<const VmaAllocationCreateInfo*>(&allocationCreateInfo), static_cast<VkDeviceSize>(minAlignment), reinterpret_cast<VkBuffer*>(&buffer), reinterpret_cast<VmaAllocation*>(&allocation), reinterpret_cast<VmaAllocationInfo*>(static_cast<AllocationInfo*>(allocationInfo))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::createBufferWithAlignment");
return createResultValueType(result, createUniqueHandle(pair, this));
return createResultValueType(result, createUniqueHandle(pair, *this));
}
#endif
#endif
@ -768,6 +831,24 @@ namespace VMA_HPP_NAMESPACE {
return result;
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE typename VULKAN_HPP_NAMESPACE::ResultValueType<VULKAN_HPP_NAMESPACE::Buffer>::type Allocator::createAliasingBuffer2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::BufferCreateInfo& bufferCreateInfo) const {
VULKAN_HPP_NAMESPACE::Buffer buffer;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateAliasingBuffer2(m_allocator, static_cast<VmaAllocation>(allocation), static_cast<VkDeviceSize>(allocationLocalOffset), reinterpret_cast<const VkBufferCreateInfo*>(&bufferCreateInfo), reinterpret_cast<VkBuffer*>(&buffer)) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::createAliasingBuffer2");
return createResultValueType(result, buffer);
}
#endif
VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result Allocator::createAliasingBuffer2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::BufferCreateInfo* bufferCreateInfo,
VULKAN_HPP_NAMESPACE::Buffer* buffer) const {
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateAliasingBuffer2(m_allocator, static_cast<VmaAllocation>(allocation), static_cast<VkDeviceSize>(allocationLocalOffset), reinterpret_cast<const VkBufferCreateInfo*>(bufferCreateInfo), reinterpret_cast<VkBuffer*>(buffer)) );
return result;
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE void Allocator::destroyBuffer(VULKAN_HPP_NAMESPACE::Buffer buffer,
Allocation allocation) const {
@ -800,7 +881,7 @@ namespace VMA_HPP_NAMESPACE {
Allocation& allocation = pair.second;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateImage(m_allocator, reinterpret_cast<const VkImageCreateInfo*>(&imageCreateInfo), reinterpret_cast<const VmaAllocationCreateInfo*>(&allocationCreateInfo), reinterpret_cast<VkImage*>(&image), reinterpret_cast<VmaAllocation*>(&allocation), reinterpret_cast<VmaAllocationInfo*>(static_cast<AllocationInfo*>(allocationInfo))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::createImage");
return createResultValueType(result, createUniqueHandle(pair, this));
return createResultValueType(result, createUniqueHandle(pair, *this));
}
#endif
#endif
@ -829,6 +910,24 @@ namespace VMA_HPP_NAMESPACE {
return result;
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE typename VULKAN_HPP_NAMESPACE::ResultValueType<VULKAN_HPP_NAMESPACE::Image>::type Allocator::createAliasingImage2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::ImageCreateInfo& imageCreateInfo) const {
VULKAN_HPP_NAMESPACE::Image image;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateAliasingImage2(m_allocator, static_cast<VmaAllocation>(allocation), static_cast<VkDeviceSize>(allocationLocalOffset), reinterpret_cast<const VkImageCreateInfo*>(&imageCreateInfo), reinterpret_cast<VkImage*>(&image)) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::Allocator::createAliasingImage2");
return createResultValueType(result, image);
}
#endif
VULKAN_HPP_INLINE VULKAN_HPP_NAMESPACE::Result Allocator::createAliasingImage2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::ImageCreateInfo* imageCreateInfo,
VULKAN_HPP_NAMESPACE::Image* image) const {
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaCreateAliasingImage2(m_allocator, static_cast<VmaAllocation>(allocation), static_cast<VkDeviceSize>(allocationLocalOffset), reinterpret_cast<const VkImageCreateInfo*>(imageCreateInfo), reinterpret_cast<VkImage*>(image)) );
return result;
}
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_INLINE void Allocator::destroyImage(VULKAN_HPP_NAMESPACE::Image image,
Allocation allocation) const {
@ -913,7 +1012,7 @@ namespace VMA_HPP_NAMESPACE {
VirtualAllocation allocation;
VULKAN_HPP_NAMESPACE::Result result = static_cast<VULKAN_HPP_NAMESPACE::Result>( vmaVirtualAllocate(m_virtualBlock, reinterpret_cast<const VmaVirtualAllocationCreateInfo*>(&createInfo), reinterpret_cast<VmaVirtualAllocation*>(&allocation), reinterpret_cast<VkDeviceSize*>(static_cast<VULKAN_HPP_NAMESPACE::DeviceSize*>(offset))) );
resultCheck(result, VMA_HPP_NAMESPACE_STRING "::VirtualBlock::virtualAllocate");
return createResultValueType(result, createUniqueHandle(allocation, this));
return createResultValueType(result, createUniqueHandle(allocation, *this));
}
#endif
#endif

View file

@ -14,6 +14,7 @@ namespace VMA_HPP_NAMESPACE {
struct AllocationCreateInfo;
struct PoolCreateInfo;
struct AllocationInfo;
struct AllocationInfo2;
struct DefragmentationInfo;
struct DefragmentationMove;
struct DefragmentationPassMoveInfo;
@ -22,11 +23,11 @@ namespace VMA_HPP_NAMESPACE {
struct VirtualAllocationCreateInfo;
struct VirtualAllocationInfo;
class Allocator;
class Pool;
class Allocation;
class DefragmentationContext;
class VirtualAllocation;
class Allocator;
class VirtualBlock;
}
@ -81,7 +82,8 @@ namespace VMA_HPP_NAMESPACE {
}
#ifndef VULKAN_HPP_NO_SMART_HANDLE
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<VMA_HPP_NAMESPACE::Pool, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<VMA_HPP_NAMESPACE::Pool, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<VMA_HPP_NAMESPACE::Pool, VMA_HPP_NAMESPACE::Allocator>;
};
}
@ -138,7 +140,8 @@ namespace VMA_HPP_NAMESPACE {
}
#ifndef VULKAN_HPP_NO_SMART_HANDLE
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<VMA_HPP_NAMESPACE::Allocation, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<VMA_HPP_NAMESPACE::Allocation, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<VMA_HPP_NAMESPACE::Allocation, VMA_HPP_NAMESPACE::Allocator>;
};
}
@ -193,14 +196,6 @@ namespace VMA_HPP_NAMESPACE {
VULKAN_HPP_STATIC_ASSERT(sizeof(DefragmentationContext) == sizeof(VmaDefragmentationContext),
"handle and wrapper have different size!");
}
#ifndef VULKAN_HPP_NO_SMART_HANDLE
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<VMA_HPP_NAMESPACE::DefragmentationContext, VMA_HPP_NAMESPACE::Dispatcher> {
using deleter = VMA_HPP_NAMESPACE::Deleter<VMA_HPP_NAMESPACE::DefragmentationContext, void>;
};
}
namespace VMA_HPP_NAMESPACE { using UniqueDefragmentationContext = VULKAN_HPP_NAMESPACE::UniqueHandle<DefragmentationContext, Dispatcher>; }
#endif
namespace VMA_HPP_NAMESPACE {
class Allocator {
@ -444,9 +439,9 @@ namespace VMA_HPP_NAMESPACE {
AllocationInfo* allocationInfo) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
void freeMemory(const Allocation allocation) const;
void freeMemory(Allocation allocation) const;
#else
void freeMemory(const Allocation allocation) const;
void freeMemory(Allocation allocation) const;
#endif
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
@ -461,6 +456,12 @@ namespace VMA_HPP_NAMESPACE {
void getAllocationInfo(Allocation allocation,
AllocationInfo* allocationInfo) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS AllocationInfo2 getAllocationInfo2(Allocation allocation) const;
#endif
void getAllocationInfo2(Allocation allocation,
AllocationInfo2* allocationInfo) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
void setAllocationUserData(Allocation allocation,
void* userData) const;
@ -535,6 +536,37 @@ namespace VMA_HPP_NAMESPACE {
const VULKAN_HPP_NAMESPACE::DeviceSize* offsets,
const VULKAN_HPP_NAMESPACE::DeviceSize* sizes) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
typename VULKAN_HPP_NAMESPACE::ResultValueType<void>::type copyMemoryToAllocation(const void* srcHostPointer,
Allocation dstAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize dstAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size) const;
#else
VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::Result copyMemoryToAllocation(const void* srcHostPointer,
Allocation dstAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize dstAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size) const;
#endif
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
template<typename VectorAllocator = std::allocator<void>,
typename B = VectorAllocator,
typename std::enable_if<std::is_same<typename B::value_type, void>::value, int>::type = 0>
VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS typename VULKAN_HPP_NAMESPACE::ResultValueType<std::vector<void, VectorAllocator>>::type copyAllocationToMemory(Allocation srcAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize srcAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size,
VectorAllocator& vectorAllocator) const;
template<typename VectorAllocator = std::allocator<void>>
VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS typename VULKAN_HPP_NAMESPACE::ResultValueType<std::vector<void, VectorAllocator>>::type copyAllocationToMemory(Allocation srcAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize srcAllocationLocalOffset,
VULKAN_HPP_NAMESPACE::DeviceSize size) const;
#endif
VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::Result copyAllocationToMemory(Allocation srcAllocation,
VULKAN_HPP_NAMESPACE::DeviceSize srcAllocationLocalOffset,
void* dstHostPointer,
VULKAN_HPP_NAMESPACE::DeviceSize size) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
typename VULKAN_HPP_NAMESPACE::ResultValueType<void>::type checkCorruption(uint32_t memoryTypeBits) const;
#else
@ -649,6 +681,16 @@ namespace VMA_HPP_NAMESPACE {
const VULKAN_HPP_NAMESPACE::BufferCreateInfo* bufferCreateInfo,
VULKAN_HPP_NAMESPACE::Buffer* buffer) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS typename VULKAN_HPP_NAMESPACE::ResultValueType<VULKAN_HPP_NAMESPACE::Buffer>::type createAliasingBuffer2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::BufferCreateInfo& bufferCreateInfo) const;
#endif
VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::Result createAliasingBuffer2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::BufferCreateInfo* bufferCreateInfo,
VULKAN_HPP_NAMESPACE::Buffer* buffer) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
void destroyBuffer(VULKAN_HPP_NAMESPACE::Buffer buffer,
Allocation allocation) const;
@ -681,6 +723,16 @@ namespace VMA_HPP_NAMESPACE {
const VULKAN_HPP_NAMESPACE::ImageCreateInfo* imageCreateInfo,
VULKAN_HPP_NAMESPACE::Image* image) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
VULKAN_HPP_NODISCARD_WHEN_NO_EXCEPTIONS typename VULKAN_HPP_NAMESPACE::ResultValueType<VULKAN_HPP_NAMESPACE::Image>::type createAliasingImage2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::ImageCreateInfo& imageCreateInfo) const;
#endif
VULKAN_HPP_NODISCARD VULKAN_HPP_NAMESPACE::Result createAliasingImage2(Allocation allocation,
VULKAN_HPP_NAMESPACE::DeviceSize allocationLocalOffset,
const VULKAN_HPP_NAMESPACE::ImageCreateInfo* imageCreateInfo,
VULKAN_HPP_NAMESPACE::Image* image) const;
#ifndef VULKAN_HPP_DISABLE_ENHANCED_MODE
void destroyImage(VULKAN_HPP_NAMESPACE::Image image,
Allocation allocation) const;
@ -711,7 +763,8 @@ namespace VMA_HPP_NAMESPACE {
}
#ifndef VULKAN_HPP_NO_SMART_HANDLE
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<VMA_HPP_NAMESPACE::Allocator, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<VMA_HPP_NAMESPACE::Allocator, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<VMA_HPP_NAMESPACE::Allocator, void>;
};
}
@ -768,7 +821,8 @@ namespace VMA_HPP_NAMESPACE {
}
#ifndef VULKAN_HPP_NO_SMART_HANDLE
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<VMA_HPP_NAMESPACE::VirtualAllocation, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<VMA_HPP_NAMESPACE::VirtualAllocation, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<VMA_HPP_NAMESPACE::VirtualAllocation, VMA_HPP_NAMESPACE::VirtualBlock>;
};
}
@ -899,7 +953,8 @@ namespace VMA_HPP_NAMESPACE {
}
#ifndef VULKAN_HPP_NO_SMART_HANDLE
namespace VULKAN_HPP_NAMESPACE {
template<> struct UniqueHandleTraits<VMA_HPP_NAMESPACE::VirtualBlock, VMA_HPP_NAMESPACE::Dispatcher> {
template<> class UniqueHandleTraits<VMA_HPP_NAMESPACE::VirtualBlock, VMA_HPP_NAMESPACE::Dispatcher> {
public:
using deleter = VMA_HPP_NAMESPACE::Deleter<VMA_HPP_NAMESPACE::VirtualBlock, void>;
};
}

View file

@ -111,9 +111,9 @@ namespace VMA_HPP_NAMESPACE {
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
, PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2KHR_ = {}
#endif
#if VMA_VULKAN_VERSION >= 1003000
, PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements_ = {}
, PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements_ = {}
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
, PFN_vkGetDeviceBufferMemoryRequirementsKHR vkGetDeviceBufferMemoryRequirements_ = {}
, PFN_vkGetDeviceImageMemoryRequirementsKHR vkGetDeviceImageMemoryRequirements_ = {}
#endif
) VULKAN_HPP_NOEXCEPT
: vkGetInstanceProcAddr(vkGetInstanceProcAddr_)
@ -146,7 +146,7 @@ namespace VMA_HPP_NAMESPACE {
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
, vkGetPhysicalDeviceMemoryProperties2KHR(vkGetPhysicalDeviceMemoryProperties2KHR_)
#endif
#if VMA_VULKAN_VERSION >= 1003000
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
, vkGetDeviceBufferMemoryRequirements(vkGetDeviceBufferMemoryRequirements_)
, vkGetDeviceImageMemoryRequirements(vkGetDeviceImageMemoryRequirements_)
#endif
@ -204,7 +204,7 @@ namespace VMA_HPP_NAMESPACE {
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
&& vkGetPhysicalDeviceMemoryProperties2KHR == rhs.vkGetPhysicalDeviceMemoryProperties2KHR
#endif
#if VMA_VULKAN_VERSION >= 1003000
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
&& vkGetDeviceBufferMemoryRequirements == rhs.vkGetDeviceBufferMemoryRequirements
&& vkGetDeviceImageMemoryRequirements == rhs.vkGetDeviceImageMemoryRequirements
#endif
@ -339,14 +339,14 @@ namespace VMA_HPP_NAMESPACE {
return *this;
}
#endif
#if VMA_VULKAN_VERSION >= 1003000
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
VULKAN_HPP_CONSTEXPR_14 VulkanFunctions& setVkGetDeviceBufferMemoryRequirements(PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements_) VULKAN_HPP_NOEXCEPT {
VULKAN_HPP_CONSTEXPR_14 VulkanFunctions& setVkGetDeviceBufferMemoryRequirements(PFN_vkGetDeviceBufferMemoryRequirementsKHR vkGetDeviceBufferMemoryRequirements_) VULKAN_HPP_NOEXCEPT {
vkGetDeviceBufferMemoryRequirements = vkGetDeviceBufferMemoryRequirements_;
return *this;
}
VULKAN_HPP_CONSTEXPR_14 VulkanFunctions& setVkGetDeviceImageMemoryRequirements(PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements_) VULKAN_HPP_NOEXCEPT {
VULKAN_HPP_CONSTEXPR_14 VulkanFunctions& setVkGetDeviceImageMemoryRequirements(PFN_vkGetDeviceImageMemoryRequirementsKHR vkGetDeviceImageMemoryRequirements_) VULKAN_HPP_NOEXCEPT {
vkGetDeviceImageMemoryRequirements = vkGetDeviceImageMemoryRequirements_;
return *this;
}
@ -384,9 +384,9 @@ namespace VMA_HPP_NAMESPACE {
#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000
PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2KHR = {};
#endif
#if VMA_VULKAN_VERSION >= 1003000
PFN_vkGetDeviceBufferMemoryRequirements vkGetDeviceBufferMemoryRequirements = {};
PFN_vkGetDeviceImageMemoryRequirements vkGetDeviceImageMemoryRequirements = {};
#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000
PFN_vkGetDeviceBufferMemoryRequirementsKHR vkGetDeviceBufferMemoryRequirements = {};
PFN_vkGetDeviceImageMemoryRequirementsKHR vkGetDeviceImageMemoryRequirements = {};
#endif
};
VULKAN_HPP_STATIC_ASSERT(sizeof(VulkanFunctions) == sizeof(VmaVulkanFunctions),
@ -1297,6 +1297,79 @@ namespace VMA_HPP_NAMESPACE {
VULKAN_HPP_STATIC_ASSERT(std::is_nothrow_move_constructible<AllocationInfo>::value,
"AllocationInfo is not nothrow_move_constructible!");
struct AllocationInfo2 {
using NativeType = VmaAllocationInfo2;
#if !defined( VULKAN_HPP_NO_STRUCT_CONSTRUCTORS )
VULKAN_HPP_CONSTEXPR AllocationInfo2(
AllocationInfo allocationInfo_ = {}
, VULKAN_HPP_NAMESPACE::DeviceSize blockSize_ = {}
, VULKAN_HPP_NAMESPACE::Bool32 dedicatedMemory_ = {}
) VULKAN_HPP_NOEXCEPT
: allocationInfo(allocationInfo_)
, blockSize(blockSize_)
, dedicatedMemory(dedicatedMemory_)
{}
VULKAN_HPP_CONSTEXPR AllocationInfo2(AllocationInfo2 const &) VULKAN_HPP_NOEXCEPT = default;
AllocationInfo2(VmaAllocationInfo2 const & rhs) VULKAN_HPP_NOEXCEPT : AllocationInfo2(*reinterpret_cast<AllocationInfo2 const *>(&rhs)) {}
#endif
AllocationInfo2& operator=(AllocationInfo2 const &) VULKAN_HPP_NOEXCEPT = default;
AllocationInfo2& operator=(VmaAllocationInfo2 const & rhs) VULKAN_HPP_NOEXCEPT {
*this = *reinterpret_cast<VMA_HPP_NAMESPACE::AllocationInfo2 const *>(&rhs);
return *this;
}
explicit operator VmaAllocationInfo2 const &() const VULKAN_HPP_NOEXCEPT {
return *reinterpret_cast<const VmaAllocationInfo2 *>(this);
}
explicit operator VmaAllocationInfo2&() VULKAN_HPP_NOEXCEPT {
return *reinterpret_cast<VmaAllocationInfo2 *>(this);
}
#if defined( VULKAN_HPP_HAS_SPACESHIP_OPERATOR )
bool operator==(AllocationInfo2 const &) const = default;
#else
bool operator==(AllocationInfo2 const & rhs) const VULKAN_HPP_NOEXCEPT {
return allocationInfo == rhs.allocationInfo
&& blockSize == rhs.blockSize
&& dedicatedMemory == rhs.dedicatedMemory
;
}
#endif
#if !defined( VULKAN_HPP_NO_STRUCT_SETTERS )
VULKAN_HPP_CONSTEXPR_14 AllocationInfo2& setAllocationInfo(AllocationInfo allocationInfo_) VULKAN_HPP_NOEXCEPT {
allocationInfo = allocationInfo_;
return *this;
}
VULKAN_HPP_CONSTEXPR_14 AllocationInfo2& setBlockSize(VULKAN_HPP_NAMESPACE::DeviceSize blockSize_) VULKAN_HPP_NOEXCEPT {
blockSize = blockSize_;
return *this;
}
VULKAN_HPP_CONSTEXPR_14 AllocationInfo2& setDedicatedMemory(VULKAN_HPP_NAMESPACE::Bool32 dedicatedMemory_) VULKAN_HPP_NOEXCEPT {
dedicatedMemory = dedicatedMemory_;
return *this;
}
#endif
public:
AllocationInfo allocationInfo = {};
VULKAN_HPP_NAMESPACE::DeviceSize blockSize = {};
VULKAN_HPP_NAMESPACE::Bool32 dedicatedMemory = {};
};
VULKAN_HPP_STATIC_ASSERT(sizeof(AllocationInfo2) == sizeof(VmaAllocationInfo2),
"struct and wrapper have different size!");
VULKAN_HPP_STATIC_ASSERT(std::is_standard_layout<AllocationInfo2>::value,
"struct wrapper is not a standard layout!");
VULKAN_HPP_STATIC_ASSERT(std::is_nothrow_move_constructible<AllocationInfo2>::value,
"AllocationInfo2 is not nothrow_move_constructible!");
struct DefragmentationInfo {
using NativeType = VmaDefragmentationInfo;
@ -1306,11 +1379,15 @@ namespace VMA_HPP_NAMESPACE {
, Pool pool_ = {}
, VULKAN_HPP_NAMESPACE::DeviceSize maxBytesPerPass_ = {}
, uint32_t maxAllocationsPerPass_ = {}
, PFN_vmaCheckDefragmentationBreakFunction pfnBreakCallback_ = {}
, void* pBreakCallbackUserData_ = {}
) VULKAN_HPP_NOEXCEPT
: flags(flags_)
, pool(pool_)
, maxBytesPerPass(maxBytesPerPass_)
, maxAllocationsPerPass(maxAllocationsPerPass_)
, pfnBreakCallback(pfnBreakCallback_)
, pBreakCallbackUserData(pBreakCallbackUserData_)
{}
VULKAN_HPP_CONSTEXPR DefragmentationInfo(DefragmentationInfo const &) VULKAN_HPP_NOEXCEPT = default;
@ -1339,6 +1416,8 @@ namespace VMA_HPP_NAMESPACE {
&& pool == rhs.pool
&& maxBytesPerPass == rhs.maxBytesPerPass
&& maxAllocationsPerPass == rhs.maxAllocationsPerPass
&& pfnBreakCallback == rhs.pfnBreakCallback
&& pBreakCallbackUserData == rhs.pBreakCallbackUserData
;
}
#endif
@ -1364,6 +1443,16 @@ namespace VMA_HPP_NAMESPACE {
maxAllocationsPerPass = maxAllocationsPerPass_;
return *this;
}
VULKAN_HPP_CONSTEXPR_14 DefragmentationInfo& setPfnBreakCallback(PFN_vmaCheckDefragmentationBreakFunction pfnBreakCallback_) VULKAN_HPP_NOEXCEPT {
pfnBreakCallback = pfnBreakCallback_;
return *this;
}
VULKAN_HPP_CONSTEXPR_14 DefragmentationInfo& setPBreakCallbackUserData(void* pBreakCallbackUserData_) VULKAN_HPP_NOEXCEPT {
pBreakCallbackUserData = pBreakCallbackUserData_;
return *this;
}
#endif
public:
@ -1371,6 +1460,8 @@ namespace VMA_HPP_NAMESPACE {
Pool pool = {};
VULKAN_HPP_NAMESPACE::DeviceSize maxBytesPerPass = {};
uint32_t maxAllocationsPerPass = {};
PFN_vmaCheckDefragmentationBreakFunction pfnBreakCallback = {};
void* pBreakCallbackUserData = {};
};
VULKAN_HPP_STATIC_ASSERT(sizeof(DefragmentationInfo) == sizeof(VmaDefragmentationInfo),
"struct and wrapper have different size!");

1
external/cubeb vendored Submodule

@ -0,0 +1 @@
Subproject commit ac8474a5929e9de3bce84f16f8c589240eb9f7c4

View file

@ -1,4 +1,4 @@
// Formatting library for C++ - dynamic format arguments
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
@ -8,11 +8,13 @@
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#ifndef FMT_MODULE
# include <functional> // std::reference_wrapper
# include <memory> // std::unique_ptr
# include <vector>
#endif
#include "core.h"
#include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE
@ -22,20 +24,24 @@ template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
template <typename T>
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// 2022 (v17.10.0).
//
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So node is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> {
T value;
@ -50,7 +56,7 @@ class dynamic_arg_list {
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
@ -61,14 +67,10 @@ class dynamic_arg_list {
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
* A dynamic list of formatting arguments with storage.
*
* 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
@ -110,14 +112,14 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>;
unsigned long long get_types() const {
auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
@ -146,22 +148,20 @@ class dynamic_format_arg_store
constexpr dynamic_format_arg_store() = default;
/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
* Adds an argument into the dynamic store for later passing to a formatting
* function.
*
* Note that custom types and string types (but not string views) are copied
* into the store dynamically allocating memory if necessary.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* store.push_back(42);
* store.push_back("abc");
* store.push_back(1.5f);
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
@ -170,20 +170,18 @@ class dynamic_format_arg_store
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
* Adds a reference to the argument into the dynamic store for later passing
* to a formatting function.
*
* **Example**:
*
* fmt::dynamic_format_arg_store<fmt::format_context> store;
* char band[] = "Rolling Stones";
* store.push_back(std::cref(band));
* band[9] = 'c'; // Changing str affects the output.
* std::string result = fmt::vformat("{}", store);
* // result == "Rolling Scones"
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
@ -192,10 +190,10 @@ class dynamic_format_arg_store
}
/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
* Adds named argument into the dynamic store for later passing to a
* formatting function. `std::reference_wrapper` is supported to avoid
* copying of the argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
@ -208,19 +206,15 @@ class dynamic_format_arg_store
}
}
/** Erase all elements from the store */
/// Erase all elements from the store.
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
/// Reserves space to store at least `new_cap` arguments including
/// `new_cap_named` named arguments.
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");

3077
external/fmt/include/fmt/base.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10,15 +10,8 @@
#include "format.h"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
@ -210,7 +203,7 @@ struct rgb {
uint8_t b;
};
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@ -232,22 +225,21 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace detail
FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
/// A text style consisting of foreground and background colors and emphasis.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@ -256,7 +248,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@ -265,39 +257,29 @@ class text_style {
return *this;
}
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
-> text_style {
return lhs |= rhs;
}
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
const text_style& rhs) {
return and_assign(rhs);
}
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
operator&(text_style lhs, const text_style& rhs) {
return lhs.and_assign(rhs);
}
FMT_CONSTEXPR bool has_foreground() const noexcept {
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const noexcept {
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const noexcept {
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR detail::color_type get_background() const noexcept {
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@ -315,36 +297,11 @@ class text_style {
}
}
// DEPRECATED!
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
-> text_style;
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR_DECL text_style
fg(detail::color_type foreground) noexcept;
friend FMT_CONSTEXPR_DECL text_style
bg(detail::color_type background) noexcept;
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
-> text_style;
detail::color_type foreground_color;
detail::color_type background_color;
@ -353,21 +310,24 @@ class text_style {
emphasis ems;
};
/** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
/// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
/// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style {
return text_style(false, background);
}
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
-> text_style {
return text_style(lhs) | rhs;
}
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@ -429,9 +389,9 @@ template <typename Char> struct ansi_color_escape {
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
return buffer + std::char_traits<Char>::length(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();
}
private:
@ -445,62 +405,45 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
-> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) noexcept {
FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) noexcept {
FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
-> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
int result = std::fputs(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
int result = std::fputws(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg {
template <typename T> struct styled_arg : detail::view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
void vformat_to(
buffer<Char>& buf, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffered_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
@ -521,118 +464,94 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
if (has_style) detail::reset_color<Char>(buf);
}
FMT_END_DETAIL_NAMESPACE
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
if (detail::is_utf8()) {
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
* Formats a string and prints it to the specified file stream using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
template <typename... T>
void print(FILE* f, const text_style& ts, format_string<T...> fmt,
T&&... args) {
vprint(f, ts, fmt, fmt::make_format_args(args...));
}
/**
\rst
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
* Formats a string and prints it to stdout using ANSI escape sequences to
* specify text formatting.
*
* **Example**:
*
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
template <typename... T>
void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
inline auto vformat(const text_style& ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
* Formats arguments and returns the result as a string using ANSI escape
* sequences to specify text formatting.
*
* **Example**:
*
* ```
* #include <fmt/color.h>
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
* "The answer is {}", 42);
* ```
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
template <typename... T>
inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
-> std::string {
return fmt::vformat(ts, fmt, fmt::make_format_args(args...));
}
/// Formats a string with the given text_style and writes the output to `out`.
template <typename OutputIt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
format_args args) -> OutputIt {
auto&& buf = detail::get_buffer<char>(out);
detail::vformat_to(buf, ts, fmt, args);
return detail::get_iterator(buf, out);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
* Formats arguments with the given text style, writes the result to the output
* iterator `out` and returns the iterator past the end of the output range.
*
* **Example**:
*
* std::vector<char> out;
* fmt::format_to(std::back_inserter(out),
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
*/
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
inline auto format_to(OutputIt out, const text_style& ts,
format_string<T...> fmt, T&&... args) -> OutputIt {
return vformat_to(out, ts, fmt, fmt::make_format_args(args...));
}
template <typename T, typename Char>
@ -672,15 +591,14 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
};
/**
\rst
Returns an argument that will be formatted using ANSI escape sequences,
to be used in a formatting function.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds",
fmt::styled(1.23, fmt::fg(fmt::color::green) | fmt::bg(fmt::color::blue)));
\endrst
* Returns an argument that will be formatted using ANSI escape sequences,
* to be used in a formatting function.
*
* **Example**:
*
* fmt::print("Elapsed time: {0:.2f} seconds",
* fmt::styled(1.23, fmt::fg(fmt::color::green) |
* fmt::bg(fmt::color::blue)));
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
@ -688,7 +606,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts)
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COLOR_H_

View file

@ -8,122 +8,46 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
FMT_EXPORT class compiled_string {};
namespace detail {
template <typename T, typename InputIt>
FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it)
-> counting_iterator {
return it + (end - begin);
}
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
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
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
* 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
* `constexpr if` compiler support.
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
@ -135,7 +59,7 @@ struct udl_compiled_string : compiled_string {
#endif
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
auto first(const T& value, const Tail&...) -> const T& {
return value;
}
@ -196,7 +120,8 @@ template <typename Char> struct code_unit {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
*out++ = value;
return out;
}
};
@ -220,7 +145,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
return write<Char>(out, get_arg_checked<T, N>(args...));
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
}
return write<Char>(out, arg);
}
};
@ -308,13 +238,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
@ -331,38 +260,35 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id;
};
constexpr int manual_indexing_id = -1;
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) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int operator()() {
constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
};
template <typename Char> struct parse_arg_id_result {
@ -388,43 +314,48 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
} else if constexpr (c == ':') {
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
} else {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
if constexpr (result.end >= str.size() || str[result.end] != '}') {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
fmt);
}
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(
format_str);
POS + 1, ID, next_id>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -440,60 +371,55 @@ constexpr auto compile_format_string(S format_str) {
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
if constexpr (arg_index >= 0) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
arg_index, next_id>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
return result;
}
}
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
@ -558,35 +484,36 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) {
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
format_str, std::forward<Args>(args)...);
return {it.base(), it.count()};
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... 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)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) {
return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
return fmt::format_to(detail::counting_iterator(), fmt, args...).count();
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) {
void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...);
fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()});
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>;
@ -596,7 +523,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,17 +8,19 @@
#ifndef FMT_OS_H_
#define FMT_OS_H_
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif
#endif // FMT_MODULE
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
@ -46,6 +48,7 @@
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
@ -71,132 +74,78 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
* A reference to a null-terminated string. It can be constructed from a C
* string or `std::string`.
*
* You can use one of the following type aliases for common character types:
*
* +---------------+-----------------------------+
* | Type | Definition |
* +===============+=============================+
* | cstring_view | basic_cstring_view<char> |
* +---------------+-----------------------------+
* | wcstring_view | basic_cstring_view<wchar_t> |
* +---------------+-----------------------------+
*
* This class is most useful as a parameter type for functions that wrap C APIs.
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
/// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
/// Constructs a string reference from an `std::string` object.
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
/// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
namespace detail {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE
}
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args);
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
* Constructs a `std::system_error` object with the description of the form
*
* <message>: <system-message>
*
* where `<message>` is the formatted message and `<system-message>` is the
* system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
* If `error_code` is not a valid error code such as -1, the system message
* will look like "error -1".
*
* **Example**:
*
* // This throws a system_error with the description
* // cannot open file 'madeup': The system cannot find the file
* specified.
* // or similar (system message may vary).
* const char *filename = "madeup";
* LPOFSTRUCT of = LPOFSTRUCT();
* HFILE file = OpenFile(filename, &of, OF_READ);
* if (file == HFILE_ERROR) {
* throw fmt::windows_error(GetLastError(),
* "cannot open file '{}'", filename);
* }
*/
template <typename... Args>
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
@ -207,7 +156,7 @@ std::system_error windows_error(int error_code, string_view message,
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else
inline const std::error_category& system_category() noexcept {
inline auto system_category() noexcept -> const std::error_category& {
return std::system_category();
}
#endif // _WIN32
@ -244,7 +193,7 @@ class buffered_file {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@ -258,24 +207,20 @@ class buffered_file {
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const noexcept { return file_; }
auto get() const noexcept -> FILE* { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
// DEPRECATED! Rename to descriptor to avoid issues with macros.
FMT_API int(fileno)() const;
FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
template <typename... T>
inline void print(string_view fmt, const T&... args) {
const auto& vargs = fmt::make_format_args(args...);
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
: fmt::vprint(file_, fmt, vargs);
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as
@ -289,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public:
// Possible values for the oflag argument to the constructor.
enum {
@ -313,7 +260,7 @@ class FMT_API file {
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -324,24 +271,24 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
int descriptor() const noexcept { return fd_; }
auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
long long size() const;
auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer.
size_t read(void* buffer, size_t count);
auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file.
size_t write(const void* buffer, size_t count);
auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static file dup(int fd);
static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
@ -351,24 +298,35 @@ class FMT_API file {
// necessary.
void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
buffered_file fdopen(const char* mode);
auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
};
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size.
long getpagesize();
auto getpagesize() -> long;
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
struct buffer_size {
buffer_size() = default;
size_t value = 0;
buffer_size operator=(size_t val) const {
auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
@ -400,82 +358,82 @@ struct ostream_params {
# endif
};
FMT_END_DETAIL_NAMESPACE
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
class file_buffer final : public buffer<char> {
private:
file file_;
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
FMT_API static void grow(buffer<char>& buf, size_t);
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other) noexcept;
FMT_API ~file_buffer();
void flush() {
if (size() == 0) return;
file_.write(data(), size());
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
};
/**
Formats ``args`` according to specifications in ``fmt`` and writes the
output to the file.
*/
} // namespace detail
constexpr auto buffer_size = detail::buffer_size();
/// A fast output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
/// Formats `args` according to specifications in `fmt` and writes the
/// output to the file.
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...));
}
};
/**
\rst
Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
* Opens a file for writing. Supported parameters passed in `params`:
*
* - `<integer>`: Flags passed to [open](
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
* - `buffer_size=<integer>`: Output buffer size
*
* **Example**:
*
* auto out = fmt::output_file("guide.txt");
* out.print("Don't {}", "Panic");
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View file

@ -8,84 +8,71 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include <fstream>
#include <ostream>
#ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#include "format.h"
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "chrono.h" // formatbuf
FMT_BEGIN_NAMESPACE
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
// Checks if T has a user-defined operator<<.
template <typename T, typename Char, typename Enable = void>
class is_streamable {
private:
template <typename U>
static auto test(int)
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
<< std::declval<U>()) != 0>;
template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
return nullptr;
}
struct dummy_filebuf {
FILE* _Myfile;
};
template <typename T, typename U = int> struct ms_filebuf {
using type = dummy_filebuf;
};
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
using type = T;
};
using filebuf_type = ms_filebuf<std::filebuf>::type;
FILE* get_file(filebuf_type& buf);
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
struct filebuf_access_tag {};
struct file_access_tag {};
} // namespace
template <typename Tag, typename FileMemberPtr, FileMemberPtr file>
class filebuf_access {
friend FILE* get_file(filebuf_type& buf) { return buf.*file; }
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
};
template class filebuf_access<filebuf_access_tag,
decltype(&filebuf_type::_Myfile),
&filebuf_type::_Myfile>;
inline bool write(std::filebuf& buf, fmt::string_view data) {
print(get_file(buf), data);
return true;
#if FMT_MSC_VERSION
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
#else
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
@ -93,10 +80,6 @@ inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
if (const_check(FMT_MSC_VER)) {
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
}
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
@ -110,26 +93,31 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
template <typename T, typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
void set_debug_format() = delete;
template <typename T, typename Context>
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
@ -137,55 +125,85 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
using ostream_formatter = basic_ostream_formatter<char>;
namespace detail {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> {
using basic_ostream_formatter<Char>::format;
// DEPRECATED!
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) const
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
template <typename Context>
auto format(detail::streamed_view<T> view, Context& ctx) const
-> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx);
}
};
} // namespace detail
FMT_MODULE_EXPORT
template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>();
/**
* Returns a view that formats `value` via an ostream `operator<<`.
*
* **Example**:
*
* fmt::print("Current thread id: {}\n",
* fmt::streamed(std::this_thread::get_id()));
*/
template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}
namespace detail {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
} // namespace detail
**Example**::
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_MODULE_EXPORT
template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args(args...));
FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str,
typename detail::vformat_args<Char>::type args) {
auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args);
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
detail::write_buffer(os, buffer);
}
FMT_MODULE_EXPORT
/**
* Prints formatted data to the stream `os`.
*
* **Example**:
*
* fmt::print(cerr, "Don't {}!", "panic");
*/
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...);
if (detail::use_utf8())
vprint(os, fmt, vargs);
else
detail::vprint_directly(os, fmt, vargs);
}
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
FMT_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE

View file

@ -8,108 +8,103 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include <ostream>
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h"
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename OutputIt, typename Char> class basic_printf_context {
template <typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
/// Constructs a `printf_context` object. References to the arguments are
/// stored in the context object so make sure they have appropriate lifetimes.
basic_printf_context(basic_appender<Char> out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
detail::locale_ref locale() { return {}; }
auto locale() -> detail::locale_ref { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <typename T> static bool fits_in_int(T value) {
unsigned max = max_value<int>();
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
}
static bool fits_in_int(bool) { return true; }
static auto fits_in_int(bool) -> bool { return true; }
};
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
template <typename T> static auto fits_in_int(T value) -> bool {
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
static auto fits_in_int(int) -> bool { return true; }
};
class printf_precision_handler {
public:
struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
auto operator()(T) -> int {
report_error("precision is not integer");
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int {
public:
struct is_zero_int {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
bool operator()(T value) {
auto operator()(T value) -> bool {
return value == 0;
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
bool operator()(T) {
auto operator()(T) -> bool {
return false;
}
};
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:
@ -133,22 +128,23 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
auto n = static_cast<int>(static_cast<target_type>(value));
arg_ = detail::make_arg<Context>(n);
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
arg_ = detail::make_arg<Context>(n);
}
} else {
if (is_signed) {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
auto n = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n);
} else {
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
arg_ = detail::make_arg<Context>(n);
}
}
}
@ -163,7 +159,7 @@ template <typename T, typename Context> class arg_converter {
// unsigned).
template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
arg.visit(arg_converter<T, Context>(arg, type));
}
// Converts an integer argument to char for printf.
@ -176,8 +172,8 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
auto c = static_cast<typename Context::char_type>(value);
arg_ = detail::make_arg<Context>(c);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -187,122 +183,126 @@ template <typename Context> class char_converter {
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
auto operator()(const Char* s) -> const Char* { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
class printf_width_handler {
private:
using format_specs = basic_format_specs<Char>;
format_specs& specs_;
public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
auto operator()(T) -> unsigned {
report_error("width is not integer");
return 0;
}
};
// The ``printf`` argument formatter.
template <typename OutputIt, typename Char>
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The `printf` argument formatter.
template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> {
private:
using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
using context_type = basic_printf_context<Char>;
context_type& context_;
OutputIt write_null_pointer(bool is_string = false) {
void write_null_pointer(bool is_string = false) {
auto s = this->specs;
s.type = presentation_type::none;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); }
void operator()(monostate value) { base::operator()(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
OutputIt operator()(T value) {
void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead.
if (std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
if (!std::is_same<T, Char>::value) {
base::operator()(value);
return;
}
return base::operator()(value);
format_specs s = this->specs;
if (s.type != presentation_type::none && s.type != presentation_type::chr) {
return (*this)(static_cast<int>(value));
}
s.sign = sign::none;
s.alt = false;
s.fill = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (s.align == align::none || s.align == align::numeric)
s.align = align::right;
write<Char>(this->out, static_cast<Char>(value), s);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
OutputIt operator()(T value) {
return base::operator()(value);
void operator()(T value) {
base::operator()(value);
}
/** Formats a null-terminated C string. */
OutputIt operator()(const char* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != presentation_type::pointer);
void operator()(const char* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
/** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != presentation_type::pointer);
void operator()(const wchar_t* value) {
if (value)
base::operator()(value);
else
write_null_pointer(this->specs.type != presentation_type::pointer);
}
OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value);
void operator()(basic_string_view<Char> value) { base::operator()(value); }
void operator()(const void* value) {
if (value)
base::operator()(value);
else
write_null_pointer();
}
/** Formats a pointer. */
OutputIt operator()(const void* value) {
return value ? base::operator()(value) : write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx =
basic_printf_parse_context<Char>(basic_string_view<Char>());
void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({});
handle.format(parse_ctx, context_);
return this->out;
}
};
template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
const Char* end) {
void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) {
switch (*it) {
case '-':
@ -312,12 +312,10 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
specs.fill = '0';
break;
case ' ':
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
if (specs.sign != sign::plus) specs.sign = sign::space;
break;
case '#':
specs.alt = true;
@ -329,8 +327,8 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
}
template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end,
basic_format_specs<Char>& specs, GetArg get_arg) {
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
Char c = *it;
if (c >= '0' && c <= '9') {
@ -341,11 +339,11 @@ int parse_header(const Char*& it, const Char* end,
++it;
arg_index = value != -1 ? value : max_value<int>();
} else {
if (c == '0') specs.fill[0] = '0';
if (c == '0') specs.fill = '0';
if (value != 0) {
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big"));
if (value == -1) report_error("number is too big");
specs.width = value;
return arg_index;
}
@ -356,23 +354,68 @@ int parse_header(const Char*& it, const Char* end,
if (it != end) {
if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<Char>(specs), get_arg(-1)));
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
return arg_index;
}
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'X':
upper = true;
FMT_FALLTHROUGH;
case 'x':
return in(t, integral_set) ? pt::hex : pt::none;
case 'E':
upper = true;
FMT_FALLTHROUGH;
case 'e':
return in(t, float_set) ? pt::exp : pt::none;
case 'F':
upper = true;
FMT_FALLTHROUGH;
case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
case 'G':
upper = true;
FMT_FALLTHROUGH;
case 'g':
return in(t, float_set) ? pt::general : pt::none;
case 'A':
upper = true;
FMT_FALLTHROUGH;
case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
}
}
template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
using OutputIt = buffer_appender<Char>;
auto out = OutputIt(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format);
using iterator = basic_appender<Char>;
auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
@ -388,26 +431,24 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
const Char* end = parse_ctx.end();
auto it = start;
while (it != end) {
if (!detail::find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
if (!find<false, Char>(it, end, '%', it)) {
it = end; // find leaves it == nullptr if it doesn't find '%'.
break;
}
Char c = *it++;
if (it != end && *it == c) {
out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
start = ++it;
continue;
}
out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs;
auto specs = format_specs();
specs.align = align::right;
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) parse_ctx.on_error("argument not found");
if (arg_index == 0) report_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
@ -417,8 +458,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else {
specs.precision = 0;
}
@ -427,32 +468,30 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
if (specs.precision >= 0 && arg.is_integral()) {
// Ignore '0' for non-numeric types or if '-' present.
specs.fill = ' ';
}
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv);
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
if (specs.fill.template get<Char>() == '0') {
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@ -491,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.
@ -501,157 +540,117 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
type = 'd';
break;
case 'c':
visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
arg.visit(char_converter<basic_printf_context<Char>>(arg));
break;
}
}
specs.type = parse_presentation_type(type);
bool upper = false;
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier");
report_error("invalid format specifier");
specs.upper = upper;
start = it;
// Format argument.
out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
arg.visit(printf_arg_formatter<Char>(out, specs, context));
}
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
}
FMT_END_DETAIL_NAMESPACE
} // namespace detail
template <typename Char>
using basic_printf_context_t =
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
using printf_context = basic_printf_context<char>;
using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>;
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
/// Constructs an `format_arg_store` object that contains references to
/// arguments and can be implicitly converted to `printf_args`.
template <typename Char = char, typename... T>
inline auto make_printf_args(T&... args)
-> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
return fmt::make_format_args<basic_printf_context<Char>>(args...);
}
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template <typename... T>
inline auto make_wprintf_args(const T&... args)
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename Char> struct vprintf_args {
using type = basic_format_args<basic_printf_context<Char>>;
};
template <typename S, typename Char = char_t<S>>
inline auto vsprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
template <typename Char>
inline auto vsprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer);
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
return to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
*
* **Example**:
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vfprintf(
std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
template <typename Char>
inline 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();
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
* Formats `args` according to specifications in `fmt` and writes the output
* to `f`.
*
* **Example**:
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(fmt),
fmt::make_format_args<context>(args...));
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
}
template <typename S, typename Char = char_t<S>>
inline auto vprintf(
const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, to_string_view(fmt), args);
return vfprintf(stdout, fmt, args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
* Formats `args` according to specifications in `fmt` and writes the output
* to `stdout`.
*
* **Example**:
*
* fmt::printf("Elapsed time: %.2f seconds", 1.23);
*/
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
inline auto printf(const S& fmt, const T&... args) -> int {
return vprintf(
to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
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...));
}
template <typename S, typename Char = char_t<S>>
FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args);
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size());
}
template <typename S, typename... T, typename Char = char_t<S>>
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
const T&... args) -> int {
return vfprintf(os, to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

File diff suppressed because it is too large Load diff

699
external/fmt/include/fmt/std.h vendored Normal file
View file

@ -0,0 +1,699 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_STD_H_
#define FMT_STD_H_
#include "format.h"
#include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# 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
#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
#endif
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx);
if (it != end && *it == '?') {
debug_ = true;
++it;
}
if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
auto path_string =
!path_type_ ? p.native()
: p.generic_string<std::filesystem::path::value_type>();
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
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
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// 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) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
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
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<is_formattable<T, Char>::value &&
is_formattable<E, Char>::value>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write(out, loc.file_name());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.line());
out = detail::write(out, ':');
out = detail::write<char>(out, loc.column());
out = detail::write(out, ": ");
out = detail::write(out, loc.function_name());
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;
};
FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
};
FMT_EXPORT
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>>>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "variant(");
FMT_TRY {
std::visit(
[&](const auto& v) {
out = detail::write_escaped_alternative<Char>(out, v);
},
value);
}
FMT_CATCH(const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes<Char>(out, ec.category().name(), format_specs());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#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
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
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);
}
};
#endif
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
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.
FMT_EXPORT
template <typename BitRef, typename Char>
struct formatter<BitRef, Char,
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
: formatter<bool, Char> {
template <typename FormatContext>
FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v, ctx);
}
};
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();
}
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
: formatter<T, Char> {
template <typename FormatContext>
auto format(const std::atomic<T>& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<T, Char>::format(v.load(), ctx);
}
};
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.sign = sign::plus;
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.width_ref.kind != detail::arg_id_kind::none ||
specs.precision_ref.kind != detail::arg_id_kind::none) {
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.fill = specs.fill;
outer_specs.align = specs.align;
specs.width = 0;
specs.fill = {};
specs.align = align::none;
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View file

@ -8,52 +8,92 @@
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include <cwchar>
#include <tuple>
#include "color.h"
#include "format.h"
#include "ranges.h"
#ifndef FMT_MODULE
# include <cwchar>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
# endif
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
template <typename S, typename = void> struct format_string_char {};
template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
#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...)) {
return fmt::make_format_args<wformat_context>(args...);
}
inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
constexpr auto operator""_a(const wchar_t* s, size_t)
-> detail::udl_arg<wchar_t> {
return {s};
}
#endif
@ -78,136 +118,150 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep);
}
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args);
return to_string(buf);
}
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
return vformat(to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename Char = char_t<S>,
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& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
inline auto vformat(const Locale& loc, const S& format_str,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args);
return detail::vformat(loc, detail::to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
template <typename Locale, 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& format_str, Args&&... args)
inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
return detail::vformat(
loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
typename detail::vformat_args<Char>::type args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, to_string_view(format_str), args);
return detail::get_iterator(buf);
detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf, out);
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
return vformat_to(out, to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
detail::vformat_to(buf, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...), {});
return detail::buffer_appender<Char>(buf);
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
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& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
inline auto vformat_to(OutputIt out, const Locale& loc, const S& format_str,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc));
return detail::get_iterator(buf, out);
}
template <
typename OutputIt, typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
template <typename OutputIt, typename Locale, 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& format_str,
Args&&... args) ->
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
}
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> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
inline auto vformat_to_n(OutputIt out, size_t n,
basic_string_view<Char> format_str,
typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename S, typename... Args, typename Char = char_t<S>,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf;
detail::vformat_to(buf, to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...));
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
auto buf = wmemory_buffer();
detail::vformat_to(buf, fmt, args);
buf.push_back(L'\0');
if (std::fputws(buf.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
@ -224,13 +278,45 @@ template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
-> std::wstring {
auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const 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(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_MODULE_EXPORT_END
FMT_END_EXPORT
FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_

View file

@ -10,96 +10,34 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// DEPRECATED!
template <typename T = void> struct basic_data {
FMT_API static constexpr const char digits[100][2] = {
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
0};
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
0};
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
0x1000000u | ' '};
};
#ifdef FMT_SHARED
// Required for -flto, -fivisibility=hidden and -shared to work
extern template struct basic_data<void>;
#endif
#if __cplusplus < 201703L
// DEPRECATED! These are here only for ABI compatiblity.
template <typename T> constexpr const char basic_data<T>::digits[][2];
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::signs[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(
float x) noexcept;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(
double x) noexcept;
} // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
template FMT_API auto dragonbox::to_decimal(float x) noexcept
-> dragonbox::decimal_fp<float>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
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 detail::thousands_sep_impl(locale_ref)
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
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 detail::thousands_sep_impl(locale_ref)
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template struct detail::basic_data<void>;
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail
FMT_END_NAMESPACE

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