Compare commits

..
60 Commits
Author SHA1 Message Date
JordanTheToasterandTy 50baaf39d6 Deps: Update Mac OS ffmpeg to 8.0 2025-10-15 08:32:19 -04:00
lightningterror 4743ccac8c GS/HW/Shaders: Fix afail shader typo.
No dual source blend means PS_NO_COLOR1 is true, no output on target 1.
2025-10-15 00:57:33 +02:00
lightningterror 0dc3fc6228 GS/HW: Unify blend levels when barriers are and are not supported.
When barriers/multidraw fb copy is supported no change.
When barriers/multidraw fb copy isn't supported keep the same levels but only enable them if there's no overlap.
2025-10-14 02:19:43 +02:00
lightningterror a7a4583c84 GS/HW: Enable any fbmask emulation if Blending is above minimum.
Since dx11/12 now supports sw blending and copies/draw area/quads optimizations have been made perf impact will be lower so we can use the same unified path if texture barriers are disabled.
2025-10-14 02:19:43 +02:00
lightningterror 6b52937262 GS/TC: Make sure target is created before setting any flags.
Xenosaga 3 fails to create a target because the rect is empty so we should avoid setting any flags since it's null.
2025-10-14 02:19:08 +02:00
PCSX2 Botandlightningterror df9caf6fb8 [ci skip] Qt: Update Base Translation. 2025-10-14 02:17:24 +02:00
JordanandGitHub be5f8d2e60 GameDB: Various fixes for issues in Xenosaga 3 HyperSonic Xtreme Jeopardy and Jak 3. 2025-10-13 21:56:30 +02:00
SeanandGitHub b0e01ca518 Debugger: Floating point display in memory view (#13362) 2025-10-13 12:51:37 -04:00
TheTechnician27andTy 4e85272393 Settings: Remove obsolete transitionary function 2025-10-13 12:47:20 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bffed9a839 [ci skip] CI: Bump softprops/action-gh-release from 2.3.4 to 2.4.1 (#13383)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 08:49:29 -04:00
PCSX2 Botandlightningterror 7ed0c0d031 [ci skip] Qt: Update Base Translation. 2025-10-13 10:38:02 +02:00
TheTechnician27andTellowKrinkle ae6e4c98f6 AudioSettingsWidget: Disambiguate overloaded volume name 2025-10-12 17:17:59 -05:00
TheTechnician27andTellowKrinkle 38cfa9912d SPU2: Fix interface for output muting 2025-10-12 17:17:59 -05:00
JordanTheToasterandTellowKrinkle b02318e2b6 Misc: Qt 6.10.0 Build Fixes 2025-10-12 00:39:34 -05:00
JordanTheToasterandTellowKrinkle db45263221 Deps: Update Windows and Linux to Qt 6.10.0 2025-10-12 00:39:34 -05:00
JordanTheToasterandlightningterror 3fd9625f1c 3rdparty: Update rcheevos v12.1.0 2025-10-11 20:17:13 +02:00
JordanTheToasterandlightningterror 2978b6050d GameDB: Black adjust HPO 2025-10-11 20:05:44 +02:00
PCSX2 Botandlightningterror 274acddcf1 [ci skip] Qt: Update Base Translation. 2025-10-11 20:04:52 +02:00
JordanTheToasterandTellowKrinkle 8dffc85707 Misc: Qt 6.10.0 Build Fixes 2025-10-10 23:20:48 -05:00
lightningterror f799631a70 Qt: Update dx12 info.
Allow texture barrier override, update blending tooltip.
2025-10-10 08:10:31 +02:00
lightningterror d744f0dfeb GS/DX12: Backport multidraw fb copy from dx11 to dx12. 2025-10-10 08:10:31 +02:00
TyandGitHub bdb8de6d3b translations: Syncing Crowdin translations (#13372) 2025-10-09 16:37:44 -04:00
TJnotJTandlightningterror e751f367ca GS/SW: Rewrite vertex ST values if they might overflow the rasterizer fixed point format. 2025-10-09 19:42:27 +02:00
TJnotJTandlightningterror 13f2d87ba8 GS: Add ST NaN tracking to the vertex trace.
Add fields that will trace if any of the S/Q or T/Q values is NaN. Ignore any NaN ST values while calculating min/max.
2025-10-09 19:42:27 +02:00
TheLastRarandGovanifY 3cac2cf7c2 Qt: Add null check for gamelist icons 2025-10-09 16:57:55 +02:00
TheLastRarandlightningterror a67409bfc6 Deps: Patch SDL3 to fix erroneous uninitialise 2025-10-09 12:53:40 +02:00
lightningterror 6a556c9968 GS/HW: Fix manual de swizzle -Wsign-compare warnings. 2025-10-09 03:28:00 +02:00
TJnotJTandlightningterror 4d93285ca2 GS: Use triangle quad detection in primitive overlap detection.
Coauthored-by: TellowKrinkle
2025-10-09 03:26:08 +02:00
Ty 98c35a308d ci: Set NPROCS in the linux gs runner dependency script 2025-10-08 16:24:15 -04:00
Ty Lamontagne 7da97e6d80 cmake/ci: Allow gs runner builds without building all of pcsx2-qt 2025-10-08 15:26:34 -04:00
Ty Lamontagne edf686752a GSRunner: Support GNU/Linux 2025-10-08 15:26:34 -04:00
lightningterror b7e17646a3 GS/DX12: Backport dx11 full rt copy optimizations.
We are copying the whole RT so just call CopyResource instead of CopyTextureRegion which will be faster.
2025-10-08 02:24:32 +02:00
JordanTheToasterandlightningterror 3f437e7496 3rdparty: Update fmt to 12.0.0 2025-10-07 15:12:49 +02:00
lightningterror 7ab6c62dee GS/HW: Update manual deswizzle detection.
Check if a draw is either a palette or 32bit depth format, also check for swizzle format is 16 or 32bit.

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

Clean up SendHWDraw across renderers.

Recycle tex for rt copy if colclip tex fails.

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

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

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

When API does not provide barriers get bbox of batched draws.
2025-09-30 05:53:59 +02:00
PCSX2 Botandlightningterror 2177de2238 [ci skip] Qt: Update Base Translation. 2025-09-30 02:19:21 +02:00
Ty 32973c746e CI: Use pull_request_target for deps dispatch 2025-09-29 20:05:27 -04:00
146 changed files with 262471 additions and 201867 deletions
+2 -2
View File
@@ -68,7 +68,7 @@ jobs:
mv ./release-notes.md ${GITHUB_WORKSPACE}/release-notes.md
- name: Create a GitHub Release (Manual)
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
if: steps.tag_version.outputs.new_tag && github.event_name == 'workflow_dispatch'
with:
body_path: ./release-notes.md
@@ -77,7 +77,7 @@ jobs:
tag_name: ${{ steps.tag_version.outputs.new_tag }}
- name: Create a GitHub Release (Push)
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
if: steps.tag_version.outputs.new_tag && github.event_name != 'workflow_dispatch'
with:
body_path: ./release-notes.md
+1 -17
View File
@@ -50,12 +50,6 @@ declare -a MANUAL_LIBS=(
"libfreetype.so.6"
)
declare -a REMOVE_LIBS=(
'libwayland-client.so*'
'libwayland-cursor.so*'
'libwayland-egl.so*'
)
set -e
LINUXDEPLOY=./linuxdeploy-x86_64.AppImage
@@ -130,7 +124,7 @@ echo "Running linuxdeploy to create AppDir..."
# Interestingly, specifying the module doesn't copy the module, only the required plugins for it
# https://github.com/linuxdeploy/linuxdeploy-plugin-qt/issues/160#issuecomment-2655543893
EXTRA_QT_MODULES="core;gui;svg;waylandclient;waylandcompositor;widgets;xcbqpa" \
EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" \
EXTRA_PLATFORM_PLUGINS="libqwayland.so" \
DEPLOY_PLATFORM_THEMES="1" \
QMAKE="$DEPSDIR/bin/qmake" \
NO_STRIP="1" \
@@ -140,16 +134,6 @@ $LINUXDEPLOY --plugin qt --appdir="$OUTDIR" --executable="$BUILDDIR/bin/pcsx2-qt
echo "Copying resources into AppDir..."
cp -a "$BUILDDIR/bin/resources" "$OUTDIR/usr/bin"
# Why do we have to manually remove these libs? Because the linuxdeploy Qt plugin
# copies them, not the "main" linuxdeploy binary, and plugins don't inherit the
# include list...
for lib in "${REMOVE_LIBS[@]}"; do
for libpath in $(find "$OUTDIR/usr/lib" -name "$lib"); do
echo " Removing problematic library ${libpath}."
rm -f "$libpath"
done
done
# Restore unstripped deps (for cache).
rm -fr "$DEPSDIR"
mv "$DEPSDIR.bak" "$DEPSDIR"
@@ -20,8 +20,8 @@ LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.2
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.22
QT=6.9.2
SDL=SDL3-3.2.24
QT=6.10.0
QTAPNG=1.3.0
LZ4=1.10.0
ZSTD=1.5.7
@@ -44,16 +44,16 @@ fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
81cc0fc17e5bf2c1754eeca9af9c47a76789ac5efdd165b3b91cbbe4b90bfb76 $SDL.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
44be9c9ecfe04129c4dea0a7e1b36ad476c9cc07c292016ac98e7b41514f2440 qtbase-everywhere-src-$QT.tar.xz
8a023f7e2f57dedc02e2ab10c975f7cb3cccac9b8f0823c12fd6824834549139 qtimageformats-everywhere-src-$QT.tar.xz
d984cab8f26334aa1c15e5b8f0cd9f1b7c0c1289fe0b68c1c84ab469b75605a5 qtsvg-everywhere-src-$QT.tar.xz
d8b7f7e8e970cc0b975205fd6d5832ea917ef3e751df69b97439c1cddd67a489 qttools-everywhere-src-$QT.tar.xz
c73bb6281ed365c0f954f4b1b6e1b13e1b3fefd94854f46fcd9a412f641f7ed6 qttranslations-everywhere-src-$QT.tar.xz
cad79806565568f12f9983fed69219416abcee9d5deef4abdfcf94aa2eef7781 qtwayland-everywhere-src-$QT.tar.xz
ead4623bcb54a32257c5b3e3a5aec6d16ec96f4cda58d2e003f5a0c16f72046d qtbase-everywhere-src-$QT.tar.xz
64450a52507c540de53616ed5e516df0e0905a99d3035ddfaa690f2b3f7c0cea qtimageformats-everywhere-src-$QT.tar.xz
5ed2c0e04d5e73ff75c2a2ed92db5dc1788ba70f704fc2b71bc21644beda2533 qtsvg-everywhere-src-$QT.tar.xz
d86d5098cf3e3e599f37e18df477e65908fc8f036e10ea731b3469ec4fdbd02a qttools-everywhere-src-$QT.tar.xz
326e8253cfd0cb5745238117f297da80e30ce8f4c1db81990497bd388b026cde qttranslations-everywhere-src-$QT.tar.xz
603f2b0a259b24bd0fb14f880d7761b1d248118a42a6870cdbe8fdda4173761f qtwayland-everywhere-src-$QT.tar.xz
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
@@ -0,0 +1,196 @@
#!/usr/bin/env bash
set -e
if [ "$#" -ne 1 ]; then
echo "Syntax: $0 <output directory>"
exit 1
fi
SCRIPTDIR=$(realpath $(dirname "${BASH_SOURCE[0]}"))
NPROCS="$(getconf _NPROCESSORS_ONLN)"
INSTALLDIR="$1"
if [ "${INSTALLDIR:0:1}" != "/" ]; then
INSTALLDIR="$PWD/$INSTALLDIR"
fi
FREETYPE=2.14.1
HARFBUZZ=12.0.0
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBPNG=1.6.50
LIBWEBP=1.6.0
SDL=SDL3-3.2.22
LZ4=1.10.0
ZSTD=1.5.7
PLUTOVG=1.3.1
PLUTOSVG=0.0.7
SHADERC=2025.3
SHADERC_GLSLANG=efd24d75bcbc55620e759f6bf42c45a32abac5f8
SHADERC_SPIRVHEADERS=2a611a970fdbc41ac2e3e328802aed9985352dca
SHADERC_SPIRVTOOLS=33e02568181e3312f49a3cf33df470bf96ef293a
mkdir -p deps-build
cd deps-build
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.zip
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
a8e4a25e5c2686fd36981e527ed05e451fcfc226bddf350f4e76181371190937 shaderc-$SHADERC.tar.gz
9427deccbdf4bde6a269938df38c6bd75247493786a310d8d733a2c82065ef47 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
c2225a49c3d7efa5c4f4ce4a6b42081e6ea3daca376f3353d9d7c2722d77a28a shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
44d1005880c583fc00a0fb41c839214c68214b000ea8dcb54d352732fee600ff shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
bea672eb96ee36c2cbeb911b9bac66dfe989b3ad9a9943101e00aeb2df2aefdb plutovg-$PLUTOVG.tar.gz
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
EOF
curl -L \
-o "freetype-$FREETYPE.tar.xz" "https://sourceforge.net/projects/freetype/files/freetype2/$FREETYPE/freetype-$FREETYPE.tar.xz/download" \
-o "harfbuzz-$HARFBUZZ.tar.gz" "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/$HARFBUZZ.tar.gz" \
-O "https://github.com/ianlancetaylor/libbacktrace/archive/$LIBBACKTRACE.zip" \
-O "https://downloads.sourceforge.net/project/libpng/libpng16/$LIBPNG/libpng-$LIBPNG.tar.xz" \
-O "https://download.sourceforge.net/libpng-apng/libpng-$LIBPNG-apng.patch.gz" \
-O "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-$LIBWEBP.tar.gz" \
-O "https://github.com/lz4/lz4/releases/download/v$LZ4/lz4-$LZ4.tar.gz" \
-O "https://libsdl.org/release/$SDL.tar.gz" \
-O "https://github.com/facebook/zstd/releases/download/v$ZSTD/zstd-$ZSTD.tar.gz" \
-o "shaderc-$SHADERC.tar.gz" "https://github.com/google/shaderc/archive/refs/tags/v$SHADERC.tar.gz" \
-o "shaderc-glslang-$SHADERC_GLSLANG.tar.gz" "https://github.com/KhronosGroup/glslang/archive/$SHADERC_GLSLANG.tar.gz" \
-o "shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Headers/archive/$SHADERC_SPIRVHEADERS.tar.gz" \
-o "shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz" "https://github.com/KhronosGroup/SPIRV-Tools/archive/$SHADERC_SPIRVTOOLS.tar.gz" \
-o "plutovg-$PLUTOVG.tar.gz" "https://github.com/sammycage/plutovg/archive/v$PLUTOVG.tar.gz" \
-o "plutosvg-$PLUTOSVG.tar.gz" "https://github.com/sammycage/plutosvg/archive/v$PLUTOSVG.tar.gz"
shasum -a 256 --check SHASUMS
echo "Building libbacktrace..."
rm -fr "libbacktrace-$LIBBACKTRACE"
unzip "$LIBBACKTRACE.zip"
cd "libbacktrace-$LIBBACKTRACE"
./configure --prefix="$INSTALLDIR"
make
make install
cd ..
echo "Building libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building LZ4..."
rm -fr "lz4-$LZ4"
tar xf "lz4-$LZ4.tar.gz"
cd "lz4-$LZ4"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DLZ4_BUILD_CLI=OFF -DLZ4_BUILD_LEGACY_LZ4C=OFF -B build-dir -G Ninja build/cmake
cmake --build build-dir --parallel
ninja -C build-dir install
cd ..
echo "Building Zstandard..."
rm -fr "zstd-$ZSTD"
tar xf "zstd-$ZSTD.tar.gz"
cd "zstd-$ZSTD"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building FreeType without HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_DISABLE_HARFBUZZ=TRUE -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building HarfBuzz..."
rm -fr "harfbuzz-$HARFBUZZ"
tar xf "harfbuzz-$HARFBUZZ.tar.gz"
cd "harfbuzz-$HARFBUZZ"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -DHB_HAVE_FREETYPE=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building WebP..."
rm -fr "libwebp-$LIBWEBP"
tar xf "libwebp-$LIBWEBP.tar.gz"
cd "libwebp-$LIBWEBP"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -B build -G Ninja \
-DWEBP_BUILD_ANIM_UTILS=OFF -DWEBP_BUILD_CWEBP=OFF -DWEBP_BUILD_DWEBP=OFF -DWEBP_BUILD_GIF2WEBP=OFF -DWEBP_BUILD_IMG2WEBP=OFF \
-DWEBP_BUILD_VWEBP=OFF -DWEBP_BUILD_WEBPINFO=OFF -DWEBP_BUILD_WEBPMUX=OFF -DWEBP_BUILD_EXTRAS=OFF -DBUILD_SHARED_LIBS=ON
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building FreeType with HarfBuzz..."
rm -fr "freetype-$FREETYPE"
tar xf "freetype-$FREETYPE.tar.xz"
cd "freetype-$FREETYPE"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DFT_REQUIRE_ZLIB=ON -DFT_REQUIRE_PNG=ON -DFT_DISABLE_BZIP2=TRUE -DFT_DISABLE_BROTLI=TRUE -DFT_REQUIRE_HARFBUZZ=TRUE -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building SDL..."
rm -fr "$SDL"
tar xf "$SDL.tar.gz"
cd "$SDL"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building PlutoVG..."
rm -fr "plutovg-$PLUTOVG"
tar xf "plutovg-$PLUTOVG.tar.gz"
cd "plutovg-$PLUTOVG"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DPLUTOVG_BUILD_EXAMPLES=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building PlutoSVG..."
rm -fr "plutosvg-$PLUTOSVG"
tar xf "plutosvg-$PLUTOSVG.tar.gz"
cd "plutosvg-$PLUTOSVG"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DPLUTOSVG_ENABLE_FREETYPE=ON -DPLUTOSVG_BUILD_EXAMPLES=OFF -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Building shaderc..."
rm -fr "shaderc-$SHADERC"
tar xf "shaderc-$SHADERC.tar.gz"
cd "shaderc-$SHADERC"
cd third_party
tar xf "../../shaderc-glslang-$SHADERC_GLSLANG.tar.gz"
mv "glslang-$SHADERC_GLSLANG" "glslang"
tar xf "../../shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz"
mv "SPIRV-Headers-$SHADERC_SPIRVHEADERS" "spirv-headers"
tar xf "../../shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz"
mv "SPIRV-Tools-$SHADERC_SPIRVTOOLS" "spirv-tools"
cd ..
patch -p1 < "$SCRIPTDIR/../common/shaderc-changes.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DSHADERC_SKIP_TESTS=ON -DSHADERC_SKIP_EXAMPLES=ON -DSHADERC_SKIP_COPYRIGHT_CHECK=ON -B build -G Ninja
cmake --build build --parallel
ninja -C build install
cd ..
echo "Cleaning up..."
cd ..
rm -r deps-build
@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.2.22.tar.gz",
"sha256": "f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc"
"url": "https://libsdl.org/release/SDL3-3.2.24.tar.gz",
"sha256": "81cc0fc17e5bf2c1754eeca9af9c47a76789ac5efdd165b3b91cbbe4b90bfb76"
}
],
"cleanup": [
@@ -40,13 +40,13 @@ fi
FREETYPE=2.14.1
HARFBUZZ=12.0.0
SDL=SDL3-3.2.22
SDL=SDL3-3.2.24
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.2
LIBWEBP=1.6.0
FFMPEG=6.0
FFMPEG=8.0
MOLTENVK=1.2.9
QT=6.7.3
QTAPNG=1.3.0
@@ -80,14 +80,14 @@ CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
81cc0fc17e5bf2c1754eeca9af9c47a76789ac5efdd165b3b91cbbe4b90bfb76 $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
9fd58144081654c3373768dd96ead294023830927b14fe3d3c1ef641fb324753 qtimageformats-everywhere-src-$QT.tar.xz
@@ -22,13 +22,13 @@ fi
FREETYPE=2.14.1
HARFBUZZ=12.0.0
SDL=SDL3-3.2.22
SDL=SDL3-3.2.24
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.50
LIBJPEGTURBO=3.1.2
LIBWEBP=1.6.0
FFMPEG=6.0
FFMPEG=8.0
MOLTENVK=1.2.9
QT=6.7.3
QTAPNG=1.3.0
@@ -60,14 +60,14 @@ CMAKE_COMMON=(
cat > SHASUMS <<EOF
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
c4a398539c3e0fdc9a82dfe7824d0438cae78c1e2124e7c6ada3dfa600cdb6c8 harfbuzz-$HARFBUZZ.tar.gz
f29d00cbcee273c0a54f3f32f86bf5c595e8823a96b1d92a145aac40571ebfcc $SDL.tar.gz
81cc0fc17e5bf2c1754eeca9af9c47a76789ac5efdd165b3b91cbbe4b90bfb76 $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 libpng-$LIBPNG-apng.patch.gz
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
57be87c22d9b49c112b6d24bc67d42508660e6b718b3db89c44e47e289137082 ffmpeg-$FFMPEG.tar.xz
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
f415a09385030c6510a936155ce211f617c31506db5fbc563e804345f1ecf56e v$MOLTENVK.tar.gz
8ccbb9ab055205ac76632c9eeddd1ed6fc66936fc56afc2ed0fd5d9e23da3097 qtbase-everywhere-src-$QT.tar.xz
9fd58144081654c3373768dd96ead294023830927b14fe3d3c1ef641fb324753 qtimageformats-everywhere-src-$QT.tar.xz
@@ -46,10 +46,10 @@ set FREETYPE=2.14.1
set HARFBUZZ=12.0.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.22
set QT=6.9.2
set LIBPNGLONG=1.6.50
set QTMINOR=6.9
set SDL=SDL3-3.2.24
set QT=6.10.0
set QTMINOR=6.10
set QTAPNG=1.3.0
set LZ4=1.10.0
set WEBP=1.6.0
@@ -71,12 +71,13 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d60068b1e5c83c66bb14c325dfef46f8fcc380735b4591de6f5e7b9738929d1 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 97d59c78e40b4ddd018738d285a12afc320b57f8265a3f760353739a3619ccdb || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f2fc6ff382c6f3af79493d0709dbd64847d0356313518f094f9096315f2fdb30 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" af80bb671ea0f66c0036ce7041a56b0e550fc94fb88d2c77b5b6a3e33e42139b || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" d2f4c7a4a12630e879702353f944f96a5d8e764771b5a5f04163334ad61b39db || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 3e168d1b081ee3a2175fe1bd97ad03bb40fe7ce38a37e99923a19f0e7ec4d81c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" ca7fe2ca54a97e047f5eff236e62ae87546e862f509f0a62fc6e564ded3c6a95 || goto error
call :downloadfile "SDL-7914bdb7ea14ee5109d50df857c8dfc69a28a62d.patch" https://github.com/libsdl-org/SDL/commit/7914bdb7ea14ee5109d50df857c8dfc69a28a62d.patch 5c09a29a9cac87a85ec3b8f5a68ff0e5c6c47229f3e2282241b9c32166f26977 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" d3b5379edcace266273d789249b6d68ae9495ec1b0b562ba6d039034cd315d8e || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" ac2fe34a9f1c1451b6785474e9b1b64eb59edef6553be3d630240f16a730456d || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" c12f8bfb617e4a03da104be36f6966ba7f64bee331f0095da1a649a1149796d2 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c6d0f0a512304ad87b20f5ff604442dd8d55769d659576ecfe5462fcd7bb9b7d || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" e6cc1ebf62cf37d81f3b86990086108518037bb383e75da327f297cc4fc1ae36 || goto error
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
@@ -188,6 +189,7 @@ echo Building SDL...
rmdir /S /Q "%SDL%"
%SEVENZIP% x "%SDL%.zip" || goto error
cd "%SDL%" || goto error
%PATCH% -p1 < "..\SDL-7914bdb7ea14ee5109d50df857c8dfc69a28a62d.patch" || goto error
cmake -B build %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release %FORCEPDB% -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
@@ -44,10 +44,10 @@ set FREETYPE=2.14.1
set HARFBUZZ=12.0.0
set LIBJPEGTURBO=3.1.2
set LIBPNG=1650
set SDL=SDL3-3.2.22
set SDL=SDL3-3.2.24
set LIBPNGLONG=1.6.50
set QT=6.9.2
set QTMINOR=6.9
set QT=6.10.0
set QTMINOR=6.10
set QTAPNG=1.3.0
set LZ4=1.10.0
set WEBP=1.6.0
@@ -69,12 +69,13 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 687ddc0c7cb128a3ea58e159b5129252537c27ede0c32a93f11f03127f0c0165 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d60068b1e5c83c66bb14c325dfef46f8fcc380735b4591de6f5e7b9738929d1 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 97d59c78e40b4ddd018738d285a12afc320b57f8265a3f760353739a3619ccdb || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f2fc6ff382c6f3af79493d0709dbd64847d0356313518f094f9096315f2fdb30 || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" af80bb671ea0f66c0036ce7041a56b0e550fc94fb88d2c77b5b6a3e33e42139b || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" d2f4c7a4a12630e879702353f944f96a5d8e764771b5a5f04163334ad61b39db || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 3e168d1b081ee3a2175fe1bd97ad03bb40fe7ce38a37e99923a19f0e7ec4d81c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" ca7fe2ca54a97e047f5eff236e62ae87546e862f509f0a62fc6e564ded3c6a95 || goto error
call :downloadfile "SDL-7914bdb7ea14ee5109d50df857c8dfc69a28a62d.patch" https://github.com/libsdl-org/SDL/commit/7914bdb7ea14ee5109d50df857c8dfc69a28a62d.patch 5c09a29a9cac87a85ec3b8f5a68ff0e5c6c47229f3e2282241b9c32166f26977 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" d3b5379edcace266273d789249b6d68ae9495ec1b0b562ba6d039034cd315d8e || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" ac2fe34a9f1c1451b6785474e9b1b64eb59edef6553be3d630240f16a730456d || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" c12f8bfb617e4a03da104be36f6966ba7f64bee331f0095da1a649a1149796d2 || goto error
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" c6d0f0a512304ad87b20f5ff604442dd8d55769d659576ecfe5462fcd7bb9b7d || goto error
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" e6cc1ebf62cf37d81f3b86990086108518037bb383e75da327f297cc4fc1ae36 || goto error
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
@@ -185,6 +186,7 @@ echo Building SDL...
rmdir /S /Q "%SDL%"
%SEVENZIP% x "%SDL%.zip" || goto error
cd "%SDL%" || goto error
%PATCH% -p1 < "..\SDL-7914bdb7ea14ee5109d50df857c8dfc69a28a62d.patch" || goto error
cmake -B build -DCMAKE_BUILD_TYPE=Release %FORCEPDB% -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DSDL_SHARED=ON -DSDL_STATIC=OFF -G Ninja || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
+3 -2
View File
@@ -1,7 +1,7 @@
name: 🖥️ 📦 Dispatch Windows Deps Build
on:
pull_request:
pull_request_target:
types: [closed]
jobs:
@@ -10,7 +10,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Dispatch to windows-dependencies repo
uses: peter-evans/repository-dispatch@v3
uses: peter-evans/repository-dispatch@v4
with:
token: ${{ secrets.DEPS_REPO_DISPATCH_ACCESS_TOKEN }}
repository: pcsx2/pcsx2-windows-dependencies
event-type: deps-update
+113 -3
View File
@@ -1,7 +1,117 @@
# libcubeb - Cross-platform Audio I/O Library
[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml)
See INSTALL.md for build instructions.
`libcubeb` is a cross-platform C library for high and low-latency audio input/output. It provides a simple, consistent API for audio playback and recording across multiple platforms and audio backends. It is written in C, C++ and Rust, with a C ABI and [Rust](https://github.com/mozilla/cubeb-rs) bindings. While originally written for use in the Firefox Web browser, a number of other software projects have adopted it.
See [Backend Support](https://github.com/mozilla/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
## Features
Licensed under an ISC-style license. See LICENSE for details.
- **Cross-platform support**: Windows, macOS, Linux, Android, and other platforms
- **Versatile**: Optimized for low-latency real-time audio applications, or power efficient higher latency playback
- **A/V sync**: Latency compensated audio clock reporting for easy audio/video synchronization
- **Full-duplex support**: Simultaneous audio input and output, reclocked
- **Device enumeration**: Query available audio devices
- **Audio processing for speech**: Can use VoiceProcessing IO on recent macOS
## Supported Backends & status
| *Backend* | *Support Level* | *Platform version* | *Notes* |
|-------------------|-----------------|--------------------|--------------------------------------------------|
| PulseAudio (Rust) | Tier-1 | | Main Linux desktop backend |
| AudioUnit (Rust) | Tier-1 | | Main macOS backend |
| WASAPI | Tier-1 | Windows >= 7 | Main Windows backend |
| AAudio | Tier-1 | Android >= 8 | Main Android backend for most devices |
| OpenSL | Tier-1 | Android >= 2.3 | Android backend for older devices |
| OSS | Tier-2 | | |
| sndio | Tier-2 | | |
| Sun | Tier-2 | | |
| WinMM | Tier-3 | Windows XP | Was Tier-1, Firefox minimum Windows version 7. |
| AudioTrack | Tier-3 | Android < 2.3 | Was Tier-1, Firefox minimum Android version 4.1. |
| ALSA | Tier-3 | | |
| JACK | Tier-3 | | |
| KAI | Tier-3 | | |
| PulseAudio (C) | Tier-4 | | Was Tier-1, superseded by Rust |
| AudioUnit (C++) | Tier-4 | | Was Tier-1, superseded by Rust |
Tier-1: Actively maintained. Should have CI coverage. Critical for Firefox.
Tier-2: Actively maintained by contributors. CI coverage appreciated.
Tier-3: Maintainers/patches accepted. Status unclear.
Tier-4: Deprecated, obsolete. Scheduled to be removed.
Note that the support level is not a judgement of the relative merits
of a backend, only the current state of support, which is informed
by Firefox's needs, the responsiveness of a backend's
maintainer, and the level of contributions to that backend.
## Building
### Prerequisites
- CMake 3.15 or later
- Non-ancient MSVC, clang or gcc, for compiling both C and C++
- Platform-specific audio libraries (automatically detected)
- Optional but recommended: Rust compiler to compile and link more recent backends for macOS and PulseAudio
### Quick build
```bash
git clone https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build
cmake --build build
```
### Better build with Rust backends
```bash
git clone --recursive https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build -DBUILD_RUST_LIBS=ON
cmake --build build
```
### Platform-Specific Notes
**Windows**: Supports Visual Studio 2015+ and MinGW-w64. Use `-G "Visual Studio 16 2019"` or `-G "MinGW Makefiles"`.
**macOS**: Requires Xcode command line tools. Audio frameworks are automatically linked.
**Linux**: Development packages for desired backends:
```bash
# Ubuntu/Debian
sudo apt-get install libpulse-dev libasound2-dev libjack-dev
# Fedora/RHEL
sudo dnf install pulseaudio-libs-devel alsa-lib-devel jack-audio-connection-kit-devel
```
**Android**: Use with Android NDK. AAudio requires API level 26+.
## Testing
Run the test suite:
```bash
cd build
ctest
```
Use the interactive test tool:
```bash
./cubeb-test
```
## License
Licensed under an ISC-style license. See [LICENSE](LICENSE) for details.
## Contributing
Contributions are welcome! Please see the [contribution guidelines](CONTRIBUTING.md) and check the [issue tracker](https://github.com/mozilla/cubeb/issues).
## Links
- [GitHub Repository](https://github.com/mozilla/cubeb)
- [API Documentation](https://mozilla.github.io/cubeb/)
+57 -40
View File
@@ -49,6 +49,7 @@ extern "C" {
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
output_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
if (rv != CUBEB_OK) {
@@ -62,6 +63,7 @@ extern "C" {
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
input_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
cubeb_stream * stm;
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
@@ -193,39 +195,39 @@ typedef uint32_t cubeb_channel_layout;
// Some common layout definitions.
enum {
CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
CUBEB_LAYOUT_MONO = (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = (uint32_t)CUBEB_LAYOUT_MONO | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = (uint32_t)CUBEB_LAYOUT_STEREO | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F =
(uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT | (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = (uint32_t)CUBEB_LAYOUT_3F | (uint32_t)CHANNEL_LOW_FREQUENCY,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F1 =
(uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT | (uint32_t)CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = (uint32_t)CUBEB_LAYOUT_2F1 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = (uint32_t)CUBEB_LAYOUT_3F1 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_SIDE_LEFT | (uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = (uint32_t)CUBEB_LAYOUT_2F2 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_BACK_LEFT | (uint32_t)CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = (uint32_t)CUBEB_LAYOUT_QUAD | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_SIDE_LEFT |
(uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = (uint32_t)CUBEB_LAYOUT_3F2 | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = (uint32_t)CUBEB_LAYOUT_QUAD | (uint32_t)CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = (uint32_t)CUBEB_LAYOUT_3F2_BACK | (uint32_t)CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_LOW_FREQUENCY |
(uint32_t)CHANNEL_BACK_CENTER | (uint32_t)CHANNEL_SIDE_LEFT |
(uint32_t)CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = (uint32_t)CHANNEL_FRONT_LEFT | (uint32_t)CHANNEL_FRONT_RIGHT |
(uint32_t)CHANNEL_FRONT_CENTER | (uint32_t)CHANNEL_LOW_FREQUENCY |
(uint32_t)CHANNEL_BACK_LEFT | (uint32_t)CHANNEL_BACK_RIGHT |
(uint32_t)CHANNEL_SIDE_LEFT | (uint32_t)CHANNEL_SIDE_RIGHT,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
};
/** Miscellaneous stream preferences. */
@@ -279,7 +281,10 @@ typedef struct {
cubeb_channel_layout
layout; /**< Requested channel layout. This must be consistent with the
provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_input_processing_params input_params; /**< Requested input processing
params. Ignored for output streams. At present, only supported on the
WASAPI backend; others should use cubeb_set_input_processing_params. */
} cubeb_stream_params;
/** Audio device description */
@@ -414,6 +419,13 @@ typedef struct {
size_t count; /**< Device count in collection. */
} cubeb_device_collection;
/** Array of compiled backends returned by `cubeb_get_backend_names`. */
typedef struct {
const char * const *
names; /**< Array of strings representing backend names. */
size_t count; /**< Length of the array. */
} cubeb_backend_names;
/** User supplied data callback.
- Calling other cubeb functions from this callback is unsafe.
- The code in the callback should be non-blocking.
@@ -454,6 +466,8 @@ typedef void (*cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* This callback will be called when devices are added or removed from the
* system, or when the default device changes for the specified device type.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to
* cubeb_register_device_collection_changed. */
@@ -485,17 +499,18 @@ CUBEB_EXPORT int
cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
/** Returns a list of backend names which can be supplid to cubeb_init().
Array is null-terminated. */
CUBEB_EXPORT const char**
cubeb_get_backend_names();
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
CUBEB_EXPORT char const *
cubeb_get_backend_id(cubeb * context);
/** Get a read-only array of strings identifying available backends.
These can be passed as `backend_name` parameter to `cubeb_init`.
@retval Struct containing the array with backend names. */
CUBEB_EXPORT cubeb_backend_names
cubeb_get_backend_names();
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@param max_channels The maximum number of channels.
@@ -674,7 +689,7 @@ cubeb_stream_get_current_device(cubeb_stream * stm,
application is accessing audio input. When all inputs are muted they can
prove to the user that the application is not actively capturing any input.
@param stream the stream for which to set input mute state
@param muted whether the input should mute or not
@param mute whether the input should mute or not
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if this stream does not have an input
device
@@ -745,14 +760,16 @@ cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
a new device or a device is removed, or when the default device
changes for the specified device type.
@param context
@param devtype device type to include. Different callbacks and user pointers
can be registered for each devtype. The hybrid devtype
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
and will register the provided callback and user pointer in both
sides.
@param callback a function called whenever the system device list changes.
@param callback a function called whenever the system device list changes,
including when default devices change.
Passing NULL allow to unregister a function. You have to unregister
first before you register a new callback.
@param user_ptr pointer to user specified data which will be present in
+111 -14
View File
@@ -31,6 +31,10 @@ struct cubeb_stream {
int
pulse_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_PULSE_RUST)
int
pulse_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_JACK)
int
jack_init(cubeb ** context, char const * context_name);
@@ -43,6 +47,10 @@ alsa_init(cubeb ** context, char const * context_name);
int
audiounit_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT_RUST)
int
audiounit_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_WINMM)
int
winmm_init(cubeb ** context, char const * context_name);
@@ -55,10 +63,30 @@ wasapi_init(cubeb ** context, char const * context_name);
int
sndio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SUN)
int
sun_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OPENSL)
int
opensl_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OSS)
int
oss_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AAUDIO)
int
aaudio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOTRACK)
int
audiotrack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_KAI)
int
kai_init(cubeb ** context, char const * context_name);
#endif
static int
validate_stream_params(cubeb_stream_params * input_stream_params,
@@ -123,6 +151,10 @@ cubeb_init(cubeb ** context, char const * context_name,
if (!strcmp(backend_name, "pulse")) {
#if defined(USE_PULSE)
init_oneshot = pulse_init;
#endif
} else if (!strcmp(backend_name, "pulse-rust")) {
#if defined(USE_PULSE_RUST)
init_oneshot = pulse_rust_init;
#endif
} else if (!strcmp(backend_name, "jack")) {
#if defined(USE_JACK)
@@ -135,6 +167,10 @@ cubeb_init(cubeb ** context, char const * context_name,
} else if (!strcmp(backend_name, "audiounit")) {
#if defined(USE_AUDIOUNIT)
init_oneshot = audiounit_init;
#endif
} else if (!strcmp(backend_name, "audiounit-rust")) {
#if defined(USE_AUDIOUNIT_RUST)
init_oneshot = audiounit_rust_init;
#endif
} else if (!strcmp(backend_name, "wasapi")) {
#if defined(USE_WASAPI)
@@ -147,10 +183,30 @@ cubeb_init(cubeb ** context, char const * context_name,
} else if (!strcmp(backend_name, "sndio")) {
#if defined(USE_SNDIO)
init_oneshot = sndio_init;
#endif
} else if (!strcmp(backend_name, "sun")) {
#if defined(USE_SUN)
init_oneshot = sun_init;
#endif
} else if (!strcmp(backend_name, "opensl")) {
#if defined(USE_OPENSL)
init_oneshot = opensl_init;
#endif
} else if (!strcmp(backend_name, "oss")) {
#if defined(USE_OSS)
init_oneshot = oss_init;
#endif
} else if (!strcmp(backend_name, "aaudio")) {
#if defined(USE_AAUDIO)
init_oneshot = aaudio_init;
#endif
} else if (!strcmp(backend_name, "audiotrack")) {
#if defined(USE_AUDIOTRACK)
init_oneshot = audiotrack_init;
#endif
} else if (!strcmp(backend_name, "kai")) {
#if defined(USE_KAI)
init_oneshot = kai_init;
#endif
} else {
/* Already set */
@@ -163,6 +219,9 @@ cubeb_init(cubeb ** context, char const * context_name,
* to override all other choices
*/
init_oneshot,
#if defined(USE_PULSE_RUST)
pulse_rust_init,
#endif
#if defined(USE_PULSE)
pulse_init,
#endif
@@ -178,6 +237,9 @@ cubeb_init(cubeb ** context, char const * context_name,
#if defined(USE_OSS)
oss_init,
#endif
#if defined(USE_AUDIOUNIT_RUST)
audiounit_rust_init,
#endif
#if defined(USE_AUDIOUNIT)
audiounit_init,
#endif
@@ -189,6 +251,18 @@ cubeb_init(cubeb ** context, char const * context_name,
#endif
#if defined(USE_SUN)
sun_init,
#endif
#if defined(USE_AAUDIO)
aaudio_init,
#endif
#if defined(USE_OPENSL)
opensl_init,
#endif
#if defined(USE_AUDIOTRACK)
audiotrack_init,
#endif
#if defined(USE_KAI)
kai_init,
#endif
};
int i;
@@ -214,13 +288,26 @@ cubeb_init(cubeb ** context, char const * context_name,
return CUBEB_ERROR;
}
const char**
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
}
cubeb_backend_names
cubeb_get_backend_names()
{
static const char* backend_names[] = {
static const char * const backend_names[] = {
#if defined(USE_PULSE)
"pulse",
#endif
#if defined(USE_PULSE_RUST)
"pulse-rust",
#endif
#if defined(USE_JACK)
"jack",
#endif
@@ -230,6 +317,9 @@ cubeb_get_backend_names()
#if defined(USE_AUDIOUNIT)
"audiounit",
#endif
#if defined(USE_AUDIOUNIT_RUST)
"audiounit-rust",
#endif
#if defined(USE_WASAPI)
"wasapi",
#endif
@@ -239,23 +329,30 @@ cubeb_get_backend_names()
#if defined(USE_SNDIO)
"sndio",
#endif
#if defined(USE_SUN)
"sun",
#endif
#if defined(USE_OPENSL)
"opensl",
#endif
#if defined(USE_OSS)
"oss",
#endif
NULL,
#if defined(USE_AAUDIO)
"aaudio",
#endif
#if defined(USE_AUDIOTRACK)
"audiotrack",
#endif
#if defined(USE_KAI)
"kai",
#endif
};
return backend_names;
}
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
return (cubeb_backend_names){
.names = backend_names,
.count = NELEMS(backend_names),
};
}
int
+12 -5
View File
@@ -213,12 +213,19 @@ struct cubeb_stream {
cubeb_device_changed_callback device_changed_callback = nullptr;
owned_critical_section device_changed_callback_lock;
/* Stream creation parameters */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
device_info input_device;
device_info output_device;
/* Format descriptions */
+159 -2
View File
@@ -16,8 +16,8 @@
#include <time.h>
#endif
static std::atomic<cubeb_log_level> g_cubeb_log_level;
static std::atomic<cubeb_log_callback> g_cubeb_log_callback;
std::atomic<cubeb_log_level> g_cubeb_log_level;
std::atomic<cubeb_log_callback> g_cubeb_log_callback;
/** The maximum size of a log message, after having been formatted. */
const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
@@ -32,6 +32,133 @@ cubeb_noop_log_callback(char const * /* fmt */, ...)
{
}
/**
* This wraps an inline buffer, that represents a log message, that must be
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message {
public:
cubeb_log_message() { *storage = '\0'; }
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
size_t length = strlen(str);
/* paranoia against malformed message */
assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
return;
}
PodCopy(storage, str, length);
storage[length] = '\0';
}
char const * get() { return storage; }
private:
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE]{};
};
/** Lock-free asynchronous logger, made so that logging from a
* real-time audio callback does not block the audio thread. */
class cubeb_async_logger {
public:
/* This is thread-safe since C++11 */
static cubeb_async_logger & get()
{
static cubeb_async_logger instance;
return instance;
}
void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
cubeb_log_message msg(str);
auto * owned_queue = msg_queue.load();
// Check if the queue is being deallocated. If not, grab ownership. If yes,
// return, the message won't be logged.
if (!owned_queue ||
!msg_queue.compare_exchange_strong(owned_queue, nullptr)) {
return;
}
owned_queue->enqueue(msg);
// Return ownership.
msg_queue.store(owned_queue);
}
void run()
{
assert(logging_thread.get_id() == std::thread::id());
logging_thread = std::thread([this]() {
CUBEB_REGISTER_THREAD("cubeb_log");
while (!shutdown_thread) {
cubeb_log_message msg;
while (msg_queue_consumer.load()->dequeue(&msg, 1)) {
cubeb_log_internal_no_format(msg.get());
}
std::this_thread::sleep_for(
std::chrono::milliseconds(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS));
}
CUBEB_UNREGISTER_THREAD();
});
}
// Tell the underlying queue the producer thread has changed, so it does not
// assert in debug. This should be called with the thread stopped.
void reset_producer_thread()
{
if (msg_queue) {
msg_queue.load()->reset_thread_ids();
}
}
void start()
{
auto * queue =
new lock_free_queue<cubeb_log_message>(CUBEB_LOG_MESSAGE_QUEUE_DEPTH);
msg_queue.store(queue);
msg_queue_consumer.store(queue);
shutdown_thread = false;
run();
}
void stop()
{
assert(((g_cubeb_log_callback == cubeb_noop_log_callback) ||
!g_cubeb_log_callback) &&
"Only call stop after logging has been disabled.");
shutdown_thread = true;
if (logging_thread.get_id() != std::thread::id()) {
logging_thread.join();
logging_thread = std::thread();
auto * owned_queue = msg_queue.load();
// Check if the queue is being used. If not, grab ownership. If yes,
// try again shortly. At this point, the logging thread has been joined,
// so nothing is going to dequeue.
// If there is a valid pointer here, then the real-time audio thread that
// logs won't attempt to write into the queue, and instead drop the
// message.
while (!msg_queue.compare_exchange_weak(owned_queue, nullptr)) {
}
delete owned_queue;
msg_queue_consumer.store(nullptr);
}
}
private:
cubeb_async_logger() {}
~cubeb_async_logger()
{
assert(logging_thread.get_id() == std::thread::id() &&
(g_cubeb_log_callback == cubeb_noop_log_callback ||
!g_cubeb_log_callback));
if (msg_queue.load()) {
delete msg_queue.load();
}
}
/** This is quite a big data structure, but is only instantiated if the
* asynchronous logger is used. The two pointers point to the same object, but
* the first one can be temporarily null when a message is being enqueued. */
std::atomic<lock_free_queue<cubeb_log_message> *> msg_queue = {nullptr};
std::atomic<lock_free_queue<cubeb_log_message> *> msg_queue_consumer = {
nullptr};
std::atomic<bool> shutdown_thread = {false};
std::thread logging_thread;
};
void
cubeb_log_internal(char const * file, uint32_t line, char const * fmt, ...)
{
@@ -49,6 +176,29 @@ cubeb_log_internal_no_format(const char * msg)
g_cubeb_log_callback.load()(msg);
}
void
cubeb_async_log(char const * fmt, ...)
{
// This is going to copy a 256 bytes array around, which is fine.
// We don't want to allocate memory here, because this is made to
// be called from a real-time callback.
va_list args;
va_start(args, fmt);
char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
cubeb_async_logger::get().push(msg);
va_end(args);
}
void
cubeb_async_log_reset_threads(void)
{
if (!g_cubeb_log_callback) {
return;
}
cubeb_async_logger::get().reset_producer_thread();
}
void
cubeb_log_set(cubeb_log_level log_level, cubeb_log_callback log_callback)
{
@@ -57,8 +207,15 @@ cubeb_log_set(cubeb_log_level log_level, cubeb_log_callback log_callback)
// nullptr, to prevent a TOCTOU race between checking the pointer
if (log_callback && log_level != CUBEB_LOG_DISABLED) {
g_cubeb_log_callback = log_callback;
if (log_level == CUBEB_LOG_VERBOSE) {
cubeb_async_logger::get().start();
}
} else if (!log_callback || CUBEB_LOG_DISABLED) {
g_cubeb_log_callback = cubeb_noop_log_callback;
// This returns once the thread has joined.
// This is safe even if CUBEB_LOG_VERBOSE was not set; the thread will
// simply not be joinable.
cubeb_async_logger::get().stop();
} else {
assert(false && "Incorrect parameters passed to cubeb_log_set");
}
+15 -3
View File
@@ -39,7 +39,12 @@ cubeb_log_get_callback(void);
void
cubeb_log_internal_no_format(const char * msg);
void
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...)
PRINTF_FORMAT(3, 4);
void
cubeb_async_log(const char * fmt, ...) PRINTF_FORMAT(1, 2);
void
cubeb_async_log_reset_threads(void);
#ifdef __cplusplus
}
@@ -55,9 +60,16 @@ cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
} \
} while (0)
#define ALOG_INTERNAL(level, fmt, ...) \
do { \
if (cubeb_log_get_level() >= level && cubeb_log_get_callback()) { \
cubeb_async_log(fmt, ##__VA_ARGS__); \
} \
} while (0)
/* Asynchronous logging macros to log in real-time callbacks. */
/* Should not be used on android due to the use of global/static variables. */
#define ALOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define ALOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#define ALOGV(msg, ...) ALOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define ALOG(msg, ...) ALOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#endif // CUBEB_LOG
+6
View File
@@ -371,3 +371,9 @@ cubeb_resampler_latency(cubeb_resampler * resampler)
{
return resampler->latency();
}
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler)
{
return resampler->stats();
}
+14
View File
@@ -84,6 +84,20 @@ cubeb_resampler_destroy(cubeb_resampler * resampler);
long
cubeb_resampler_latency(cubeb_resampler * resampler);
/**
* Test-only introspection API to ensure that there is no buffering
* buildup when resampling.
*/
typedef struct {
size_t input_input_buffer_size;
size_t input_output_buffer_size;
size_t output_input_buffer_size;
size_t output_output_buffer_size;
} cubeb_resampler_stats;
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler);
#if defined(__cplusplus)
}
#endif
+62 -30
View File
@@ -56,6 +56,7 @@ struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long frames_needed) = 0;
virtual long latency() = 0;
virtual cubeb_resampler_stats stats() = 0;
virtual ~cubeb_resampler() {}
};
@@ -86,6 +87,16 @@ public:
virtual long latency() { return 0; }
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats;
stats.input_input_buffer_size = internal_input_buffer.length();
stats.input_output_buffer_size = 0;
stats.output_input_buffer_size = 0;
stats.output_output_buffer_size = 0;
return stats;
}
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
@@ -122,6 +133,20 @@ public:
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed);
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats = {};
if (input_processor) {
stats.input_input_buffer_size = input_processor->input_buffer_size();
stats.input_output_buffer_size = input_processor->output_buffer_size();
}
if (output_processor) {
stats.output_input_buffer_size = output_processor->input_buffer_size();
stats.output_output_buffer_size = output_processor->output_buffer_size();
}
return stats;
}
virtual long latency()
{
if (input_processor && output_processor) {
@@ -280,29 +305,28 @@ public:
}
/** Returns the number of frames to pass in the input of the resampler to have
* exactly `output_frame_count` resampled frames. This can return a number
* slightly bigger than what is strictly necessary, but it guaranteed that the
* number of output frames will be exactly equal. */
* at least `output_frame_count` resampled frames. */
uint32_t input_needed_for_output(int32_t output_frame_count) const
{
assert(output_frame_count >= 0); // Check overflow
int32_t unresampled_frames_left =
samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left =
samples_to_frames(resampling_out_buffer.length());
float input_frames_needed =
(output_frame_count - unresampled_frames_left) * resampling_ratio -
resampled_frames_left;
if (input_frames_needed < 0) {
return 0;
}
return (uint32_t)ceilf(input_frames_needed);
float input_frames_needed_frac =
static_cast<float>(output_frame_count) * resampling_ratio;
// speex_resample()` can be irregular in its consumption of input samples.
// Provide one more frame than the number that would be required with
// regular consumption, to make the speex resampler behave more regularly,
// and so predictably.
auto input_frame_needed =
1 + static_cast<int32_t>(ceilf(input_frames_needed_frac));
input_frame_needed -= std::min(unresampled_frames_left, input_frame_needed);
return input_frame_needed;
}
/** Returns a pointer to the input buffer, that contains empty space for at
* least `frame_count` elements. This is useful so that consumer can directly
* write into the input buffer of the resampler. The pointer returned is
* adjusted so that leftover data are not overwritten.
* least `frame_count` elements. This is useful so that consumer can
* directly write into the input buffer of the resampler. The pointer
* returned is adjusted so that leftover data are not overwritten.
*/
T * input_buffer(size_t frame_count)
{
@@ -312,8 +336,8 @@ public:
return resampling_in_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t written_frames)
{
resampling_in_buffer.set_length(leftover_samples +
@@ -331,6 +355,9 @@ public:
}
}
size_t input_buffer_size() const { return resampling_in_buffer.length(); }
size_t output_buffer_size() const { return resampling_out_buffer.length(); }
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
@@ -359,6 +386,7 @@ private:
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
/** The state for the speex resampler used internaly. */
SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */
@@ -371,8 +399,8 @@ private:
auto_array<T> resampling_out_buffer;
/** Additional latency inserted into the pipeline for synchronisation. */
uint32_t additional_latency;
/** When `input_buffer` is called, this allows tracking the number of samples
that were in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that were in the buffer. */
uint32_t leftover_samples;
};
@@ -417,8 +445,8 @@ public:
return delay_output_buffer.data();
}
/** Get a pointer to the first writable location in the input buffer>
* @parameter frames_needed the number of frames the user needs to write into
* the buffer.
* @parameter frames_needed the number of frames the user needs to write
* into the buffer.
* @returns a pointer to a location in the input buffer where #frames_needed
* can be writen. */
T * input_buffer(uint32_t frames_needed)
@@ -428,8 +456,8 @@ public:
frames_to_samples(frames_needed));
return delay_input_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t frames_written)
{
delay_input_buffer.set_length(leftover_samples +
@@ -450,8 +478,8 @@ public:
return to_pop;
}
/** Returns the number of frames one needs to input into the delay line to get
* #frames_needed frames back.
/** Returns the number of frames one needs to input into the delay line to
* get #frames_needed frames back.
* @parameter frames_needed the number of frames one want to write into the
* delay_line
* @returns the number of frames one will get. */
@@ -469,19 +497,23 @@ public:
void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
size_t input_buffer_size() const { return delay_input_buffer.length(); }
size_t output_buffer_size() const { return delay_output_buffer.length(); }
private:
/** The length, in frames, of this delay line */
uint32_t length;
/** When `input_buffer` is called, this allows tracking the number of samples
that where in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that where in the buffer. */
uint32_t leftover_samples;
/** The input buffer, where the delay is applied. */
auto_array<T> delay_input_buffer;
@@ -511,8 +543,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
"need at least one valid parameter pointer.");
/* All the streams we have have a sample rate that matches the target
sample rate, use a no-op resampler, that simply forwards the buffers to the
callback. */
sample rate, use a no-op resampler, that simply forwards the buffers to
the callback. */
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
+193 -297
View File
@@ -4,8 +4,12 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0603
#endif // !_WIN32_WINNT
#ifndef NOMINMAX
#define NOMINMAX
#endif // !NOMINMAX
#include <algorithm>
#include <atomic>
@@ -37,31 +41,6 @@
#include "cubeb_tracing.h"
#include "cubeb_utils.h"
// Some people have reported glitches with IAudioClient3 capture streams:
// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
// https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
#define ALLOW_AUDIO_CLIENT_3_FOR_INPUT 0
// IAudioClient3::GetSharedModeEnginePeriod() seem to return min latencies
// bigger than IAudioClient::GetDevicePeriod(), which is confusing (10ms vs
// 3ms), though the default latency is usually the same and we should use the
// IAudioClient3 function anyway, as it's more correct
#define USE_AUDIO_CLIENT_3_MIN_PERIOD 1
// If this is true, we allow IAudioClient3 the creation of sessions with a
// latency above the default one (usually 10ms).
// Whether we should default this to true or false depend on many things:
// -Does creating a shared IAudioClient3 session (not locked to a format)
// actually forces all the IAudioClient(1) sessions to have the same latency?
// I could find no proof of that.
// -Does creating a shared IAudioClient3 session with a latency >= the default
// one actually improve the latency (as in how late the audio is) at all?
// -Maybe we could expose this as cubeb stream pref
// (e.g. take priority over other apps)?
#define ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT 1
// If this is true and the user specified a target latency >= the IAudioClient3
// max one, then we reject it and fall back to IAudioClient(1). There wouldn't
// be much point in having a low latency if that's not what the user wants.
#define REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX 0
// Windows 10 exposes the IAudioClient3 interface to create low-latency streams.
// Copy the interface definition from audioclient.h here to make the code
// simpler and so that we can still access IAudioClient3 via COM if cubeb was
@@ -229,11 +208,6 @@ struct auto_stream_ref {
cubeb_stream * stm;
};
using set_mm_thread_characteristics_function =
decltype(&AvSetMmThreadCharacteristicsW);
using revert_mm_thread_characteristics_function =
decltype(&AvRevertMmThreadCharacteristics);
extern cubeb_ops const wasapi_ops;
static com_heap_ptr<wchar_t>
@@ -304,8 +278,8 @@ wasapi_enumerate_devices_internal(cubeb * context, cubeb_device_type type,
static int
wasapi_device_collection_destroy(cubeb * ctx,
cubeb_device_collection * collection);
static char const *
wstr_to_utf8(wchar_t const * str);
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str);
static std::unique_ptr<wchar_t const[]>
utf8_to_wstr(char const * str);
@@ -314,6 +288,15 @@ utf8_to_wstr(char const * str);
class wasapi_collection_notification_client;
class monitor_device_notifications;
typedef enum {
/* Clear options */
CUBEB_AUDIO_CLIENT2_NONE,
/* Use AUDCLNT_STREAMOPTIONS_RAW */
CUBEB_AUDIO_CLIENT2_RAW,
/* Use CUBEB_STREAM_PREF_COMMUNICATIONS */
CUBEB_AUDIO_CLIENT2_VOICE
} AudioClient2Option;
struct cubeb {
cubeb_ops const * ops = &wasapi_ops;
owned_critical_section lock;
@@ -331,13 +314,6 @@ struct cubeb {
nullptr;
void * output_collection_changed_user_ptr = nullptr;
UINT64 performance_counter_frequency;
/* Library dynamically opened to increase the render thread priority, and
the two function pointers we need. */
HMODULE mmcss_module = nullptr;
set_mm_thread_characteristics_function set_mm_thread_characteristics =
nullptr;
revert_mm_thread_characteristics_function revert_mm_thread_characteristics =
nullptr;
};
class wasapi_endpoint_notification_client;
@@ -360,20 +336,33 @@ struct cubeb_stream {
/* Mixer pameters. We need to convert the input stream to this
samplerate/channel layout, as WASAPI does not resample nor upmix
itself. */
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* Stream parameters. This is what the client requested,
* and what will be presented in the callback. */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* A MMDevice role for this stream: either communication or console here. */
ERole role;
/* True if this stream will transport voice-data. */
@@ -662,6 +651,10 @@ public:
LPCWSTR device_id)
{
LOG("collection: Audio device default changed, id = %S.", device_id);
/* Default device changes count as device collection changes */
monitor_notifications.notify(flow);
return S_OK;
}
@@ -772,7 +765,7 @@ public:
LPCWSTR device_id)
{
LOG("endpoint: Audio device default changed flow=%d role=%d "
"new_device_id=%ws.",
"new_device_id=%S.",
flow, role, device_id);
/* we only support a single stream type for now. */
@@ -783,11 +776,13 @@ public:
DWORD last_change_ms = timeGetTime() - last_device_change;
bool same_device = default_device_id && device_id &&
wcscmp(default_device_id.get(), device_id) == 0;
LOG("endpoint: Audio device default changed last_change=%u same_device=%d",
LOG("endpoint: Audio device default changed last_change=%lu same_device=%d",
last_change_ms, same_device);
if (last_change_ms > DEVICE_CHANGE_DEBOUNCE_MS || !same_device) {
if (device_id) {
default_device_id.reset(_wcsdup(device_id));
wchar_t * new_device_id = new wchar_t[wcslen(device_id) + 1];
wcscpy(new_device_id, device_id);
default_device_id.reset(new_device_id);
} else {
default_device_id.reset();
}
@@ -863,16 +858,12 @@ intern_device_id(cubeb * ctx, wchar_t const * id)
auto_lock lock(ctx->lock);
char const * tmp = wstr_to_utf8(id);
std::unique_ptr<char const[]> tmp = wstr_to_utf8(id);
if (!tmp) {
return nullptr;
}
char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
free((void *)tmp);
return interned;
return cubeb_strings_intern(ctx->device_ids, tmp.get());
}
bool
@@ -977,7 +968,7 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
cubeb_resampler_fill(stm->resampler.get(), input_buffer,
&input_frames_count, dest, output_frames_needed);
if (out_frames < 0) {
ALOGV("Callback refill error: %d", out_frames);
ALOGV("Callback refill error: %ld", out_frames);
wasapi_state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return out_frames;
}
@@ -1263,8 +1254,8 @@ refill_callback_duplex(cubeb_stream * stm)
XASSERT(has_input(stm) && has_output(stm));
if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
rv = get_input_buffer(stm);
if (!rv) {
return rv;
}
}
@@ -1274,7 +1265,6 @@ refill_callback_duplex(cubeb_stream * stm)
rv = get_output_buffer(stm, output_buffer, output_frames);
if (!rv) {
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
return rv;
}
@@ -1291,9 +1281,11 @@ refill_callback_duplex(cubeb_stream * stm)
stm->total_output_frames += output_frames;
ALOGV("in: %zu, out: %zu, missing: %ld, ratio: %f", stm->total_input_frames,
stm->total_output_frames,
static_cast<long>(stm->total_output_frames) - stm->total_input_frames,
ALOGV("in: %llu, out: %llu, missing: %ld, ratio: %f",
(unsigned long long)stm->total_input_frames,
(unsigned long long)stm->total_output_frames,
static_cast<long long>(stm->total_output_frames) -
static_cast<long long>(stm->total_input_frames),
static_cast<float>(stm->total_output_frames) / stm->total_input_frames);
long got;
@@ -1438,8 +1430,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
/* We could consider using "Pro Audio" here for WebAudio and
maybe WebRTC. */
mmcss_handle =
stm->context->set_mm_thread_characteristics(L"Audio", &mmcss_task_index);
mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
if (!mmcss_handle) {
/* This is not fatal, but we might glitch under heavy load. */
LOG("Unable to use mmcss to bump the render thread priority: %lx",
@@ -1519,8 +1510,8 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
is_playing = stm->refill_callback(stm);
break;
case WAIT_OBJECT_0 + 3: { /* input available */
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
bool rv = get_input_buffer(stm);
if (!rv) {
is_playing = false;
continue;
}
@@ -1532,8 +1523,11 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
break;
}
default:
LOG("case %lu not handled in render loop.", waitResult);
XASSERT(false);
LOG("render_loop: waitResult=%lu (lastError=%lu) unhandled, exiting",
waitResult, GetLastError());
is_playing = false;
hr = E_FAIL;
continue;
}
}
@@ -1547,7 +1541,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
}
if (mmcss_handle) {
stm->context->revert_mm_thread_characteristics(mmcss_handle);
AvRevertMmThreadCharacteristics(mmcss_handle);
}
if (FAILED(hr)) {
@@ -1560,18 +1554,6 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
void
wasapi_destroy(cubeb * context);
HANDLE WINAPI
set_mm_thread_characteristics_noop(LPCWSTR, LPDWORD mmcss_task_index)
{
return (HANDLE)1;
}
BOOL WINAPI
revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
{
return true;
}
HRESULT
register_notification_client(cubeb_stream * stm)
{
@@ -1807,31 +1789,6 @@ wasapi_init(cubeb ** context, char const * context_name)
ctx->performance_counter_frequency = 0;
}
ctx->mmcss_module = LoadLibraryW(L"Avrt.dll");
bool success = false;
if (ctx->mmcss_module) {
ctx->set_mm_thread_characteristics =
reinterpret_cast<set_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module, "AvSetMmThreadCharacteristicsW"));
ctx->revert_mm_thread_characteristics =
reinterpret_cast<revert_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module,
"AvRevertMmThreadCharacteristics"));
success = ctx->set_mm_thread_characteristics &&
ctx->revert_mm_thread_characteristics;
}
if (!success) {
// This is not a fatal error, but we might end up glitching when
// the system is under high load.
LOG("Could not load avrt.dll or fetch AvSetMmThreadCharacteristicsW "
"AvRevertMmThreadCharacteristics: %lx",
GetLastError());
ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
ctx->revert_mm_thread_characteristics =
&revert_mm_thread_characteristics_noop;
}
*context = ctx;
return CUBEB_OK;
@@ -1839,7 +1796,6 @@ wasapi_init(cubeb ** context, char const * context_name)
}
namespace {
enum ShutdownPhase { OnStop, OnDestroy };
bool
stop_and_join_render_thread(cubeb_stream * stm)
@@ -1855,16 +1811,7 @@ stop_and_join_render_thread(cubeb_stream * stm)
return false;
}
/* Wait five seconds for the rendering thread to return. It's supposed to
* check its event loop very often, five seconds is rather conservative.
* Note: 5*1s loop to work around timer sleep issues on pre-Windows 8. */
DWORD r;
for (int i = 0; i < 5; ++i) {
r = WaitForSingleObject(stm->thread, 1000);
if (r == WAIT_OBJECT_0) {
break;
}
}
DWORD r = WaitForSingleObject(stm->thread, INFINITE);
if (r != WAIT_OBJECT_0) {
LOG("stop_and_join_render_thread: WaitForSingleObject on thread failed: "
"%lx, %lx",
@@ -1888,10 +1835,6 @@ wasapi_destroy(cubeb * context)
}
}
if (context->mmcss_module) {
FreeLibrary(context->mmcss_module);
}
delete context;
}
@@ -1949,44 +1892,6 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params,
return CUBEB_ERROR;
}
#if USE_AUDIO_CLIENT_3_MIN_PERIOD
// This is unreliable as we can't know the actual mixer format cubeb will
// ask for later on (nor we can branch on ALLOW_AUDIO_CLIENT_3_FOR_INPUT),
// and the min latency can change based on that.
com_ptr<IAudioClient3> client3;
hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL,
client3.receive_vpp());
if (SUCCEEDED(hr)) {
WAVEFORMATEX * mix_format = nullptr;
hr = client3->GetMixFormat(&mix_format);
if (SUCCEEDED(hr)) {
uint32_t default_period = 0, fundamental_period = 0, min_period = 0,
max_period = 0;
hr = client3->GetSharedModeEnginePeriod(mix_format, &default_period,
&fundamental_period, &min_period,
&max_period);
auto sample_rate = mix_format->nSamplesPerSec;
CoTaskMemFree(mix_format);
if (SUCCEEDED(hr)) {
// Print values in the same format as IAudioDevice::GetDevicePeriod()
REFERENCE_TIME min_period_rt(frames_to_hns(sample_rate, min_period));
REFERENCE_TIME default_period_rt(
frames_to_hns(sample_rate, default_period));
LOG("default device period: %I64d, minimum device period: %I64d",
default_period_rt, min_period_rt);
*latency_frames = hns_to_frames(params.rate, min_period_rt);
LOG("Minimum latency in frames: %u", *latency_frames);
return CUBEB_OK;
}
}
}
#endif
com_ptr<IAudioClient> client;
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
client.receive_vpp());
@@ -2006,8 +1911,18 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params,
LOG("default device period: %I64d, minimum device period: %I64d",
default_period, minimum_period);
// The minimum_period is only relevant in exclusive streams.
/* If we're on Windows 10, we can use IAudioClient3 to get minimal latency.
Otherwise, according to the docs, the best latency we can achieve is by
synchronizing the stream and the engine.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx
*/
// #ifdef _WIN32_WINNT_WIN10
#if 0
*latency_frames = hns_to_frames(params.rate, minimum_period);
#else
*latency_frames = hns_to_frames(params.rate, default_period);
#endif
LOG("Minimum latency in frames: %u", *latency_frames);
@@ -2044,6 +1959,21 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
return CUBEB_OK;
}
int
wasapi_get_supported_input_processing_params(
cubeb * ctx, cubeb_input_processing_params * params)
{
// This is not entirely accurate -- windows doesn't document precisely what
// AudioCategory_Communications does -- but assume that we can set all or none
// of them.
*params = static_cast<cubeb_input_processing_params>(
CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION);
return CUBEB_OK;
}
static void
waveformatex_update_derived_properties(WAVEFORMATEX * format)
{
@@ -2097,10 +2027,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
if (hr == S_FALSE) {
/* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
and handle the eventual upmix/downmix ourselves. Ignore the subformat of
the suggestion, since it seems to always be IEEE_FLOAT.
This fallback doesn't update the bit depth, so if a device
only supported bit depths cubeb doesn't support, so IAudioClient3
streams might fail */
the suggestion, since it seems to always be IEEE_FLOAT. */
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
WAVEFORMATEXTENSIBLE * closest_pcm =
@@ -2122,7 +2049,8 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
}
static int
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client,
AudioClient2Option option)
{
com_ptr<IAudioClient2> audio_client2;
audio_client->QueryInterface<IAudioClient2>(audio_client2.receive());
@@ -2131,10 +2059,14 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
"AUDCLNT_STREAMOPTIONS_RAW.");
return CUBEB_OK;
}
AudioClientProperties properties = {0};
AudioClientProperties properties = {};
properties.cbSize = sizeof(AudioClientProperties);
#ifndef __MINGW32__
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
if (option == CUBEB_AUDIO_CLIENT2_RAW) {
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
} else if (option == CUBEB_AUDIO_CLIENT2_VOICE) {
properties.eCategory = AudioCategory_Communications;
}
#endif
HRESULT hr = audio_client2->SetClientProperties(&properties);
if (FAILED(hr)) {
@@ -2144,12 +2076,12 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
return CUBEB_OK;
}
#if 0
bool
initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
cubeb_stream * stm,
const com_heap_ptr<WAVEFORMATEX> & mix_format,
DWORD flags, EDataFlow direction,
REFERENCE_TIME latency_hns)
DWORD flags, EDataFlow direction)
{
com_ptr<IAudioClient3> audio_client3;
audio_client->QueryInterface<IAudioClient3>(audio_client3.receive());
@@ -2165,22 +2097,24 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
return false;
}
// Some people have reported glitches with capture streams:
// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html
if (direction == eCapture) {
LOG("Audio stream is capture, not using IAudioClient3");
return false;
}
// Possibly initialize a shared-mode stream using IAudioClient3. Initializing
// a stream this way lets you request lower latencies, but also locks the
// global WASAPI engine at that latency.
// - If we request a shared-mode stream, streams created with IAudioClient
// might have their latency adjusted to match. When the shared-mode stream
// is closed, they'll go back to normal.
// - If there's already a shared-mode stream running, if it created with the
// AUDCLNT_STREAMOPTIONS_MATCH_FORMAT option, the audio engine would be
// locked to that format, so we have to match it (a custom one would fail).
// - We don't lock the WASAPI engine to a format, as it's antisocial towards
// other apps, especially if we locked to a latency >= than its default.
// - If the user requested latency is >= the default one, we might still
// accept it (without locking the format) depending on
// ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT, as we might want to prioritize
// to lower our latency over other apps
// (there might still be latency advantages compared to IAudioDevice(1)).
// will
// have their latency adjusted to match. When the shared-mode stream is
// closed, they'll go back to normal.
// - If there's already a shared-mode stream running, then we cannot request
// the engine change to a different latency - we have to match it.
// - It's antisocial to lock the WASAPI engine at its default latency. If we
// would do this, then stop and use IAudioClient instead.
HRESULT hr;
uint32_t default_period = 0, fundamental_period = 0, min_period = 0,
@@ -2192,59 +2126,28 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
LOG("Could not get shared mode engine period: error: %lx", hr);
return false;
}
uint32_t requested_latency =
hns_to_frames(mix_format->nSamplesPerSec, latency_hns);
#if !ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT
uint32_t requested_latency = stm->latency;
if (requested_latency >= default_period) {
LOG("Requested latency %i equal or greater than default latency %i,"
" not using IAudioClient3",
LOG("Requested latency %i greater than default latency %i, not using "
"IAudioClient3",
requested_latency, default_period);
return false;
}
#elif REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX
if (requested_latency > max_period) {
// Fallback to IAudioClient(1) as it's more accepting of large latencies
LOG("Requested latency %i greater than max latency %i,"
" not using IAudioClient3",
requested_latency, max_period);
return false;
}
#endif
LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i",
default_period, fundamental_period, min_period, max_period);
// Snap requested latency to a valid value
uint32_t old_requested_latency = requested_latency;
// The period is required to be a multiple of the fundamental period
// (and >= min and <= max, which should still be true)
requested_latency -= requested_latency % fundamental_period;
if (requested_latency < min_period) {
requested_latency = min_period;
}
// Likely unnecessary, but won't hurt
if (requested_latency > max_period) {
requested_latency = max_period;
}
requested_latency -= (requested_latency - min_period) % fundamental_period;
if (requested_latency != old_requested_latency) {
LOG("Requested latency %i was adjusted to %i", old_requested_latency,
requested_latency);
}
DWORD new_flags = flags;
// Always add these flags to IAudioClient3, they might help
// if the stream doesn't have the same format as the audio engine.
new_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
new_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
hr = audio_client3->InitializeSharedAudioStream(new_flags, requested_latency,
hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency,
mix_format.get(), NULL);
// This error should be returned first even if
// the period was locked (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED)
if (hr == AUDCLNT_E_INVALID_STREAM_FLAG) {
LOG("Got AUDCLNT_E_INVALID_STREAM_FLAG, removing some flags");
hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency,
mix_format.get(), NULL);
}
if (SUCCEEDED(hr)) {
return true;
} else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) {
@@ -2256,37 +2159,22 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
}
uint32_t current_period = 0;
WAVEFORMATEX * current_format_ptr = nullptr;
WAVEFORMATEX * current_format = nullptr;
// We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise
// GetCurrentSharedModeEnginePeriod will return E_POINTER
hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format_ptr,
hr = audio_client3->GetCurrentSharedModeEnginePeriod(&current_format,
&current_period);
CoTaskMemFree(current_format);
if (FAILED(hr)) {
LOG("Could not get current shared mode engine period: error: %lx", hr);
return false;
}
com_heap_ptr<WAVEFORMATEX> current_format(current_format_ptr);
if (current_format->nSamplesPerSec != mix_format->nSamplesPerSec) {
// Unless some other external app locked the shared mode engine period
// within our audio initialization, this is unlikely to happen, though we
// can't respect the user selected latency, so we fallback on IAudioClient
LOG("IAudioClient3::GetCurrentSharedModeEnginePeriod() returned a "
"different mixer format (nSamplesPerSec) from "
"IAudioClient::GetMixFormat(); not using IAudioClient3");
return false;
}
#if REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX
// Reject IAudioClient3 if we can't respect the user target latency.
// We don't need to check against default_latency anymore,
// as the current_period is already the best one we could get.
if (old_requested_latency > current_period) {
LOG("Requested latency %i greater than currently locked shared mode "
"latency %i, not using IAudioClient3",
old_requested_latency, current_period);
if (current_period >= default_period) {
LOG("Current shared mode engine period %i too high, not using IAudioClient",
current_period);
return false;
}
#endif
hr = audio_client3->InitializeSharedAudioStream(flags, current_period,
mix_format.get(), NULL);
@@ -2299,6 +2187,7 @@ initialize_iaudioclient3(com_ptr<IAudioClient> & audio_client,
LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr);
return false;
}
#endif
#define DIRECTION_NAME (direction == eCapture ? "capture" : "render")
@@ -2322,12 +2211,6 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
return CUBEB_ERROR;
}
#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT
constexpr bool allow_audio_client_3 = true;
#else
const bool allow_audio_client_3 = direction == eRender;
#endif
stm->stream_reset_lock.assert_current_thread_owns();
// If user doesn't specify a particular device, we can choose another one when
// the given devid is unavailable.
@@ -2364,14 +2247,17 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
/* Get a client. We will get all other interfaces we need from
* this pointer. */
if (allow_audio_client_3) {
hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
}
if (!allow_audio_client_3 || hr == E_NOINTERFACE) {
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
hr = device->Activate(__uuidof(IAudioClient3),
CLSCTX_INPROC_SERVER,
NULL, audio_client.receive_vpp());
if (hr == E_NOINTERFACE) {
#endif
hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
audio_client.receive_vpp());
#if 0
}
#endif
if (FAILED(hr)) {
LOG("Could not activate the device to get an audio"
@@ -2494,21 +2380,41 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
}
if (stream_params->prefs & CUBEB_STREAM_PREF_RAW) {
if (initialize_iaudioclient2(audio_client) != CUBEB_OK) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_RAW) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else if (direction == eCapture &&
(stream_params->prefs & CUBEB_STREAM_PREF_VOICE) &&
stream_params->input_params != CUBEB_INPUT_PROCESSING_PARAM_NONE) {
if (stream_params->input_params ==
(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION)) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_VOICE) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else {
LOG("Invalid combination of input processing params %#x",
stream_params->input_params);
return CUBEB_ERROR;
}
}
if (allow_audio_client_3 &&
initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction,
latency_hns)) {
#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902
if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) {
LOG("Initialized with IAudioClient3");
} else {
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns,
0, mix_format.get(), NULL);
#endif
hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns, 0,
mix_format.get(), NULL);
#if 0
}
#endif
if (FAILED(hr)) {
LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
@@ -2970,6 +2876,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
}
}
cubeb_async_log_reset_threads();
stm->thread =
(HANDLE)_beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm,
STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
@@ -3031,7 +2938,7 @@ wasapi_stream_add_ref(cubeb_stream * stm)
{
XASSERT(stm);
LONG result = InterlockedIncrement(&stm->ref_count);
LOGV("Stream ref count incremented = %i (%p)", result, stm);
LOGV("Stream ref count incremented = %ld (%p)", result, stm);
return result;
}
@@ -3041,7 +2948,7 @@ wasapi_stream_release(cubeb_stream * stm)
XASSERT(stm);
LONG result = InterlockedDecrement(&stm->ref_count);
LOGV("Stream ref count decremented = %i (%p)", result, stm);
LOGV("Stream ref count decremented = %ld (%p)", result, stm);
if (result == 0) {
LOG("Stream ref count hit zero, destroying (%p)", stm);
@@ -3303,7 +3210,7 @@ wasapi_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_OK;
}
static char const *
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str)
{
int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL);
@@ -3311,8 +3218,8 @@ wstr_to_utf8(LPCWSTR str)
return nullptr;
}
char * ret = static_cast<char *>(malloc(size));
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
std::unique_ptr<char[]> ret(new char[size]);
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret.get(), size, NULL, NULL);
return ret;
}
@@ -3440,7 +3347,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant namevar;
hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
if (SUCCEEDED(hr) && namevar.vt == VT_LPWSTR) {
ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
ret.friendly_name = wstr_to_utf8(namevar.pwszVal).release();
}
if (!ret.friendly_name) {
// This is not fatal, but a valid string is expected in all cases.
@@ -3461,7 +3368,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant instancevar;
hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
if (SUCCEEDED(hr) && instancevar.vt == VT_LPWSTR) {
ret.group_id = wstr_to_utf8(instancevar.pwszVal);
ret.group_id = wstr_to_utf8(instancevar.pwszVal).release();
}
}
@@ -3477,7 +3384,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA |
CUBEB_DEVICE_PREF_NOTIFICATION);
} else if (defaults->is_default(flow, eCommunications, device_id.get())) {
}
if (defaults->is_default(flow, eCommunications, device_id.get())) {
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
}
@@ -3504,7 +3412,6 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
CUBEB_DEVICE_FMT_S16NE);
ret.default_format = CUBEB_DEVICE_FMT_F32NE;
prop_variant fmtvar;
WAVEFORMATEX * wfx = NULL;
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
@@ -3514,7 +3421,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
ret.max_channels = pcm->wf.nChannels;
} else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
wfx = reinterpret_cast<WAVEFORMATEX *>(fmtvar.blob.pBlobData);
WAVEFORMATEX * wfx =
reinterpret_cast<WAVEFORMATEX *>(fmtvar.blob.pBlobData);
if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
wfx->wFormatTag == WAVE_FORMAT_PCM) {
@@ -3524,30 +3432,9 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
}
}
#if USE_AUDIO_CLIENT_3_MIN_PERIOD
// Here we assume an IAudioClient3 stream will successfully
// be initialized later (it might fail)
#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT
constexpr bool allow_audio_client_3 = true;
#else
const bool allow_audio_client_3 = flow == eRender;
#endif
com_ptr<IAudioClient3> client3;
uint32_t def, fun, min, max;
if (allow_audio_client_3 && wfx &&
SUCCEEDED(dev->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER,
NULL, client3.receive_vpp())) &&
SUCCEEDED(
client3->GetSharedModeEnginePeriod(wfx, &def, &fun, &min, &max))) {
ret.latency_lo = min;
// This latency might actually be used as "default" and not "max" later on,
// so we return the default (we never really want to use the max anyway)
ret.latency_hi = def;
} else
#endif
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
} else {
@@ -3638,7 +3525,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
{
return wasapi_enumerate_devices_internal(
context, type, out,
DEVICE_STATE_ACTIVE /*| DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED*/);
DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED);
}
static int
@@ -3656,6 +3543,14 @@ wasapi_device_collection_destroy(cubeb * /*ctx*/,
return CUBEB_OK;
}
int
wasapi_set_input_processing_params(cubeb_stream * stream,
cubeb_input_processing_params params)
{
LOG("Cannot set voice processing params after init. Use cubeb_stream_init.");
return CUBEB_ERROR_NOT_SUPPORTED;
}
static int
wasapi_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
@@ -3736,7 +3631,8 @@ cubeb_ops const wasapi_ops = {
/*.get_max_channel_count =*/wasapi_get_max_channel_count,
/*.get_min_latency =*/wasapi_get_min_latency,
/*.get_preferred_sample_rate =*/wasapi_get_preferred_sample_rate,
/*.get_supported_input_processing_params =*/NULL,
/*.get_supported_input_processing_params =*/
wasapi_get_supported_input_processing_params,
/*.enumerate_devices =*/wasapi_enumerate_devices,
/*.device_collection_destroy =*/wasapi_device_collection_destroy,
/*.destroy =*/wasapi_destroy,
@@ -3751,7 +3647,7 @@ cubeb_ops const wasapi_ops = {
/*.stream_set_name =*/NULL,
/*.stream_get_current_device =*/NULL,
/*.stream_set_input_mute =*/NULL,
/*.stream_set_input_processing_params =*/NULL,
/*.stream_set_input_processing_params =*/wasapi_set_input_processing_params,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback =*/NULL,
/*.register_device_collection_changed =*/
+8 -11
View File
@@ -41,10 +41,10 @@
#ifdef FLOATING_POINT
#error You cannot compile as floating point and fixed point at the same time
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#error SSE is only for floating-point
#endif
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#if defined(ARM4_ASM) + defined(ARM5E_ASM) + defined(BFIN_ASM) > 1
#error Make up your mind. What CPU do you have?
#endif
#ifdef VORBIS_PSYCHO
@@ -56,10 +56,10 @@
#ifndef FLOATING_POINT
#error You now need to define either FIXED_POINT or FLOATING_POINT
#endif
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#if defined(ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#ifdef FIXED_POINT_DEBUG
#ifdef FIXED_DEBUG
#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
#endif
@@ -117,9 +117,9 @@ typedef spx_word32_t spx_sig_t;
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#elif defined(ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#elif defined(BFIN_ASM)
#include "fixed_bfin.h"
#endif
@@ -177,16 +177,13 @@ typedef float spx_word32_t;
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_32_32(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
@@ -210,7 +207,7 @@ typedef float spx_word32_t;
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
#if defined(CONFIG_TI_C54X) || defined(CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2
+6 -10
View File
@@ -69,22 +69,18 @@
/* result fits in 16 bits */
#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
#define MULT16_16_16(a,b) (((spx_word16_t)(a))*((spx_word16_t)(b)))
/* result fits in 32 bits */
#define MULT16_32_32(a,b) (((spx_word16_t)(a))*((spx_word32_t)(b)))
/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
#define MULT16_32_P15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,MULT16_32_Q15(a,b))
#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))
+46 -44
View File
@@ -46,7 +46,7 @@
Smith, Julius O. Digital Audio Resampling Home Page
Center for Computer Research in Music and Acoustics (CCRMA),
Stanford University, 2007.
Web published at http://ccrma.stanford.edu/~jos/resample/.
Web published at https://ccrma.stanford.edu/~jos/resample/.
There is one main difference, though. This resampler uses cubic
interpolation instead of linear interpolation in the above paper. This
@@ -63,9 +63,12 @@
#ifdef OUTSIDE_SPEEX
#include <stdlib.h>
static void *speex_alloc (int size) {return calloc(size,1);}
static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
static void speex_free (void *ptr) {free(ptr);}
static void *speex_alloc(int size) {return calloc(size,1);}
static void *speex_realloc(void *ptr, int size) {return realloc(ptr, size);}
static void speex_free(void *ptr) {free(ptr);}
#ifndef EXPORT
#define EXPORT
#endif
#include "speex_resampler.h"
#include "arch.h"
#else /* OUTSIDE_SPEEX */
@@ -75,7 +78,6 @@ static void speex_free (void *ptr) {free(ptr);}
#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include "stack_alloc.h"
#include <math.h>
#include <limits.h>
@@ -91,18 +93,18 @@ static void speex_free (void *ptr) {free(ptr);}
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967296U
#define UINT32_MAX 4294967295U
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#include "resample_sse.h"
#endif
#ifdef _USE_NEON
#ifdef USE_NEON
#include "resample_neon.h"
#endif
/* Numer of elements to allocate on the stack */
/* Number of elements to allocate on the stack */
#ifdef VAR_ARRAYS
#define FIXED_STACK_ALLOC 8192
#else
@@ -194,16 +196,14 @@ struct FuncDef {
int oversample;
};
static const struct FuncDef _KAISER12 = {kaiser12_table, 64};
#define KAISER12 (&_KAISER12)
/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
#define KAISER12 (&_KAISER12)*/
static const struct FuncDef _KAISER10 = {kaiser10_table, 32};
#define KAISER10 (&_KAISER10)
static const struct FuncDef _KAISER8 = {kaiser8_table, 32};
#define KAISER8 (&_KAISER8)
static const struct FuncDef _KAISER6 = {kaiser6_table, 32};
#define KAISER6 (&_KAISER6)
static const struct FuncDef kaiser12_funcdef = {kaiser12_table, 64};
#define KAISER12 (&kaiser12_funcdef)
static const struct FuncDef kaiser10_funcdef = {kaiser10_table, 32};
#define KAISER10 (&kaiser10_funcdef)
static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32};
#define KAISER8 (&kaiser8_funcdef)
static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32};
#define KAISER6 (&kaiser6_funcdef)
struct QualityMapping {
int base_length;
@@ -473,7 +473,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
sum = SATURATE32PSHR(sum, 15, 32767);
#else
cubic_coef(frac, interp);
@@ -572,6 +572,7 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
(void)in;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
out[out_stride * out_sample++] = 0;
@@ -589,16 +590,15 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
return out_sample;
}
static int _muldiv(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t mul, spx_uint32_t div)
static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den)
{
speex_assert(result);
spx_uint32_t major = value / div;
spx_uint32_t remainder = value % div;
spx_uint32_t major = value / den;
spx_uint32_t remain = value % den;
/* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
if (remainder > UINT32_MAX / mul || major > UINT32_MAX / mul
|| major * mul > UINT32_MAX - remainder * mul / div)
if (remain > UINT32_MAX / num || major > UINT32_MAX / num
|| major * num > UINT32_MAX - remain * num / den)
return RESAMPLER_ERR_OVERFLOW;
*result = remainder * mul / div + major * mul;
*result = remain * num / den + major * num;
return RESAMPLER_ERR_SUCCESS;
}
@@ -619,7 +619,7 @@ static int update_filter(SpeexResamplerState *st)
{
/* down-sampling */
st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
if (_muldiv(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
goto fail;
/* Round up to make sure we have a multiple of 8 for SSE */
st->filt_len = ((st->filt_len-1)&(~0x7))+8;
@@ -638,12 +638,12 @@ static int update_filter(SpeexResamplerState *st)
st->cutoff = quality_map[st->quality].upsample_bandwidth;
}
/* Choose the resampling type that requires the least amount of memory */
#ifdef RESAMPLE_FULL_SINC_TABLE
use_direct = 1;
if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
goto fail;
#else
/* Choose the resampling type that requires the least amount of memory */
use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8
&& INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len;
#endif
@@ -733,16 +733,18 @@ static int update_filter(SpeexResamplerState *st)
{
spx_uint32_t j;
spx_uint32_t olen = old_length;
spx_uint32_t start = i*st->mem_alloc_size;
spx_uint32_t magic_samples = st->magic_samples[i];
/*if (st->magic_samples[i])*/
{
/* Try and remove the magic samples as if nothing had happened */
/* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
olen = old_length + 2*st->magic_samples[i];
for (j=old_length-1+st->magic_samples[i];j--;)
st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
for (j=0;j<st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = 0;
olen = old_length + 2*magic_samples;
for (j=old_length-1+magic_samples;j--;)
st->mem[start+j+magic_samples] = st->mem[i*old_alloc_size+j];
for (j=0;j<magic_samples;j++)
st->mem[start+j] = 0;
st->magic_samples[i] = 0;
}
if (st->filt_len > olen)
@@ -750,17 +752,18 @@ static int update_filter(SpeexResamplerState *st)
/* If the new filter length is still bigger than the "augmented" length */
/* Copy data going backward */
for (j=0;j<olen-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
st->mem[start+(st->filt_len-2-j)] = st->mem[start+(olen-2-j)];
/* Then put zeros for lack of anything better */
for (;j<st->filt_len-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
st->mem[start+(st->filt_len-2-j)] = 0;
/* Adjust last_sample */
st->last_sample[i] += (st->filt_len - olen)/2;
} else {
/* Put back some of the magic! */
st->magic_samples[i] = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
magic_samples = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+magic_samples;j++)
st->mem[start+j] = st->mem[start+j+magic_samples];
st->magic_samples[i] = magic_samples;
}
}
} else if (st->filt_len < old_length)
@@ -977,8 +980,7 @@ EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t cha
const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
#ifdef VAR_ARRAYS
const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
VARDECL(spx_word16_t *ystack);
ALLOC(ystack, ylen, spx_word16_t);
spx_word16_t ystack[ylen];
#else
const unsigned int ylen = FIXED_STACK_ALLOC;
spx_word16_t ystack[FIXED_STACK_ALLOC];
@@ -1093,7 +1095,7 @@ EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_r
*out_rate = st->out_rate;
}
static inline spx_uint32_t _gcd(spx_uint32_t a, spx_uint32_t b)
static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b)
{
while (b != 0)
{
@@ -1123,7 +1125,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
st->num_rate = ratio_num;
st->den_rate = ratio_den;
fact = _gcd (st->num_rate, st->den_rate);
fact = compute_gcd(st->num_rate, st->den_rate);
st->num_rate /= fact;
st->den_rate /= fact;
@@ -1132,7 +1134,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
{
for (i=0;i<st->nb_channels;i++)
{
if (_muldiv(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
return RESAMPLER_ERR_OVERFLOW;
/* Safety net */
if (st->samp_frac_num[i] >= st->den_rate)
+154 -14
View File
@@ -36,14 +36,26 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <arm_neon.h>
#include <stdint.h>
#ifdef FIXED_POINT
#ifdef __thumb2__
#if defined(__aarch64__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("fmov s0, %w[a]\n"
"sqxtn h0, s0\n"
"sxtl v0.4s, v0.4h\n"
"fmov %w[ret], s0\n"
: [ret] "=r" (ret)
: [a] "r" (a)
: "v0" );
return ret;
}
#elif defined(__thumb2__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("ssat %[ret], #16, %[a]"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: );
return ret;
@@ -54,7 +66,7 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
asm ("vmov.s32 d0[0], %[a]\n"
"vqmovn.s32 d0, q0\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
@@ -64,7 +76,63 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
#define WORD2INT(x) (saturate_32bit_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4h}, [%[b]], #8\n"
" ld1 {v20.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smull v0.4s, v16.4h, v20.4h\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smull v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smlal v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.ne 2b\n"
"3:"
" cmp %w[remainder], #0\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4h}, [%[b]], #8\n"
" ld1 {v22.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" b.ne 4b\n"
"5:"
" saddlv d0, v0.4s\n"
" sqxtn s0, d0\n"
" sqrshrn h0, s0, #15\n"
" sxtl v0.4s, v0.4h\n"
" fmov %w[ret], s0\n"
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v0",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
@@ -112,33 +180,104 @@ static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, u
" vqmovn.s64 d0, q0\n"
" vqrshrn.s32 d0, q0, #15\n"
" vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "q0",
"d16", "d17", "d18", "d19",
"d20", "d21", "d22", "d23");
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23");
return ret;
}
#elif defined(FLOATING_POINT)
#endif // !defined(__aarch64__)
#elif defined(FLOATING_POINT)
#if defined(__aarch64__)
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("fcvtas s1, %s[a]\n"
"sqxtn h1, s1\n"
"sxtl v1.4s, v1.4h\n"
"fmov %w[ret], s1\n"
: [ret] "=r" (ret)
: [a] "w" (a)
: "v1");
return ret;
}
#else
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("vmov.f32 d0[0], %[a]\n"
"vcvt.s32.f32 d0, d0, #15\n"
"vqrshrn.s32 d0, q0, #15\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
}
#endif
#undef WORD2INT
#define WORD2INT(x) (saturate_float_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4s}, [%[b]], #16\n"
" ld1 {v20.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" fmul v2.4s, v17.4s, v21.4s\n"
" fmul v3.4s, v18.4s, v22.4s\n"
" fmul v4.4s, v19.4s, v23.4s\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmla v1.4s, v16.4s, v20.4s\n"
" fmla v2.4s, v17.4s, v21.4s\n"
" fmla v3.4s, v18.4s, v22.4s\n"
" fmla v4.4s, v19.4s, v23.4s\n"
" b.ne 2b\n"
"3:"
" fadd v16.4s, v1.4s, v2.4s\n"
" fadd v17.4s, v3.4s, v4.4s\n"
" cmp %w[remainder], #0\n"
" fadd v1.4s, v16.4s, v17.4s\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4s}, [%[b]], #16\n"
" ld1 {v22.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmla v1.4s, v18.4s, v22.4s\n"
" b.ne 4b\n"
"5:"
" faddp v1.4s, v1.4s, v1.4s\n"
" faddp %[ret].4s, v1.4s, v1.4s\n"
: [ret] "=w" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v1", "v2", "v3", "v4",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
@@ -191,11 +330,12 @@ static inline float inner_product_single(const float *a, const float *b, unsigne
" vadd.f32 d0, d0, d1\n"
" vpadd.f32 d0, d0, d0\n"
" vmov.f32 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+l" (len), [remainder] "+l" (remainder)
:
: "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q10", "q11");
: "cc", "q0", "q1", "q2", "q3",
"q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11");
return ret;
}
#endif // defined(__aarch64__)
#endif
+1 -1
View File
@@ -71,7 +71,7 @@ static inline float interpolate_product_single(const float *a, const float *b, u
return ret;
}
#ifdef _USE_SSE2
#ifdef USE_SSE2
#include <emmintrin.h>
#define OVERRIDE_INNER_PRODUCT_DOUBLE
+181 -6
View File
@@ -1,3 +1,176 @@
# 12.0.0 - 2025-09-17
- Optimized the default floating point formatting
(https://github.com/fmtlib/fmt/issues/3675,
https://github.com/fmtlib/fmt/issues/4516). In particular, formatting a
`double` with format string compilation into a stack allocated buffer is
more than 60% faster in version 12.0 compared to 11.2 according to
[dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark):
```
Function Time (ns) Speedup
fmt11 34.471 1.00x
fmt12 21.000 1.64x
```
<img width="766" height="609" src="https://github.com/user-attachments/assets/d7d768ad-7543-468c-b0bb-449abf73b31b" />
- Added `constexpr` support to `fmt::format`. For example:
```c++
#include <fmt/compile.h>
using namespace fmt::literals;
std::string s = fmt::format(""_cf, 42);
```
now works at compile time provided that `std::string` supports `constexpr`
(https://github.com/fmtlib/fmt/issues/3403,
https://github.com/fmtlib/fmt/pull/4456). Thanks @msvetkin.
- Added `FMT_STATIC_FORMAT` that allows formatting into a string of the exact
required size at compile time.
For example:
```c++
#include <fmt/compile.h>
constexpr auto s = FMT_STATIC_FORMAT("{}", 42);
```
compiles to just
```s
__ZL1s:
.asciiz "42"
```
It can be accessed as a C string with `s.c_str()` or as a string view with
`s.str()`.
- Improved C++20 module support
(https://github.com/fmtlib/fmt/pull/4451,
https://github.com/fmtlib/fmt/pull/4459,
https://github.com/fmtlib/fmt/pull/4476,
https://github.com/fmtlib/fmt/pull/4488,
https://github.com/fmtlib/fmt/issues/4491,
https://github.com/fmtlib/fmt/pull/4495).
Thanks @arBmind, @tkhyn, @Mishura4, @anonymouspc and @autoantwort.
- Switched to using estimated display width in precision. For example:
```c++
fmt::print("|{:.4}|\n|1234|\n", "🐱🐱🐱");
```
prints
![](https://github.com/user-attachments/assets/6c4446b3-13eb-43b9-b74a-b4543540ad6a)
because `🐱` has an estimated width of 2
(https://github.com/fmtlib/fmt/issues/4272,
https://github.com/fmtlib/fmt/pull/4443,
https://github.com/fmtlib/fmt/pull/4475).
Thanks @nikhilreddydev and @localspook.
- Fix interaction between debug presentation, precision, and width for strings
(https://github.com/fmtlib/fmt/pull/4478). Thanks @localspook.
- Implemented allocator propagation on `basic_memory_buffer` move
(https://github.com/fmtlib/fmt/issues/4487,
https://github.com/fmtlib/fmt/pull/4490). Thanks @toprakmurat.
- Fixed an ambiguity between `std::reference_wrapper<T>` and `format_as`
formatters (https://github.com/fmtlib/fmt/issues/4424,
https://github.com/fmtlib/fmt/pull/4434). Thanks @jeremy-rifkin.
- Removed the following deprecated APIs:
- `has_formatter`: use `is_formattable` instead,
- `basic_format_args::parse_context_type`,
`basic_format_args::formatter_type` and similar aliases in context types,
- wide stream overload of `fmt::printf`,
- wide stream overloads of `fmt::print` that take text styles,
- `is_*char` traits,
- `fmt::localtime`.
- Deprecated wide overloads of `fmt::fprintf` and `fmt::sprintf`.
- Improved diagnostics for the incorrect usage of `fmt::ptr`
(https://github.com/fmtlib/fmt/pull/4453). Thanks @TobiSchluter.
- Made handling of ANSI escape sequences more efficient
(https://github.com/fmtlib/fmt/pull/4511,
https://github.com/fmtlib/fmt/pull/4528).
Thanks @localspook and @Anas-Hamdane.
- Fixed a buffer overflow on all emphasis flags set
(https://github.com/fmtlib/fmt/pull/4498). Thanks @dominicpoeschko.
- Fixed an integer overflow for precision close to the max `int` value.
- Fixed compatibility with WASI (https://github.com/fmtlib/fmt/issues/4496,
https://github.com/fmtlib/fmt/pull/4497). Thanks @whitequark.
- Fixed `back_insert_iterator` detection, preventing a fallback on slower path
that handles arbitrary iterators (https://github.com/fmtlib/fmt/issues/4454).
- Fixed handling of invalid glibc `FILE` buffers
(https://github.com/fmtlib/fmt/issues/4469).
- Added `wchar_t` support to the `std::byte` formatter
(https://github.com/fmtlib/fmt/issues/4479,
https://github.com/fmtlib/fmt/pull/4480). Thanks @phprus.
- Changed component prefix from `fmt-` to `fmt_` for compatibility with
NSIS/CPack on Windows, e.g. `fmt-doc` changed to `fmt_doc`
(https://github.com/fmtlib/fmt/issues/4441,
https://github.com/fmtlib/fmt/pull/4442). Thanks @n-stein.
- Added the `FMT_CUSTOM_ASSERT_FAIL` macro to simplify providing a custom
`fmt::assert_fail` implementation (https://github.com/fmtlib/fmt/pull/4505).
Thanks @HazardyKnusperkeks.
- Switched to `FMT_THROW` on reporting format errors so that it can be
overriden by users when exceptions are disabled
(https://github.com/fmtlib/fmt/pull/4521). Thanks @HazardyKnusperkeks.
- Improved master project detection and disabled install targets when using
{fmt} as a subproject by default (https://github.com/fmtlib/fmt/pull/4536).
Thanks @crueter.
- Made various code improvements
(https://github.com/fmtlib/fmt/pull/4445,
https://github.com/fmtlib/fmt/pull/4448,
https://github.com/fmtlib/fmt/pull/4473,
https://github.com/fmtlib/fmt/pull/4522).
Thanks @localspook, @tchaikov and @way4sahil.
- Added Conan instructions to the docs
(https://github.com/fmtlib/fmt/pull/4537). Thanks @uilianries.
- Removed Bazel files to avoid issues with downstream packaging
(https://github.com/fmtlib/fmt/pull/4530). Thanks @mering.
- Added more entries for generated files to `.gitignore`
(https://github.com/fmtlib/fmt/pull/4355,
https://github.com/fmtlib/fmt/pull/4512).
Thanks @dinomight and @localspook.
- Fixed various warnings and compilation issues
(https://github.com/fmtlib/fmt/pull/4447,
https://github.com/fmtlib/fmt/issues/4470,
https://github.com/fmtlib/fmt/pull/4474,
https://github.com/fmtlib/fmt/pull/4477,
https://github.com/fmtlib/fmt/pull/4471,
https://github.com/fmtlib/fmt/pull/4483,
https://github.com/fmtlib/fmt/pull/4515,
https://github.com/fmtlib/fmt/issues/4533,
https://github.com/fmtlib/fmt/pull/4534).
Thanks @dodomorandi, @localspook, @remyjette, @Tomek-Stolarczyk, @Mishura4,
@mattiasljungstrom and @FatihBAKIR.
# 11.2.0 - 2025-05-03
- Added the `s` specifier for `std::error_code`. It allows formatting an error
@@ -56,17 +229,18 @@
https://github.com/fmtlib/fmt/pull/4361). Thanks @dinomight.
- Added error reporting for duplicate named arguments
(https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
(https://github.com/fmtlib/fmt/issues/4282,
https://github.com/fmtlib/fmt/pull/4367). Thanks @dinomight.
- Fixed formatting of `long` with `FMT_BUILTIN_TYPES=0`
(https://github.com/fmtlib/fmt/issues/4375,
https://github.com/fmtlib/fmt/issues/4394).
- Optimized `text_style` using bit packing
(https://github.com/fmtlib/fmt/pull/4363). Thanks @LocalSpook.
(https://github.com/fmtlib/fmt/pull/4363). Thanks @localspook.
- Added support for incomplete types (https://github.com/fmtlib/fmt/issues/3180,
https://github.com/fmtlib/fmt/pull/4383). Thanks @LocalSpook.
https://github.com/fmtlib/fmt/pull/4383). Thanks @localspook.
- Fixed a flush issue in `fmt::print` when using libstdc++
(https://github.com/fmtlib/fmt/issues/4398).
@@ -107,13 +281,14 @@
`float` (https://github.com/fmtlib/fmt/issues/3649).
- Moved `is_compiled_string` to the public API
(https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
(https://github.com/fmtlib/fmt/issues/4335,
https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
- Simplified implementation of `operator""_cf`
(https://github.com/fmtlib/fmt/pull/4349). Thanks @LocalSpook.
(https://github.com/fmtlib/fmt/pull/4349). Thanks @localspook.
- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
Thanks @LocalSpook.
Thanks @localspook.
- Fixed handling of BMI paths with the Ninja generator
(https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.
+2 -1
View File
@@ -4,8 +4,9 @@
[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
+2 -2
View File
@@ -71,7 +71,7 @@ class dynamic_arg_list {
* It can be implicitly converted into `fmt::basic_format_args` for passing
* into type-erased formatting functions such as `fmt::vformat`.
*/
template <typename Context> class dynamic_format_arg_store {
FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
private:
using char_type = typename Context::char_type;
@@ -212,7 +212,7 @@ template <typename Context> class dynamic_format_arg_store {
}
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
auto size() const noexcept -> size_t { return data_.size(); }
};
FMT_END_NAMESPACE
+92 -87
View File
@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110200
#define FMT_VERSION 120000
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -201,14 +201,6 @@
# define FMT_NODISCARD
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
@@ -260,7 +252,7 @@ FMT_PRAGMA_CLANG(diagnostic push)
#ifndef FMT_BEGIN_NAMESPACE
# define FMT_BEGIN_NAMESPACE \
namespace fmt { \
inline namespace v11 {
inline namespace v12 {
# define FMT_END_NAMESPACE \
} \
}
@@ -356,6 +348,9 @@ template <typename T> constexpr auto max_of(T a, T b) -> T {
return a > b ? a : b;
}
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
namespace detail {
// Suppresses "unused variable" warnings with the method described in
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
@@ -396,7 +391,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: fmt::detail::assert_fail(__FILE__, __LINE__, (message)))
: ::fmt::assert_fail(__FILE__, __LINE__, (message)))
#endif
#ifdef FMT_USE_INT128
@@ -463,12 +458,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled };
static_assert(!FMT_UNICODE || use_utf8,
"Unicode support requires compiling with /utf-8");
template <typename T> constexpr const char* narrow(const T*) { return nullptr; }
constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; }
template <typename T> constexpr auto narrow(T*) -> char* { return nullptr; }
constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* {
return s;
}
template <typename Char>
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n)
-> int {
FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int {
if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n);
for (; n != 0; ++s1, ++s2, --n) {
if (*s1 < *s2) return -1;
@@ -540,7 +536,7 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr.
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr.
return;
}
#endif
@@ -616,19 +612,6 @@ template <typename Char> class basic_string_view {
using string_view = basic_string_view<char>;
// DEPRECATED! Will be merged with is_char and moved to detail.
template <typename T> struct is_xchar : std::false_type {};
template <> struct is_xchar<wchar_t> : std::true_type {};
template <> struct is_xchar<char16_t> : std::true_type {};
template <> struct is_xchar<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_xchar<char8_t> : std::true_type {};
#endif
// Specifies if `T` is a character (code unit) type.
template <typename T> struct is_char : is_xchar<T> {};
template <> struct is_char<char> : std::true_type {};
template <typename T> class basic_appender;
using appender = basic_appender<char>;
@@ -781,7 +764,7 @@ class basic_specs {
(static_cast<unsigned>(p) << precision_shift);
}
constexpr bool dynamic() const {
constexpr auto dynamic() const -> bool {
return (data_ & (width_mask | precision_mask)) != 0;
}
@@ -921,14 +904,47 @@ template <typename Char = char> class parse_context {
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to std::locale to avoid the heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
FMT_END_EXPORT
namespace detail {
// Specifies if `T` is a code unit type.
template <typename T> struct is_code_unit : std::false_type {};
template <> struct is_code_unit<char> : std::true_type {};
template <> struct is_code_unit<wchar_t> : std::true_type {};
template <> struct is_code_unit<char16_t> : std::true_type {};
template <> struct is_code_unit<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_code_unit<char8_t> : bool_constant<is_utf8_enabled> {};
#endif
// Constructs fmt::basic_string_view<Char> from types implicitly convertible
// to it, deducing Char. Explicitly convertible types such as the ones returned
// from FMT_STRING are intentionally excluded.
template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
template <typename Char, FMT_ENABLE_IF(is_code_unit<Char>::value)>
constexpr auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s;
}
@@ -1057,11 +1073,11 @@ template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
template <typename... Args> constexpr auto count_named_args() -> int {
return count<is_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_named_args() -> int {
return count<is_named_arg<T>::value...>();
}
template <typename... Args> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<Args>::value...>();
template <typename... T> constexpr auto count_static_named_args() -> int {
return count<is_static_named_arg<T>::value...>();
}
template <typename Char> struct named_arg_info {
@@ -1069,7 +1085,7 @@ template <typename Char> struct named_arg_info {
int id;
};
// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13.
// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13.
template <typename Char>
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
int named_arg_index,
@@ -1173,7 +1189,7 @@ template <typename Char> struct type_mapper {
static auto map(ubitint<N>)
-> conditional_t<N <= 64, unsigned long long, void>;
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
static auto map(T) -> conditional_t<
std::is_same<T, char>::value || std::is_same<T, Char>::value, Char, void>;
@@ -1679,12 +1695,12 @@ template <typename... T> struct arg_pack {};
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
class format_string_checker {
private:
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
type types_[max_of<size_t>(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of<size_t>(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
parse_func parse_funcs_[max_of<size_t>(1, NUM_ARGS)];
public:
template <typename... T>
@@ -2033,6 +2049,17 @@ struct has_back_insert_iterator_container_append<
.append(std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
template <typename OutputIt, typename InputIt, typename = void>
struct has_back_insert_iterator_container_insert_at_end : std::false_type {};
template <typename OutputIt, typename InputIt>
struct has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt,
void_t<decltype(get_container(std::declval<OutputIt>())
.insert(get_container(std::declval<OutputIt>()).end(),
std::declval<InputIt>(),
std::declval<InputIt>()))>> : std::true_type {};
// An optimized version of std::copy with the output value type (T).
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
@@ -2047,6 +2074,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
!has_back_insert_iterator_container_append<
OutputIt, InputIt>::value &&
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)>
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
-> OutputIt {
@@ -2056,7 +2085,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
}
template <typename T, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
FMT_ENABLE_IF(!(is_back_insert_iterator<OutputIt>::value &&
(has_back_insert_iterator_container_append<
OutputIt, InputIt>::value ||
has_back_insert_iterator_container_insert_at_end<
OutputIt, InputIt>::value)))>
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
while (begin != end) *out++ = static_cast<T>(*begin++);
return out;
@@ -2176,7 +2209,7 @@ template <typename Context> class value {
static_assert(N <= 64, "unsupported _BitInt");
}
template <typename T, FMT_ENABLE_IF(is_char<T>::value)>
template <typename T, FMT_ENABLE_IF(is_code_unit<T>::value)>
constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) {
static_assert(
std::is_same<T, char>::value || std::is_same<T, char_type>::value,
@@ -2252,7 +2285,7 @@ template <typename Context> class value {
custom.value = const_cast<value_type*>(&x);
#endif
}
custom.format = format_custom<value_type, formatter<value_type, char_type>>;
custom.format = format_custom<value_type>;
}
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
@@ -2263,10 +2296,10 @@ template <typename Context> class value {
}
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
template <typename T>
static void format_custom(void* arg, parse_context<char_type>& parse_ctx,
Context& ctx) {
auto f = Formatter();
auto f = formatter<T, char_type>();
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_formatter<const T, char_type>(), const T, T>;
@@ -2293,35 +2326,14 @@ struct is_output_iterator<
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
T>::value>> : std::true_type {};
#ifndef FMT_USE_LOCALE
# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1)
#endif
// A type-erased reference to an std::locale to avoid a heavy <locale> include.
class locale_ref {
#if FMT_USE_LOCALE
private:
const void* locale_; // A type-erased pointer to std::locale.
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> locale_ref(const Locale& loc);
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif // FMT_USE_LOCALE
public:
template <typename Locale> auto get() const -> Locale;
};
template <typename> constexpr auto encode_types() -> unsigned long long {
return 0;
}
template <typename Context, typename Arg, typename... Args>
template <typename Context, typename First, typename... T>
constexpr auto encode_types() -> unsigned long long {
return static_cast<unsigned>(stored_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
return static_cast<unsigned>(stored_type_constant<First, Context>::value) |
(encode_types<Context, T...>() << packed_arg_bits);
}
template <typename Context, typename... T, size_t NUM_ARGS = sizeof...(T)>
@@ -2338,8 +2350,9 @@ template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
unsigned long long DESC>
struct named_arg_store {
// args_[0].named_args points to named_args to avoid bloating format_args.
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
arg_t<Context, NUM_ARGS> args[1u + NUM_ARGS];
named_arg_info<typename Context::char_type>
named_args[static_cast<size_t>(NUM_NAMED_ARGS)];
template <typename... T>
FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values)
@@ -2358,8 +2371,8 @@ struct named_arg_store {
}
named_arg_store(const named_arg_store& rhs) = delete;
named_arg_store& operator=(const named_arg_store& rhs) = delete;
named_arg_store& operator=(named_arg_store&& rhs) = delete;
auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete;
auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete;
operator const arg_t<Context, NUM_ARGS>*() const { return args + 1; }
};
@@ -2372,7 +2385,7 @@ struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
conditional_t<NUM_NAMED_ARGS == 0,
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
arg_t<Context, NUM_ARGS>[max_of<size_t>(1, NUM_ARGS)],
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
type args;
};
@@ -2656,22 +2669,17 @@ class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
FMT_NO_UNIQUE_ADDRESS locale_ref loc_;
public:
/// The character type for the output.
using char_type = char;
using char_type = char; ///< The character type for the output.
using iterator = appender;
using format_arg = basic_format_arg<context>;
using parse_context_type FMT_DEPRECATED = parse_context<>;
template <typename T> using formatter_type FMT_DEPRECATED = formatter<T>;
enum { builtin_types = FMT_BUILTIN_TYPES };
/// Constructs a `context` object. References to the arguments are stored
/// in the object so make sure they have appropriate lifetimes.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {})
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
@@ -2692,7 +2700,7 @@ class context {
// Advances the begin iterator to `it`.
FMT_CONSTEXPR void advance_to(iterator) {}
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; }
};
template <typename Char = char> struct runtime_format_string {
@@ -2779,9 +2787,6 @@ template <typename T, typename Char = char>
concept formattable = is_formattable<remove_reference_t<T>, Char>::value;
#endif
template <typename T, typename Char>
using has_formatter FMT_DEPRECATED = std::is_constructible<formatter<T, Char>>;
// A formatter specialization for natively supported types.
template <typename T, typename Char>
struct formatter<T, Char,
@@ -2978,9 +2983,9 @@ FMT_INLINE void println(format_string<T...> fmt, T&&... args) {
return fmt::println(stdout, fmt, static_cast<T&&>(args)...);
}
FMT_END_EXPORT
FMT_PRAGMA_CLANG(diagnostic pop)
FMT_PRAGMA_GCC(pop_options)
FMT_END_EXPORT
FMT_END_NAMESPACE
#ifdef FMT_HEADER_ONLY
+12 -101
View File
@@ -38,6 +38,7 @@ FMT_BEGIN_NAMESPACE
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
// DEPRECATED!
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
@@ -161,17 +162,6 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
@@ -282,8 +272,6 @@ namespace detail {
#define FMT_NOMACRO
template <typename T = void> struct null {};
inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); }
@@ -326,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& {
}
template <typename CodeUnit> struct codecvt_result {
static constexpr const size_t max_size = 32;
static constexpr size_t max_size = 32;
CodeUnit buf[max_size];
CodeUnit* end;
};
@@ -443,11 +431,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
using common_rep = typename std::common_type<FromRep, typename To::rep,
decltype(factor::num)>::type;
int ec = 0;
auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
from.count(), ec);
if (ec) throw_duration_error();
common_rep count = from.count(); // This conversion is lossless.
// Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) {
@@ -458,6 +442,7 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
count *= factor::num;
}
if (const_check(factor::den != 1)) count /= factor::den;
int ec = 0;
auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
count, ec));
@@ -471,6 +456,8 @@ template <typename To, typename FromRep, typename FromPeriod,
std::is_floating_point<typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
#if FMT_SAFE_DURATION_CAST
// Preserve infinity and NaN.
if (!isfinite(from.count())) return static_cast<To>(from.count());
// Throwing version of safe_duration_cast is only available for
// integer to integer or float to float casts.
int ec;
@@ -487,7 +474,7 @@ template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
// Mixed integer <-> float cast is not supported by safe_duration_cast.
// Mixed integer <-> float cast is not supported by safe duration_cast.
return std::chrono::duration_cast<To>(from);
}
@@ -501,86 +488,10 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
.count();
}
namespace tz {
// DEPRECATED!
struct time_zone {
template <typename Duration, typename LocalTime>
auto to_sys(LocalTime) -> sys_time<Duration> {
return {};
}
};
template <typename... T> auto current_zone(T...) -> time_zone* {
return nullptr;
}
template <typename... T> void _tzset(T...) {}
} // namespace tz
// DEPRECATED!
inline void tzset_once() {
static bool init = []() {
using namespace tz;
_tzset();
return false;
}();
ignore_unused(init);
}
} // namespace detail
FMT_BEGIN_EXPORT
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in local time. Unlike `std::localtime`, this function is
* thread-safe on most platforms.
*/
FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
inline dispatcher(std::time_t t) : time_(t) {}
inline auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
inline auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
inline auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
#if FMT_USE_LOCAL_TIME
template <typename Duration>
FMT_DEPRECATED auto localtime(std::chrono::local_time<Duration> time)
-> std::tm {
using namespace std::chrono;
using namespace detail::tz;
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
}
#endif
/**
* Converts given time since epoch as `std::time_t` value into calendar time,
* expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
@@ -652,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr const size_t len = 8;
constexpr size_t len = 8;
if (const_check(is_big_endian())) {
char tmp[len];
std::memcpy(tmp, &digits, len);
@@ -1000,16 +911,16 @@ template <typename T>
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
bool set_tm_zone(T& time, char* tz) {
auto set_tm_zone(T& time, char* tz) -> bool {
time.tm_zone = tz;
return true;
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
bool set_tm_zone(T&, char*) {
auto set_tm_zone(T&, char*) -> bool {
return false;
}
inline char* utc() {
inline auto utc() -> char* {
static char tz[] = "UTC";
return tz;
}
@@ -2230,7 +2141,7 @@ template <typename Char> struct formatter<std::tm, Char> {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref();
auto loc_ref = specs.localized() ? ctx.locale() : locale_ref();
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
loc, out, tm, subsecs);
+18 -18
View File
@@ -375,19 +375,17 @@ template <typename Char> struct ansi_color_escape {
// 10 more.
if (is_background) value += 10u;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
buffer[size++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[size++] = static_cast<Char>('0' + value / 10u);
buffer[size++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
buffer[size++] = static_cast<Char>('m');
return;
}
@@ -398,7 +396,7 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
size = 19;
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {};
@@ -411,26 +409,28 @@ template <typename Char> struct ansi_color_escape {
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0;
buffer[size++] = static_cast<Char>('\x1b');
buffer[size++] = static_cast<Char>('[');
for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
buffer[size++] = static_cast<Char>('0' + em_codes[i]);
buffer[size++] = static_cast<Char>(';');
}
buffer[index++] = static_cast<Char>(0);
buffer[size - 1] = static_cast<Char>('m');
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + basic_string_view<Char>(buffer).size();
FMT_CONSTEXPR auto end() const noexcept -> const Char* {
return buffer + size;
}
private:
static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
Char buffer[7u + 4u * num_emphases];
size_t size = 0;
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) noexcept {
+122 -76
View File
@@ -22,8 +22,6 @@ FMT_EXPORT class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
namespace detail {
/**
* Converts a string literal `s` into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires C++17
@@ -41,18 +39,40 @@ namespace detail {
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
/**
* Converts a string literal into a format string that will be parsed at
* compile time and converted into efficient formatting code. Requires support
* for class types in constant template parameters (a C++20 feature).
*
* **Example**:
*
* // Converts 42 into std::string using the most efficient method and no
* // runtime format string processing.
* using namespace fmt::literals;
* std::string s = fmt::format("{}"_cf, 42);
*/
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
namespace detail {
template <typename T, typename... Tail>
auto first(const T& value, const Tail&...) -> const T& {
constexpr auto first(const T& value, const Tail&...) -> const T& {
return value;
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {};
template <typename... T> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
constexpr auto get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) -> const auto& {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -84,8 +104,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) -> int {
return get_arg_index_by_name<Args...>(name);
}
@@ -105,8 +125,8 @@ template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
return write<Char>(out, data);
}
};
@@ -115,8 +135,8 @@ template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
-> text<Char> {
return {{&s[pos], size}};
}
@@ -124,8 +144,8 @@ template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&...) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
*out++ = value;
return out;
}
@@ -133,7 +153,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
constexpr auto get_arg_checked(const Args&... args) -> const T& {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
@@ -146,13 +166,13 @@ template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
template <typename Char, typename V, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
const V& arg = get_arg_checked<V, N>(args...);
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg);
return copy<Char>(s.begin(), s.end(), out);
} else {
@@ -170,10 +190,10 @@ template <typename Char> struct runtime_named_field {
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
constexpr static auto try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
@@ -183,8 +203,8 @@ template <typename Char> struct runtime_named_field {
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
@@ -197,17 +217,17 @@ template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
template <typename Char, typename V, int N> struct spec_field {
using char_type = Char;
formatter<T, Char> fmt;
formatter<V, Char> fmt;
template <typename OutputIt, typename... Args>
constexpr FMT_INLINE OutputIt format(OutputIt out,
const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
-> OutputIt {
const auto& vargs =
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(get_arg_checked<T, N>(args...), ctx);
return fmt.format(get_arg_checked<V, N>(args...), ctx);
}
};
@@ -219,8 +239,8 @@ template <typename L, typename R> struct concat {
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
template <typename OutputIt, typename... T>
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
@@ -230,14 +250,14 @@ template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
@@ -270,8 +290,8 @@ template <typename T, typename Char> struct parse_specs_result {
enum { manual_indexing_id = -1 };
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) {
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
int next_arg_id) -> parse_specs_result<T, Char> {
str.remove_prefix(pos);
auto ctx =
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
@@ -285,16 +305,16 @@ template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id;
constexpr int on_auto() {
constexpr auto on_auto() -> int {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int on_index(int id) {
constexpr auto on_index(int id) -> int {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int on_name(basic_string_view<Char> id) {
constexpr auto on_name(basic_string_view<Char> id) -> int {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id);
return 0;
@@ -433,27 +453,28 @@ FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args,
template <typename CompiledFormat, typename... T,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
const T&... args)
-> std::basic_string<Char> {
auto s = std::basic_string<Char>();
cf.format(std::back_inserter(s), args...);
return s;
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
template <typename OutputIt, typename CompiledFormat, typename... T,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
const T&... args) -> OutputIt {
return cf.format(out, args...);
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
-> std::basic_string<typename S::char_type> {
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr auto str = basic_string_view<typename S::char_type>(S());
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
@@ -466,72 +487,97 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
}
}
}
constexpr auto compiled = detail::compile<Args...>(S());
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format(
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format(compiled, std::forward<Args>(args)...);
return fmt::format(compiled, std::forward<T>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
constexpr auto compiled = detail::compile<T...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return fmt::format_to(
out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
std::forward<T>(args)...);
} else {
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
return fmt::format_to(out, compiled, std::forward<T>(args)...);
}
}
#endif
template <typename OutputIt, typename S, typename... Args,
template <typename OutputIt, typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t {
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<>();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return buf.count();
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
void print(std::FILE* f, const S& fmt, T&&... args) {
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
void print(const S& fmt, const Args&... args) {
print(stdout, fmt, args...);
void print(const S& fmt, T&&... args) {
print(stdout, fmt, std::forward<T>(args)...);
}
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals {
template <detail::fixed_string Str> constexpr auto operator""_cf() {
return FMT_COMPILE(Str.data);
}
} // namespace literals
#endif
template <size_t N> class static_format_result {
private:
char data[N];
public:
template <typename S, typename... T,
FMT_ENABLE_IF(is_compiled_string<S>::value)>
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
}
auto str() const -> fmt::string_view { return {data, N - 1}; }
auto c_str() const -> const char* { return data; }
};
/**
* Formats arguments according to the format string `fmt_str` and produces
* a string of the exact required size at compile time. Both the format string
* and the arguments must be compile-time expressions.
*
* The resulting string can be accessed as a C string via `c_str()` or as
* a `fmt::string_view` via `str()`.
*
* **Example**:
*
* // Produces the static string "42" at compile time.
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
* const char* s = result.c_str();
*/
#define FMT_STATIC_FORMAT(fmt_str, ...) \
fmt::static_format_result< \
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
FMT_COMPILE(fmt_str), __VA_ARGS__)
FMT_END_EXPORT
FMT_END_NAMESPACE
+65 -60
View File
@@ -22,7 +22,7 @@
#include "format.h"
#if FMT_USE_LOCALE
#if FMT_USE_LOCALE && !defined(FMT_MODULE)
# include <locale>
#endif
@@ -31,14 +31,49 @@
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#ifndef FMT_CUSTOM_ASSERT_FAIL
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails.
fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
abort();
}
#endif
#if FMT_USE_LOCALE
namespace detail {
using std::locale;
using std::numpunct;
using std::use_facet;
} // namespace detail
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
#else
namespace detail {
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
} // namespace detail
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
using namespace detail;
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
namespace detail {
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept {
@@ -79,33 +114,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
#if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, locale>::value, "");
}
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, locale>::value, "");
#if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
}
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
@@ -133,14 +141,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
} // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#if FMT_MSC_VERSION || defined(__NVCC__)
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (!b) return;
#endif
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id;
@@ -174,11 +181,11 @@ inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
}
// Compilers should be able to optimize this into the ror instruction.
FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
return (n >> r) | (n << (32 - r));
}
FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
@@ -275,7 +282,7 @@ template <> struct cache_accessor<float> {
static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
static constexpr uint64_t pow10_significands[] = {
0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f,
0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb,
0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28,
@@ -370,7 +377,7 @@ template <> struct cache_accessor<double> {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr const uint128_fallback pow10_significands[] = {
static constexpr uint128_fallback pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@@ -1037,7 +1044,7 @@ template <> struct cache_accessor<double> {
#if FMT_USE_FULL_CACHE_DRAGONBOX
return pow10_significands[k - float_info<double>::min_k];
#else
static constexpr const uint64_t powers_of_5_64[] = {
static constexpr uint64_t powers_of_5_64[] = {
0x0000000000000001, 0x0000000000000005, 0x0000000000000019,
0x000000000000007d, 0x0000000000000271, 0x0000000000000c35,
0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1,
@@ -1149,8 +1156,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
exponent <= case_shorter_interval_left_endpoint_upper_threshold;
}
// Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
// Remove trailing zeros from n and return the number of zeros removed (float).
FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd;
@@ -1170,22 +1177,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
return s;
}
// Removes trailing zeros and returns the number of zeros removed (double)
FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// Removes trailing zeros and returns the number of zeros removed (double).
FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int {
FMT_ASSERT(n != 0, "");
// This magic number is ceil(2^90 / 10^8).
constexpr uint64_t magic_number = 12379400392853802749ull;
auto nm = umul128(n, magic_number);
// Is n is divisible by 10^8?
if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) {
constexpr uint32_t ten_pow_8 = 100000000u;
if ((n % ten_pow_8) == 0) {
// If yes, work with the quotient...
auto n32 = static_cast<uint32_t>(nm.high() >> (90 - 64));
auto n32 = static_cast<uint32_t>(n / ten_pow_8);
// ... and use the 32 bit variant of the function
int s = remove_trailing_zeros(n32, 8);
int num_zeros = remove_trailing_zeros(n32, 8);
n = n32;
return s;
return num_zeros;
}
// If n is not divisible by 10^8, work with n itself.
@@ -1210,7 +1214,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// The main algorithm for shorter interval case
template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp<T> {
decimal_fp<T> ret_value;
// Compute k and beta
const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent);
@@ -1454,8 +1458,8 @@ FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
parse_format_string(fmt,
format_handler<>{parse_context<>(fmt), {out, args, loc}});
}
template <typename T> struct span {
@@ -1546,10 +1550,11 @@ template <typename F> class glibc_file : public file_base<F> {
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
auto size = max_of<ptrdiff_t>(this->file_->_IO_write_ptr - end, 0);
return memchr(end, '\n', static_cast<size_t>(size));
}
void flush() { fflush_unlocked(this->file_); }
@@ -1573,7 +1578,7 @@ template <typename F> class apple_file : public file_base<F> {
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
@@ -1594,7 +1599,7 @@ template <typename F> class apple_file : public file_base<F> {
this->file_->_w -= size;
}
bool needs_flush() const {
auto needs_flush() const -> bool {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));
+479 -340
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -29,7 +29,8 @@
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || \
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
!defined(__wasm__)
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
# else
+2 -2
View File
@@ -33,8 +33,8 @@
FMT_BEGIN_NAMESPACE
namespace detail {
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
// Generate a unique explicit instantiation in every translation unit using a
// tag type in an anonymous namespace.
namespace {
struct file_access_tag {};
} // namespace
+25 -34
View File
@@ -9,7 +9,7 @@
#define FMT_PRINTF_H_
#ifndef FMT_MODULE
# include <algorithm> // std::max
# include <algorithm> // std::find
# include <limits> // std::numeric_limits
#endif
@@ -18,10 +18,6 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
basic_appender<Char> out_;
@@ -33,8 +29,6 @@ template <typename Char> class basic_printf_context {
public:
using char_type = Char;
using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/// Constructs a `printf_context` object. References to the arguments are
@@ -46,7 +40,7 @@ template <typename Char> class basic_printf_context {
auto out() -> basic_appender<Char> { return out_; }
void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; }
auto locale() -> locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id);
@@ -74,10 +68,9 @@ inline auto find<false, char>(const char* first, const char* last, char value,
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
template <bool IS_SIGNED> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = to_unsigned(max_value<int>());
return value <= max;
return value <= to_unsigned(max_value<int>());
}
inline static auto fits_in_int(bool) -> bool { return true; }
};
@@ -95,7 +88,7 @@ struct printf_precision_handler {
auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
report_error("number is too big");
return (std::max)(static_cast<int>(value), 0);
return max_of(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@@ -410,7 +403,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
auto arg = context.arg(arg_index);
if (!arg) report_error("argument not found");
return arg;
};
const Char* start = parse_ctx.begin();
@@ -571,15 +566,19 @@ inline auto vsprintf(basic_string_view<Char> fmt,
*
* std::string message = fmt::sprintf("The answer is %d", 42);
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...));
template <typename... T>
inline auto sprintf(string_view fmt, const T&... args) -> std::string {
return vsprintf(fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED auto sprintf(basic_string_view<wchar_t> fmt, const T&... args)
-> std::wstring {
return vsprintf(fmt, make_printf_args<wchar_t>(args...));
}
template <typename Char>
inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args) -> int {
auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args);
size_t size = buf.size();
@@ -596,17 +595,14 @@ inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
*
* fmt::fprintf(stderr, "Don't %s!", "panic");
*/
template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt),
make_printf_args<Char>(args...));
template <typename... T>
inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args(args...));
}
template <typename Char>
FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
typename vprintf_args<Char>::type args)
-> int {
return vfprintf(stdout, fmt, args);
template <typename... T>
FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(f, fmt, make_printf_args<wchar_t>(args...));
}
/**
@@ -621,11 +617,6 @@ template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args(args...));
}
template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int {
return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
}
FMT_END_EXPORT
FMT_END_NAMESPACE
+25 -23
View File
@@ -11,7 +11,6 @@
#ifndef FMT_MODULE
# include <initializer_list>
# include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
@@ -31,7 +30,7 @@ template <typename T> class is_map {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -40,17 +39,16 @@ template <typename T> class is_set {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
};
// C array overload
template <typename T, std::size_t N>
template <typename T, size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
template <typename T, size_t N> auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
@@ -120,7 +118,7 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -154,7 +152,7 @@ using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
template <typename T, typename C, bool = is_tuple_like_<T>::value>
class is_tuple_formattable_ {
public:
static constexpr const bool value = false;
static constexpr bool value = false;
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <size_t... Is>
@@ -170,7 +168,7 @@ template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
C>::value)...>{}));
public:
static constexpr const bool value =
static constexpr bool value =
decltype(check(tuple_index_sequence<T>{}))::value;
};
@@ -208,7 +206,7 @@ template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
template <typename Tuple, typename Char, size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
@@ -219,7 +217,7 @@ template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
template <typename T, size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
@@ -281,14 +279,15 @@ template <typename FormatContext> struct format_tuple_element {
} // namespace detail
FMT_EXPORT
template <typename T> struct is_tuple_like {
static constexpr const bool value =
static constexpr bool value =
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
FMT_EXPORT
template <typename T, typename C> struct is_tuple_formattable {
static constexpr const bool value =
detail::is_tuple_formattable_<T, C>::value;
static constexpr bool value = detail::is_tuple_formattable_<T, C>::value;
};
template <typename Tuple, typename Char>
@@ -343,8 +342,9 @@ struct formatter<Tuple, Char,
}
};
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr const bool value =
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
};
@@ -368,6 +368,7 @@ template <typename P1, typename... Pn>
struct conjunction<P1, Pn...>
: conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
FMT_EXPORT
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@@ -670,7 +671,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
FMT_EXPORT
template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
@@ -685,15 +687,15 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, Tuple>, Char,
template <typename Tuple, typename Char>
struct formatter<tuple_join_view<Tuple, Char>, Char,
enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::tuple_size<Tuple>());
}
template <typename FormatContext>
auto format(const tuple_join_view<Char, Tuple>& value,
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@@ -725,14 +727,14 @@ struct formatter<tuple_join_view<Char, Tuple>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
@@ -754,7 +756,7 @@ template <typename T> class is_container_adaptor_like {
template <typename> static void check(...);
public:
static constexpr const bool value =
static constexpr bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
@@ -825,7 +827,7 @@ auto join(Range&& r, string_view sep)
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
-> tuple_join_view<Tuple, char> {
return {tuple, sep};
}
+202 -223
View File
@@ -15,15 +15,13 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
# include <typeinfo> // std::type_info
# include <utility> // std::make_index_sequence
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
@@ -62,27 +60,26 @@
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
# else
# define FMT_CPP_LIB_FILESYSTEM 0
# endif
#ifdef FMT_CPP_LIB_FILESYSTEM
// Use the provided definition.
#elif defined(__cpp_lib_filesystem)
# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
#else
# define FMT_CPP_LIB_FILESYSTEM 0
#endif
#ifndef FMT_CPP_LIB_VARIANT
# ifdef __cpp_lib_variant
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
# else
# define FMT_CPP_LIB_VARIANT 0
# endif
#ifdef FMT_CPP_LIB_VARIANT
// Use the provided definition.
#elif defined(__cpp_lib_variant)
# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
#else
# define FMT_CPP_LIB_VARIANT 0
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
@@ -111,8 +108,168 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
}
}
#endif // FMT_CPP_LIB_FILESYSTEM
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
#endif
#if FMT_CPP_LIB_VARIANT
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename Variant, typename Char> class is_variant_formattable {
template <size_t... Is>
static auto check(std::index_sequence<Is...>) -> std::conjunction<
is_formattable<std::variant_alternative_t<Is, Variant>, Char>...>;
public:
static constexpr bool value = decltype(check(
std::make_index_sequence<std::variant_size<Variant>::value>()))::value;
};
#endif // FMT_CPP_LIB_VARIANT
#if FMT_USE_RTTI
template <typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<char>(out, string_view(ti.name()));
# endif
}
#endif // FMT_USE_RTTI
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr bool value = std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value &&
has_flip<T>::value;
};
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD)
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr bool value = true;
};
#endif
template <typename T, typename Enable = void>
struct has_format_as : std::false_type {};
template <typename T>
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Enable = void>
struct has_format_as_member : std::false_type {};
template <typename T>
struct has_format_as_member<
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
: std::true_type {};
} // namespace detail
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
#if FMT_CPP_LIB_FILESYSTEM
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
template <typename Char> struct formatter<std::filesystem::path, Char> {
private:
format_specs specs_;
@@ -162,39 +319,20 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
}
};
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
template <std::size_t N, typename Char>
template <size_t N, typename Char>
struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private:
// Functor because C++11 doesn't support generic lambdas.
// This is a functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
for (auto pos = N; pos > 0; --pos)
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
@@ -209,10 +347,8 @@ struct formatter<std::bitset<N>, Char>
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
@@ -251,30 +387,9 @@ struct formatter<std::optional<T>, Char,
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
@@ -301,11 +416,9 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::source_location> {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
@@ -323,42 +436,12 @@ template <> struct formatter<std::source_location> {
return out;
}
};
FMT_END_NAMESPACE
#endif
#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>;
template <typename> struct is_variant_like_ : std::false_type {};
template <typename... Types>
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
// formattable element check.
template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... Is>
static std::conjunction<
is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<Is...>);
public:
static constexpr const bool value =
decltype(check(variant_index_sequence<T>{}))::value;
};
} // namespace detail
template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value;
};
template <typename T, typename C> struct is_variant_formattable {
static constexpr const bool value =
detail::is_variant_formattable_<T, C>::value;
static constexpr bool value = detail::is_variant_like_<T>::value;
};
template <typename Char> struct formatter<std::monostate, Char> {
@@ -374,10 +457,10 @@ template <typename Char> struct formatter<std::monostate, Char> {
};
template <typename Variant, typename Char>
struct formatter<
Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
struct formatter<Variant, Char,
std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>,
detail::is_variant_formattable<Variant, Char>>>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
@@ -402,10 +485,9 @@ struct formatter<
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
template <> struct formatter<std::error_code> {
private:
format_specs specs_;
@@ -459,101 +541,29 @@ template <> struct formatter<std::error_code> {
};
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
template <> struct formatter<std::type_info> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
return detail::write_demangled_name(ctx.out(), ti);
}
};
#endif
#endif // FMT_USE_RTTI
template <typename T, typename Char>
template <typename T>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
T, char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
@@ -570,43 +580,15 @@ struct formatter<
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
out = detail::write_demangled_name(out, typeid(ex));
*out++ = ':';
*out++ = ' ';
}
#endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
return detail::write_bytes<char>(out, string_view(ex.what()));
}
};
namespace detail {
template <typename T, typename Enable = void>
struct has_flip : std::false_type {};
template <typename T>
struct has_flip<T, void_t<decltype(std::declval<T>().flip())>>
: std::true_type {};
template <typename T> struct is_bit_reference_like {
static constexpr const bool value =
std::is_convertible<T, bool>::value &&
std::is_nothrow_assignable<T, bool>::value && has_flip<T>::value;
};
#ifdef _LIBCPP_VERSION
// Workaround for libc++ incompatibility with C++ standard.
// According to the Standard, `bitset::operator[] const` returns bool.
template <typename C>
struct is_bit_reference_like<std::__bit_const_reference<C>> {
static constexpr const bool value = true;
};
#endif
} // namespace detail
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
@@ -621,14 +603,6 @@ struct formatter<BitRef, Char,
}
};
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@@ -715,7 +689,11 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
// Guard against format_as because reference_wrapper is
// implicitly convertible to T&.
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
!detail::has_format_as<T>::value &&
!detail::has_format_as_member<T>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
@@ -725,4 +703,5 @@ struct formatter<std::reference_wrapper<T>, Char,
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_
+30 -43
View File
@@ -55,6 +55,16 @@ inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
#endif
return false;
}
template <typename Char>
void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
basic_format_args<buffered_context<Char>> args,
locale_ref loc = {}) {
static_assert(!std::is_same<Char, char>::value, "");
auto out = basic_appender<Char>(buf);
parse_format_string(
fmt, format_handler<Char>{parse_context<Char>(fmt), {out, args, loc}});
}
} // namespace detail
FMT_BEGIN_EXPORT
@@ -112,10 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T>
constexpr auto make_wformat_args(T&... args)
-> decltype(fmt::make_format_args<wformat_context>(args...)) {
@@ -151,13 +157,13 @@ auto join(std::initializer_list<T> list, wstring_view sep)
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, fmt, args);
@@ -187,24 +193,20 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
template <typename S, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto vformat(locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
detail::vformat_to(buf, detail::to_string_view(fmt), args, loc);
return {buf.data(), buf.size()};
}
template <typename Locale, typename S, typename... T,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto format(locale_ref loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
@@ -215,7 +217,7 @@ template <typename OutputIt, typename S,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& fmt,
typename detail::vformat_args<Char>::type args) -> OutputIt {
basic_format_args<buffered_context<Char>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out);
@@ -231,27 +233,24 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
fmt::make_format_args<buffered_context<Char>>(args...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
template <typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt,
basic_format_args<buffered_context<Char>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
vformat_to(buf, detail::to_string_view(fmt), args, loc);
return detail::get_iterator(buf, out);
}
template <typename Locale, typename OutputIt, typename S, typename... T,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args)
-> typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffered_context<Char>>(args...));
}
@@ -260,7 +259,7 @@ template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
typename detail::vformat_args<Char>::type args)
basic_format_args<buffered_context<Char>> args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
@@ -331,18 +330,6 @@ inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);
+2
View File
@@ -50,6 +50,8 @@ module;
# include <limits.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
#endif
#include <cerrno>
+7 -10
View File
@@ -8,6 +8,12 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
#if FMT_USE_LOCALE
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept
@@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif
// Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref)
@@ -30,16 +30,13 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char;
// DEPRECATED!
template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref);
// Explicit instantiations for wchar_t.
template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
// DEPRECATED!
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
} // namespace detail
+10 -10
View File
@@ -66,14 +66,14 @@ using rwresult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
inline unsigned convert_rwcount(size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
@@ -185,7 +185,7 @@ void buffered_file::close() {
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
}
int buffered_file::descriptor() const {
auto buffered_file::descriptor() const -> int {
#ifdef FMT_HAS_SYSTEM
// fileno is a macro on OpenBSD.
# ifdef fileno
@@ -240,7 +240,7 @@ void file::close() {
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
}
long long file::size() const {
auto file::size() const -> long long {
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
@@ -251,7 +251,7 @@ long long file::size() const {
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
FMT_THROW(windows_error(error, "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
@@ -266,7 +266,7 @@ long long file::size() const {
# endif
}
std::size_t file::read(void* buffer, std::size_t count) {
auto file::read(void* buffer, size_t count) -> size_t {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@@ -274,7 +274,7 @@ std::size_t file::read(void* buffer, std::size_t count) {
return detail::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
auto file::write(const void* buffer, size_t count) -> size_t {
rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@@ -282,7 +282,7 @@ std::size_t file::write(const void* buffer, std::size_t count) {
return detail::to_unsigned(result);
}
file file::dup(int fd) {
auto file::dup(int fd) -> file {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
@@ -308,7 +308,7 @@ void file::dup2(int fd, std::error_code& ec) noexcept {
if (result == -1) ec = std::error_code(errno, std::generic_category());
}
buffered_file file::fdopen(const char* mode) {
auto file::fdopen(const char* mode) -> buffered_file {
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
@@ -355,7 +355,7 @@ pipe::pipe() {
}
# if !defined(__MSDOS__)
long getpagesize() {
auto getpagesize() -> long {
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
+16
View File
@@ -1,3 +1,19 @@
# v12.1.0
* add rc_client_get_user_subset_summary
* add validation warning for using MeasuredIf without Measured
* add validation warning for using ResetIf without hit targets
* add rapi function for update_rich_presence
* add gap to RC_CONSOLE_WII memory map to make it easier to convert pointers
* improve range validation logic
* fix error Remembering float value
* fix MeasuredIf evaluation in rich presence
* fix parsing of code notes with addresses above 0x7FFFFFFF
* fix double evaluation of rich presence parameters
* fix validation of SubSource chain
* fix error if rc_client_allow_background_memory_reads called before calling rc_client_begin_load_raintegration
* fix memory corruption when mixing legacy and new-format macros in rich presence
* fix invalid pointer reference when iterator gets cloned
# v12.0.0
* rc_client changes
* add RC_CLIENT_EVENT_SUBSET_COMPLETED event
+1 -1
View File
@@ -5880,7 +5880,7 @@ void rc_client_do_frame(rc_client_t* client)
richpresence = client->game->runtime.richpresence;
if (richpresence && richpresence->richpresence)
rc_update_richpresence(richpresence->richpresence, client->state.legacy_peek, client, NULL);
rc_update_richpresence_internal(richpresence->richpresence, client->state.legacy_peek, client);
rc_mutex_unlock(&client->state.mutex);
+2
View File
@@ -223,6 +223,8 @@ static void rc_client_init_raintegration(rc_client_t* client,
external_client->set_encore_mode_enabled(rc_client_get_encore_mode_enabled(client));
if (external_client->set_spectator_mode_enabled)
external_client->set_spectator_mode_enabled(rc_client_get_spectator_mode_enabled(client));
if (external_client->set_allow_background_memory_reads)
external_client->set_allow_background_memory_reads(client->state.allow_background_memory_reads);
/* attach the external client and call the callback */
client->state.external_client = external_client;
+1 -1
View File
@@ -8,7 +8,7 @@
RC_BEGIN_C_DECLS
#define RCHEEVOS_VERSION_MAJOR 12
#define RCHEEVOS_VERSION_MINOR 0
#define RCHEEVOS_VERSION_MINOR 1
#define RCHEEVOS_VERSION_PATCH 0
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
+2 -2
View File
@@ -607,8 +607,8 @@ static void rc_condset_evaluate_or_next(rc_condition_t* condition, rc_eval_state
eval_state->or_next = rc_condset_evaluate_condition_no_add_hits(condition, eval_state);
}
static void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
rc_eval_state_t* eval_state, int can_short_circuit) {
void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions,
rc_eval_state_t* eval_state, int can_short_circuit) {
const rc_condition_t* condition_end = condition + num_conditions;
for (; condition < condition_end; ++condition) {
switch (condition->type) {
+2
View File
@@ -311,6 +311,7 @@ rc_condset_t* rc_parse_condset(const char** memaddr, rc_parse_state_t* parse);
int rc_test_condset(rc_condset_t* self, rc_eval_state_t* eval_state);
void rc_reset_condset(rc_condset_t* self);
rc_condition_t* rc_condset_get_conditions(rc_condset_t* self);
void rc_test_condset_internal(rc_condition_t* condition, uint32_t num_conditions, rc_eval_state_t* eval_state, int can_short_circuit);
enum {
RC_PROCESSING_COMPARE_DEFAULT = 0,
@@ -379,6 +380,7 @@ int rc_lboard_state_active(int state);
void rc_parse_richpresence_internal(rc_richpresence_t* self, const char* script, rc_parse_state_t* parse);
rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self);
void rc_reset_richpresence_triggers(rc_richpresence_t* self);
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud);
int rc_validate_memrefs(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t max_address);
int rc_validate_memrefs_for_console(const rc_memrefs_t* memrefs, char result[], const size_t result_size, uint32_t console_id);
+108 -48
View File
@@ -133,79 +133,93 @@ static uint32_t rc_max_value(const rc_operand_t* operand)
}
}
static uint32_t rc_scale_value(uint32_t value, uint8_t oper, const rc_operand_t* operand)
static void rc_combine_ranges(uint32_t* min_val, uint32_t* max_val, uint8_t oper, uint32_t oper_min_val, uint32_t oper_max_val)
{
switch (oper) {
case RC_OPERATOR_MULT:
{
unsigned long long scaled = ((unsigned long long)value) * rc_max_value(operand);
if (scaled > 0xFFFFFFFF)
return 0xFFFFFFFF;
unsigned long long scaled = ((unsigned long long)*min_val) * oper_min_val;
*min_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
return (uint32_t)scaled;
scaled = ((unsigned long long)*max_val) * oper_max_val;
*max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
break;
}
case RC_OPERATOR_DIV:
{
const uint32_t min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
return value / min_val;
}
*min_val = (oper_max_val == 0) ? *min_val : (*min_val / oper_max_val);
*max_val = (oper_min_val == 0) ? *max_val : (*max_val / oper_min_val);
break;
case RC_OPERATOR_AND:
return rc_max_value(operand);
*min_val = 0;
*max_val &= oper_max_val;
break;
case RC_OPERATOR_XOR:
return value | rc_max_value(operand);
*min_val = 0;
*max_val |= oper_max_val;
break;
case RC_OPERATOR_MOD:
{
const uint32_t divisor = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 1;
return (divisor >= value) ? (divisor - 1) : value;
}
*min_val = 0;
*max_val = (*max_val >= oper_max_val) ? oper_max_val - 1 : *max_val;
break;
case RC_OPERATOR_ADD:
{
unsigned long scaled = ((unsigned long)value) + rc_max_value(operand);
if (scaled > 0xFFFFFFFF)
return 0xFFFFFFFF;
if (*min_val > *max_val) { /* underflow occurred */
*max_val += oper_max_val;
}
else {
unsigned long scaled = ((unsigned long)*max_val) + oper_max_val;
*max_val = (scaled > 0xFFFFFFFF) ? 0xFFFFFFFF : (uint32_t)scaled;
}
return (uint32_t)scaled;
}
*min_val += oper_min_val;
break;
case RC_OPERATOR_SUB:
{
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0;
if (value >= op_max)
return value - op_max;
return 0xFFFFFFFF;
}
*min_val -= oper_max_val;
*max_val -= oper_min_val;
break;
case RC_OPERATOR_SUB_PARENT:
{
const uint32_t op_max = (operand->type == RC_OPERAND_CONST) ? operand->value.num : rc_max_value(operand);
if (op_max > value)
return op_max - value;
return 0xFFFFFFFF;
uint32_t temp = oper_min_val - *max_val;
*max_val = oper_max_val - *min_val;
*min_val = temp;
break;
}
default:
return value;
break;
}
}
static uint32_t rc_max_chain_value(const rc_operand_t* operand)
static void rc_chain_get_value_range(const rc_operand_t* operand, uint32_t* min_val, uint32_t* max_val)
{
if (rc_operand_is_memref(operand) && operand->value.memref->value.memref_type == RC_MEMREF_TYPE_MODIFIED_MEMREF) {
const rc_modified_memref_t* modified_memref = (const rc_modified_memref_t*)operand->value.memref;
if (modified_memref->modifier_type != RC_OPERATOR_INDIRECT_READ) {
const uint32_t op_max = rc_max_chain_value(&modified_memref->parent);
return rc_scale_value(op_max, modified_memref->modifier_type, &modified_memref->modifier);
if (modified_memref->modifier_type == RC_OPERATOR_DIV &&
rc_operand_is_memref(&modified_memref->modifier) &&
rc_operands_are_equal(&modified_memref->modifier, &modified_memref->parent)) {
/* division by self can only return 0 or 1. */
*min_val = 0;
*max_val = 1;
}
else {
uint32_t modifier_min_val, modifier_max_val;
rc_chain_get_value_range(&modified_memref->parent, min_val, max_val);
rc_chain_get_value_range(&modified_memref->modifier, &modifier_min_val, &modifier_max_val);
rc_combine_ranges(min_val, max_val, modified_memref->modifier_type, modifier_min_val, modifier_max_val);
}
return;
}
}
return rc_max_value(operand);
*min_val = (operand->type == RC_OPERAND_CONST) ? operand->value.num : 0;
*max_val = rc_max_value(operand);
}
static int rc_validate_get_condition_index(const rc_condset_t* condset, const rc_condition_t* condition)
@@ -291,7 +305,7 @@ static int rc_validate_range(uint32_t min_val, uint32_t max_val, char oper, uint
return 1;
}
static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address)
static int rc_validate_condset_internal(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id, uint32_t max_address, int has_hits)
{
const rc_condition_t* cond;
char buffer[128];
@@ -299,6 +313,8 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
int in_add_hits = 0;
int in_add_address = 0;
int is_combining = 0;
int has_measured = 0;
int measuredif_index = -1;
if (!condset) {
*result = '\0';
@@ -383,6 +399,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
is_combining = 0;
break;
}
if (!has_hits) {
snprintf(result, result_size, "Condition %d: No captured hits to reset", index);
return 0;
}
if (cond->required_hits == 1) {
snprintf(result, result_size, "Condition %d: Hit target of 1 is redundant on ResetIf", index);
return 0;
@@ -398,6 +418,10 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
in_add_hits = 0;
}
has_measured |= (cond->type == RC_CONDITION_MEASURED);
if (cond->type == RC_CONDITION_MEASURED_IF && measuredif_index == -1)
measuredif_index = index;
is_combining = 0;
break;
}
@@ -424,10 +448,16 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
const size_t prefix_length = snprintf(result, result_size, "Condition %d: ", index);
const rc_operand_t* operand2 = &cond->operand2;
uint8_t oper = cond->oper;
uint32_t max = rc_max_chain_value(operand1);
uint32_t min, max;
uint32_t max_val = rc_max_value(operand2);
uint32_t min_val;
rc_chain_get_value_range(operand1, &min, &max);
if (min > max) { /* underflow */
min = 0;
max = 0xFFFFFFFF;
}
if (!is_memref1) {
/* pretend constant was on right side */
operand2 = operand1;
@@ -477,6 +507,7 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
break;
}
/* min_val and max_val are the range allowed by operand2. max is the upper value from operand1. */
if (!rc_validate_range(min_val, max_val, oper, max, result + prefix_length, result_size - prefix_length))
return 0;
}
@@ -487,19 +518,48 @@ static int rc_validate_condset_internal(const rc_condset_t* condset, char result
return 0;
}
if (measuredif_index != -1 && !has_measured) {
snprintf(result, result_size, "Condition %d: MeasuredIf without Measured", measuredif_index);
return 0;
}
*result = '\0';
return 1;
}
static int rc_condset_has_hittargets(const rc_condset_t* condset)
{
if (condset->num_hittarget_conditions > 0)
return 1;
/* pause and reset conditions may have hittargets and won't be classified as hittarget conditions.
* measured conditions may also have hittargets.
*/
if (condset->num_pause_conditions || condset->num_reset_conditions || condset->num_measured_conditions) {
const rc_condition_t* condition = rc_condset_get_conditions((rc_condset_t*)condset);
/* ASSERT: don't need to add num_hittarget_conditions because it must be 0 per earlier check */
const rc_condition_t* stop = condition + condset->num_pause_conditions
+ condset->num_reset_conditions + condset->num_measured_conditions;
for (; condition < stop; ++condition) {
if (condition->required_hits)
return 1;
}
}
return 0;
}
int rc_validate_condset(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t max_address)
{
return rc_validate_condset_internal(condset, result, result_size, 0, max_address);
int has_hits = rc_condset_has_hittargets(condset);
return rc_validate_condset_internal(condset, result, result_size, 0, max_address, has_hits);
}
int rc_validate_condset_for_console(const rc_condset_t* condset, char result[], const size_t result_size, uint32_t console_id)
{
const uint32_t max_address = rc_console_max_address(console_id);
return rc_validate_condset_internal(condset, result, result_size, console_id, max_address);
int has_hits = rc_condset_has_hittargets(condset);
return rc_validate_condset_internal(condset, result, result_size, console_id, max_address, has_hits);
}
static int rc_validate_is_combining_condition(const rc_condition_t* condition)
@@ -845,7 +905,7 @@ static int rc_validate_conflicting_conditions(const rc_condset_t* conditions, co
switch (compare_condition->type)
{
case RC_CONDITION_PAUSE_IF:
if (conditions != compare_conditions)
if (conditions != compare_conditions) /* PauseIf only affects conditions in same group */
break;
/* fallthrough */
case RC_CONDITION_RESET_IF:
@@ -955,10 +1015,10 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
{
const rc_condset_t* alt;
int index;
int has_hits = (trigger->requirement && trigger->requirement->num_hittarget_conditions > 0);
int has_hits = trigger->requirement && rc_condset_has_hittargets(trigger->requirement);
if (!has_hits) {
for (alt = trigger->alternative; alt; alt = alt->next) {
if (alt->num_hittarget_conditions > 0) {
if (rc_condset_has_hittargets(alt)) {
has_hits = 1;
break;
}
@@ -966,14 +1026,14 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
}
if (!trigger->alternative) {
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address))
if (!rc_validate_condset_internal(trigger->requirement, result, result_size, console_id, max_address, has_hits))
return 0;
return rc_validate_conflicting_conditions(trigger->requirement, trigger->requirement, has_hits, "", "", result, result_size);
}
snprintf(result, result_size, "Core ");
if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address))
if (!rc_validate_condset_internal(trigger->requirement, result + 5, result_size - 5, console_id, max_address, has_hits))
return 0;
/* compare core to itself */
@@ -984,7 +1044,7 @@ static int rc_validate_trigger_internal(const rc_trigger_t* trigger, char result
for (alt = trigger->alternative; alt; alt = alt->next, ++index) {
char altname[16];
const size_t prefix_length = snprintf(result, result_size, "Alt%d ", index);
if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address))
if (!rc_validate_condset_internal(alt, result + prefix_length, result_size - prefix_length, console_id, max_address, has_hits))
return 0;
/* compare alt to itself */
+7 -2
View File
@@ -723,14 +723,19 @@ rc_memrefs_t* rc_richpresence_get_memrefs(rc_richpresence_t* self) {
}
void rc_update_richpresence(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud, void* unused_L) {
rc_richpresence_display_t* display;
(void)unused_L;
rc_update_richpresence_memrefs(richpresence, peek, peek_ud);
rc_update_values(richpresence->values, peek, peek_ud);
rc_update_richpresence_internal(richpresence, peek, peek_ud);
}
void rc_update_richpresence_internal(rc_richpresence_t* richpresence, rc_peek_t peek, void* peek_ud) {
rc_richpresence_display_t* display;
for (display = richpresence->first_display; display; display = display->next) {
if (display->has_required_hits)
rc_test_trigger(&display->trigger, peek, peek_ud, unused_L);
rc_test_trigger(&display->trigger, peek, peek_ud, NULL);
}
}
+4 -4
View File
@@ -47,7 +47,10 @@ add_subdirectory(common)
# make pcsx2
add_subdirectory(pcsx2)
add_subdirectory(pcsx2-qt)
if(ENABLE_QT_UI)
add_subdirectory(pcsx2-qt)
endif()
# Updater is Windows only for now.
if (WIN32)
@@ -62,9 +65,6 @@ endif()
# gsrunner
if(ENABLE_GSRUNNER)
if (NOT WIN32 AND NOT APPLE)
message(WARNING "GSRunner is only supported on Windows and macOS and may not build on your system")
endif()
add_subdirectory(pcsx2-gsrunner)
else()
add_subdirectory(pcsx2-gsrunner EXCLUDE_FROM_ALL)
+96 -21
View File
@@ -413,6 +413,7 @@ PAPX-90504:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
PAPX-90505:
name: "2002 夏のオススメソフト おためしDisc"
@@ -446,6 +447,7 @@ PAPX-90508:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
PAPX-90511:
name: "SIREN [体験版]"
@@ -466,6 +468,7 @@ PAPX-90512:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
PAPX-90514:
name: "冬のオススメソフト おためしDISC"
@@ -522,6 +525,7 @@ PAPX-90523:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
PAPX-90524:
name: "ワイルドアームズ ザ フィフスヴァンガード [体験版]"
@@ -1101,6 +1105,7 @@ PCPX-96634:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
PCPX-96635:
name: "プレプレ2 VOLUME.8"
@@ -1143,6 +1148,7 @@ PCPX-96649:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
PCPX-96653:
name: "ラチェット&クランク3 突撃!ガラクチック★レンジャーズ [店頭体験版]"
@@ -2419,6 +2425,7 @@ SCAJ-20179:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
SCAJ-20180:
name: "Xenosaga Episode III - Also Sprach Zarathustra [Disc 2 of 2]"
region: "NTSC-Unk"
@@ -2427,6 +2434,7 @@ SCAJ-20180:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
SCAJ-20181:
name: "Minna no Tennis"
region: "NTSC-Unk"
@@ -3231,6 +3239,7 @@ SCED-51352:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCED-51359:
name: "Official PlayStation 2 Magazine Demo 27"
@@ -3820,6 +3829,7 @@ SCED-52578:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCED-52580:
name: "Magazine Ufficiale PlayStation 2 Italia 06/04"
@@ -3836,6 +3846,7 @@ SCED-52681:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCED-52687:
name: "Formula One 04 [Demo]"
@@ -4019,7 +4030,7 @@ SCED-52952:
cpuSpriteRenderBW: 4 # Fixes character and water textures.
cpuSpriteRenderLevel: 2 # Needed for above.
autoFlush: 2 # Fixes lighting.
halfPixelOffset: 4 # Aligns post-processing.
halfPixelOffset: 5 # Aligns post-processing and fixes desert tiles.
nativeScaling: 2 # Fixes lighting effects due to upscaling.
SCED-52969:
name: "EyeToy - Play 2"
@@ -5745,7 +5756,7 @@ SCES-52460:
cpuSpriteRenderBW: 4 # Fixes character and water textures.
cpuSpriteRenderLevel: 2 # Needed for above.
autoFlush: 2 # Fixes lighting.
halfPixelOffset: 4 # Aligns post-processing.
halfPixelOffset: 5 # Aligns post-processing and fixes desert tiles.
nativeScaling: 2 # Fixes lighting effects due to upscaling.
SCES-52529:
name: "Sly 2 - Band of Thieves"
@@ -7386,7 +7397,7 @@ SCKA-20040:
cpuSpriteRenderBW: 4 # Fixes character and water textures.
cpuSpriteRenderLevel: 2 # Needed for above.
autoFlush: 2 # Fixes lighting.
halfPixelOffset: 4 # Aligns post-processing.
halfPixelOffset: 5 # Aligns post-processing and fixes desert tiles.
nativeScaling: 2 # Fixes lighting effects due to upscaling.
SCKA-20041:
name: "아이토이 - 플레이 2"
@@ -8283,6 +8294,7 @@ SCPM-85301:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCPM-85302:
name: "みんなのGOLF オンライン"
@@ -8302,6 +8314,7 @@ SCPM-85304:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCPN-60101:
name: "PlayStation BB Navigator - Version 0.10 [Prerelease] [Disc 1]"
@@ -11798,7 +11811,7 @@ SCUS-97330:
cpuSpriteRenderBW: 4 # Fixes character and water textures.
cpuSpriteRenderLevel: 2 # Needed for above.
autoFlush: 2 # Fixes lighting.
halfPixelOffset: 4 # Aligns post-processing.
halfPixelOffset: 5 # Aligns post-processing and fixes desert tiles.
nativeScaling: 2 # Fixes lighting effects due to upscaling.
SCUS-97331:
name: "Official U.S. PlayStation Magazine Demo Disc 078"
@@ -11976,6 +11989,7 @@ SCUS-97384:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCUS-97392:
name: "NBA '06 featuring The Life Vol.1 [Demo]"
@@ -12082,7 +12096,7 @@ SCUS-97412:
cpuSpriteRenderBW: 4 # Fixes character and water textures.
cpuSpriteRenderLevel: 2 # Needed for above.
autoFlush: 2 # Fixes lighting.
halfPixelOffset: 4 # Aligns post-processing.
halfPixelOffset: 5 # Aligns post-processing and fixes desert tiles.
nativeScaling: 2 # Fixes lighting effects due to upscaling.
SCUS-97413:
name: "Ratchet & Clank - Up Your Arsenal [Public Beta v1.0]"
@@ -12188,6 +12202,7 @@ SCUS-97436:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
memcardFilters:
- "SCUS-97328"
@@ -12410,6 +12425,7 @@ SCUS-97483:
gsHWFixes:
recommendedBlendingLevel: 3 # Improves banding in car showcase as well as car brightness and sheen.
halfPixelOffset: 4 # Fixes weird edge shadows and depth bleed which happens on the edge as well.
autoFlush: 1 # Helps lens flare and sun occlusion somewhat but doesn't full fix it.
getSkipCount: "GSC_PolyphonyDigitalGames" # Fixes post-processing.
SCUS-97484:
name: "Sly 3 - Honor Among Thieves [E3 Demo]"
@@ -12620,7 +12636,7 @@ SCUS-97516:
cpuSpriteRenderBW: 4 # Fixes character and water textures.
cpuSpriteRenderLevel: 2 # Needed for above.
autoFlush: 2 # Fixes lighting.
halfPixelOffset: 4 # Aligns post-processing.
halfPixelOffset: 5 # Aligns post-processing and fixes desert tiles.
nativeScaling: 2 # Fixes lighting effects due to upscaling.
SCUS-97517:
name: "Killzone [Greatest Hits]"
@@ -13064,6 +13080,8 @@ SLAJ-25008:
SLAJ-25011:
name: "Shin Sangoku Musou 3"
region: "NTSC-Unk"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLAJ-25012:
name: "Final Fantasy X-2"
region: "NTSC-Unk"
@@ -13326,7 +13344,7 @@ SLAJ-25078:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -14133,7 +14151,7 @@ SLED-53937:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -15493,6 +15511,8 @@ SLES-50510:
SLES-50511:
name: "G-Surfers"
region: "PAL-M3"
gsHWFixes:
recommendedBlendingLevel: 2 # Improves character portraits.
SLES-50512:
name: "Bass Strike"
region: "PAL-E"
@@ -18201,18 +18221,28 @@ SLES-51660:
SLES-51661:
name: "Dynasty Warriors 4"
region: "PAL-E"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLES-51662:
name: "Dynasty Warriors 4"
region: "PAL-F"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLES-51663:
name: "Dynasty Warriors 4"
region: "PAL-G"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLES-51664:
name: "Dynasty Warriors 4"
region: "PAL-I"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLES-51665:
name: "Dynasty Warriors 4"
region: "PAL-S"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLES-51666:
name: "Piglet - El Gran Juego de Disney"
name-en: "Piglet's Big Game"
@@ -21613,21 +21643,29 @@ SLES-52909:
region: "PAL-E-G"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
SLES-52910:
name: "UEFA Champions League 2005"
region: "PAL-F-G"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
SLES-52911:
name: "UEFA Champions League 2005"
region: "PAL-I-S"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
SLES-52912:
name: "UEFA Champions League 2005"
region: "PAL-E-DU"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
SLES-52913:
name: "Suikoden IV"
region: "PAL-M5"
@@ -24797,7 +24835,7 @@ SLES-53886:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -25234,7 +25272,7 @@ SLES-54030:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -26934,8 +26972,10 @@ SLES-54569:
region: "PAL-E"
compat: 5
gsHWFixes:
halfPixelOffset: 1 # Needed to remove part of red box.
roundSprite: 2 # Fully remove red box when upscaling .
halfPixelOffset: 5 # Aligns bloom outlines.
nativeScaling: 2 # Softens and aligns bloom outlines.
cpuSpriteRenderBW: 4 # Fixes bloom softness.
cpuSpriteRenderLevel: 1 # Needed for above.
gameFixes:
- XGKickHack # Fixes blank textures.
SLES-54579:
@@ -27342,6 +27382,8 @@ SLES-54713:
SLES-54714:
name: "Tony Hawk's Downhill Jam"
region: "PAL-E"
speedHacks:
mtvu: 0 # Fixes tutorial crashing.
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
@@ -27352,6 +27394,8 @@ SLES-54714:
SLES-54715:
name: "Tony Hawk's Downhill Jam"
region: "PAL-M4"
speedHacks:
mtvu: 0 # Fixes tutorial crashing.
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
@@ -28187,6 +28231,8 @@ SLES-54964:
SLES-54971:
name: "Hot Wheels - Beat That!"
region: "PAL-M4"
clampModes:
eeClampMode: 3 # Fixes various car physics issues.
gsHWFixes:
roundSprite: 1 # Fixes font artifacts.
SLES-54972:
@@ -29709,7 +29755,7 @@ SLES-55421:
region: "PAL-SWI"
SLES-55428:
name: "Disney Bolt"
region: "PAL-M4"
region: "PAL-SC"
SLES-55429:
name: "Disney Bolt"
region: "PAL-M3"
@@ -31075,6 +31121,8 @@ SLKA-25050:
name: "진・삼국무쌍 3" # Undumped on ReDump as of 2025-08-28
name-en: "Jin Samguk Mussang 3"
region: "NTSC-K"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLKA-25051:
name: "클락 타워 3"
name-en: "Clock Tower 3"
@@ -36567,6 +36615,7 @@ SLPM-61147:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
SLPM-61148:
name: "グローランサーⅤ ジェネレーションズ [体験版]"
name-sort: "ぐろーらんさー5 じぇねれーしょんず [たいけんばん]"
@@ -42239,6 +42288,8 @@ SLPM-65248:
name-sort: "しんさんごくむそう3"
name-en: "Shin Sangoku Musou 3"
region: "NTSC-J"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLPM-65249:
name: "CHAOS LEGION"
name-sort: "かおす れぎおん"
@@ -48850,7 +48901,7 @@ SLPM-66354:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -49916,6 +49967,8 @@ SLPM-66522:
name-sort: "しんさんごくむそう3 [こーえーていばんしりーず]"
name-en: "Shin Sangoku Musou 3 [KOEI Selection]"
region: "NTSC-J"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLPM-66523:
name: "三國志Ⅸ with パワーアップキット [KOEI The Best]"
name-sort: "さんごくし 9 with ぱわーあっぷきっと [KOEI The Best]"
@@ -51196,7 +51249,7 @@ SLPM-66731:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -52549,7 +52602,7 @@ SLPM-66961:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -53488,16 +53541,22 @@ SLPM-74215:
name-sort: "しんさんごくむそう3 [PlayStation2 the Best]"
name-en: "Shin Sangoku Musou 3 [PlayStation2 the Best]"
region: "NTSC-J"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLPM-74216:
name: "真・三國無双3 猛将伝 [PlayStation2 the Best]"
name-sort: "しんさんごくむそう3 もうしょうでん [PlayStation2 the Best]"
name-en: "Shin Sangoku Musou 3 - Moushouden [PlayStation2 the Best]"
region: "NTSC-J"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLPM-74217:
name: "真・三國無双3 [PlayStation2 the Best] [メガパック]"
name-sort: "しんさんごくむそう3 [PlayStation2 the Best] [めがぱっく]"
name-en: "Shin Sangoku Musou 3 [PlayStation2 the Best] [Mega Pack]"
region: "NTSC-J"
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLPM-74218:
name: "シャイニング・ティアーズ [PlayStation2 the Best]"
name-sort: "しゃいにんぐ・てぃあーず [PlayStation2 the Best]"
@@ -56428,8 +56487,10 @@ SLPS-20466:
name-en: "Simple 2000 Series Vol.101 - The OneeChanpon - OneeChan 2 Special Edition"
region: "NTSC-J"
gsHWFixes:
halfPixelOffset: 1 # Needed to remove part of red box.
roundSprite: 2 # Fully remove red box when upscaling .
halfPixelOffset: 5 # Aligns bloom outlines.
nativeScaling: 2 # Softens and aligns bloom outlines.
cpuSpriteRenderBW: 4 # Fixes bloom softness.
cpuSpriteRenderLevel: 1 # Needed for above.
gameFixes:
- XGKickHack # Fixes blank textures.
SLPS-20467:
@@ -60515,6 +60576,7 @@ SLPS-25640:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
memcardFilters: # Allows import of Xenosaga II save data.
- "SLPS-25640"
- "SLPS-25368"
@@ -60529,6 +60591,7 @@ SLPS-25641:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
memcardFilters:
- "SLPS-25640"
- "SLPS-25368"
@@ -66415,9 +66478,11 @@ SLUS-20591:
halfPixelOffset: 4 # Aligns post effects.
nativeScaling: 2 # Fixes post-processing and lighting smoothness and position.
SLUS-20592:
name: "HyperSonic Xtreme - HSX"
name: "HSX - HyperSonic - Xtreme"
region: "NTSC-U"
compat: 5
gsHWFixes:
recommendedBlendingLevel: 2 # Improves character portraits.
SLUS-20593:
name: "Magic Pengel - The Quest for Color"
region: "NTSC-U"
@@ -66741,6 +66806,8 @@ SLUS-20653:
name: "Dynasty Warriors 4"
region: "NTSC-U"
compat: 5
gsHWFixes:
textureInsideRT: 1 # Fixes missing graphics.
SLUS-20655:
name: "The Hobbit - The Prelude to the Lord of the Rings"
name-sort: "Hobbit, The - The Prelude to the Lord of the Rings"
@@ -67500,6 +67567,8 @@ SLUS-20789:
name: "Jeopardy!"
region: "NTSC-U"
compat: 5
gsHWFixes:
halfPixelOffset: 5 # Fixes menus.
SLUS-20790:
name: "Wheel of Fortune"
region: "NTSC-U"
@@ -71245,7 +71314,7 @@ SLUS-21376:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
@@ -71346,6 +71415,7 @@ SLUS-21389:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
memcardFilters: # Allows import of Xenosaga II save data.
- "SLUS-21389"
- "SLUS-20892"
@@ -71516,6 +71586,7 @@ SLUS-21417:
halfPixelOffset: 4 # Fixes lighting misalignment.
nativeScaling: 2 # Fixes lighting smoothness.
textureInsideRT: 1 # Fixes broken crystalline surface textures.
bilinearUpscale: 2 # Fixes off axis texture corruption in some areas.
memcardFilters:
- "SLUS-21389"
- "SLUS-20892"
@@ -71794,6 +71865,8 @@ SLUS-21456:
name: "Tony Hawk's Downhill Jam"
region: "NTSC-U"
compat: 5
speedHacks:
mtvu: 0 # Fixes tutorial crashing.
roundModes:
vu1RoundMode: 0 # Crashes without.
gsHWFixes:
@@ -72657,6 +72730,8 @@ SLUS-21628:
name: "Hot Wheels - Beat That!"
region: "NTSC-U"
compat: 5
clampModes:
eeClampMode: 3 # Fixes various car physics issues.
gsHWFixes:
roundSprite: 1 # Fixes font artifacts.
SLUS-21629:
@@ -75317,7 +75392,7 @@ SLUS-29180:
gsHWFixes:
recommendedBlendingLevel: 4 # Improves lighting on buildings and objects.
autoFlush: 2 # Properly diffuses light instead of strips of light.
halfPixelOffset: 4 # Mostly aligns post-processing.
halfPixelOffset: 5 # Mostly aligns post-processing.
nativeScaling: 2 # Improves post-processing smoothness.
getSkipCount: "GSC_BlackAndBurnoutSky"
beforeDraw: "OI_BurnoutGames"
+2 -1
View File
@@ -49,6 +49,7 @@
#define PS_PROCESS_RG 0
#define PS_SHUFFLE_ACROSS 0
#define PS_READ16_SRC 0
#define PS_WRITE_RG 0
#define PS_DST_FMT 0
#define PS_DEPTH_FMT 0
#define PS_PAL_FMT 0
@@ -1196,7 +1197,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if !PS_NO_COLOR1
output.c1 = alpha_blend;
#endif
#if PS_AFAIL == 3 && !PS_NO_COLOR1 // RGB_ONLY, no dual src blend
#if PS_AFAIL == 3 && PS_NO_COLOR1 // RGB_ONLY, no dual src blend
if (!atst_pass)
{
float RTa = NEEDS_RT_FOR_AFAIL ? RtTexture.Load(int3(input.p.xy, 0)).a : 0.0f;
+1 -1
View File
@@ -1134,7 +1134,7 @@ void ps_main()
#else
SV_Target0.rgb = C.rgb / 255.0f;
#endif
#if PS_AFAIL == 3 && !PS_NO_COLOR1 // RGB_ONLY, no dual src blend
#if PS_AFAIL == 3 && PS_NO_COLOR1 // RGB_ONLY, no dual src blend
if (!atst_pass)
SV_Target0.a = sample_from_rt().a;
#endif
+1
View File
@@ -7,6 +7,7 @@ include(GNUInstallDirs)
# Misc option
#-------------------------------------------------------------------------------
option(ENABLE_TESTS "Enables building the unit tests" ON)
option(ENABLE_QT_UI "Enables building the PCSX2 Qt interface." ON)
option(ENABLE_GSRUNNER "Enables building the GSRunner by default. It can still be built with `make pcsx2-gsrunner` otherwise." OFF)
option(LTO_PCSX2_CORE "Enable LTO/IPO/LTCG on the subset of pcsx2 that benefits most from it but not anything else")
option(USE_VTUNE "Plug VTUNE to profile GS JIT.")
+11 -11
View File
@@ -107,10 +107,19 @@ disable_compiler_warnings_for_target(cubeb)
disable_compiler_warnings_for_target(speex)
# Find the Qt components that we need.
find_package(Qt6 6.7.3 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
if(ENABLE_QT_UI)
find_package(Qt6 6.7.3 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
endif()
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.10.0)
find_package(Qt6 COMPONENTS CorePrivate GuiPrivate WidgetsPrivate REQUIRED)
endif()
# The docking system for the debugger.
find_package(KDDockWidgets-qt6 2.3.0 REQUIRED)
if(WIN32)
add_subdirectory(3rdparty/rainterface EXCLUDE_FROM_ALL)
add_subdirectory(3rdparty/rainterface EXCLUDE_FROM_ALL)
endif()
# Demangler for the debugger.
@@ -119,15 +128,6 @@ add_subdirectory(3rdparty/demangler EXCLUDE_FROM_ALL)
# Symbol table parser.
add_subdirectory(3rdparty/ccc EXCLUDE_FROM_ALL)
# The docking system for the debugger.
find_package(KDDockWidgets-qt6 2.0.0 REQUIRED)
# Add an extra include path to work around a broken include directive.
# TODO: Remove this the next time we update KDDockWidgets.
get_target_property(KDDOCKWIDGETS_INCLUDE_DIRECTORY KDAB::kddockwidgets INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(KDAB::kddockwidgets INTERFACE
${KDDOCKWIDGETS_INCLUDE_DIRECTORY}/kddockwidgets
)
# Architecture-specific.
if(_M_X86)
add_subdirectory(3rdparty/zydis EXCLUDE_FROM_ALL)
+122
View File
@@ -47,6 +47,14 @@
#include "svnrev.h"
// Down here because X11 has a lot of defines that can conflict
#if defined(__linux__)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <sys/select.h>
#include <unistd.h>
#endif
namespace GSRunner
{
static void InitializeConsole();
@@ -1086,4 +1094,118 @@ void GSRunner::StopPlatformMessagePump()
CocoaTools::StopMainThreadEventLoop();
}
#elif defined(__linux__)
static Display* s_display = nullptr;
static Window s_window = None;
static WindowInfo s_wi;
static std::atomic<bool> s_shutdown_requested{false};
bool GSRunner::CreatePlatformWindow()
{
pxAssertRel(!s_display && s_window == None, "Tried to create window when there already was one!");
s_display = XOpenDisplay(nullptr);
if (!s_display)
{
Console.Error("Failed to open X11 display");
return false;
}
int screen = DefaultScreen(s_display);
Window root = RootWindow(s_display, screen);
s_window = XCreateSimpleWindow(s_display, root, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 1,
BlackPixel(s_display, screen), WhitePixel(s_display, screen));
if (s_window == None)
{
Console.Error("Failed to create X11 window");
XCloseDisplay(s_display);
s_display = nullptr;
return false;
}
XStoreName(s_display, s_window, "PCSX2 GS Runner");
XSelectInput(s_display, s_window, StructureNotifyMask);
XMapWindow(s_display, s_window);
s_wi.type = WindowInfo::Type::X11;
s_wi.display_connection = s_display;
s_wi.window_handle = reinterpret_cast<void*>(s_window);
s_wi.surface_width = WINDOW_WIDTH;
s_wi.surface_height = WINDOW_HEIGHT;
s_wi.surface_scale = 1.0f;
XFlush(s_display);
PumpPlatformMessages();
return true;
}
void GSRunner::DestroyPlatformWindow()
{
if (s_display && s_window != None)
{
XDestroyWindow(s_display, s_window);
s_window = None;
}
if (s_display)
{
XCloseDisplay(s_display);
s_display = nullptr;
}
}
std::optional<WindowInfo> GSRunner::GetPlatformWindowInfo()
{
WindowInfo wi;
if (s_display && s_window != None)
wi = s_wi;
else
wi.type = WindowInfo::Type::Surfaceless;
return wi;
}
void GSRunner::PumpPlatformMessages(bool forever)
{
if (!s_display)
return;
do
{
while (XPending(s_display) > 0)
{
XEvent event;
XNextEvent(s_display, &event);
switch (event.type)
{
case ConfigureNotify:
{
const XConfigureEvent& configure = event.xconfigure;
s_wi.surface_width = static_cast<u32>(configure.width);
s_wi.surface_height = static_cast<u32>(configure.height);
break;
}
case DestroyNotify:
return;
default:
break;
}
}
if (s_shutdown_requested.load())
return;
if (forever)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
} while (forever && !s_shutdown_requested.load());
}
void GSRunner::StopPlatformMessagePump()
{
s_shutdown_requested.store(true);
}
#endif // _WIN32 / __APPLE__
+5 -6
View File
@@ -73,7 +73,6 @@ def run_regression_test(runner, dumpdir, renderer, upscale, renderhacks, paralle
#print("Running '%s'" % (" ".join(args)))
subprocess.run(args, env=environ, stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, creationflags=creationflags)
def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks, parallel=1):
paths = glob.glob(gsdir + "/*.*", recursive=True)
gamepaths = list(filter(lambda x: get_gs_name(x) is not None, paths))
@@ -104,12 +103,12 @@ def run_regression_tests(runner, gsdir, dumpdir, renderer, upscale, renderhacks,
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate frame dump images for regression tests")
parser.add_argument("-runner", action="store", required=True, help="Path to PCSX2 GS runner")
parser.add_argument("-gsdir", action="store", required=True, help="Directory containing GS dumps")
parser.add_argument("-dumpdir", action="store", required=True, help="Base directory to dump frames to")
parser.add_argument("-renderer", action="store", required=False, help="Renderer to use")
parser.add_argument("-runner", action="store", required=True, type=str.strip, help="Path to PCSX2 GS runner")
parser.add_argument("-gsdir", action="store", required=True, type=str.strip, help="Directory containing GS dumps")
parser.add_argument("-dumpdir", action="store", required=True, type=str.strip, help="Base directory to dump frames to")
parser.add_argument("-renderer", action="store", required=False, type=str.strip, help="Renderer to use")
parser.add_argument("-upscale", action="store", type=float, default=1, help="Upscaling multiplier to use")
parser.add_argument("-renderhacks", action="store", required=False, help="Enable HW Rendering hacks")
parser.add_argument("-renderhacks", action="store", required=False, type=str.strip, help="Enable HW Rendering hacks")
parser.add_argument("-parallel", action="store", type=int, default=1, help="Number of processes to run")
args = parser.parse_args()
+1
View File
@@ -281,6 +281,7 @@ target_link_libraries(pcsx2-qt PRIVATE
PCSX2
Qt6::Core
Qt6::Gui
Qt6::GuiPrivate
Qt6::Widgets
KDAB::kddockwidgets
)
+272 -61
View File
@@ -50,6 +50,8 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
const s32 charWidth = painter.fontMetrics().averageCharWidth();
const s32 x = charWidth; // Left padding
const s32 y = rowHeight;
const s32 displayTypeWidth = MemoryViewTypeWidth[static_cast<s32>(displayType)];
const s32 displayTypeVisualWidth = MemoryViewTypeVisualWidth[static_cast<s32>(displayType)];
rowVisible = (height / rowHeight);
rowCount = rowVisible + 1;
@@ -69,23 +71,32 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
const u32 currentRowAddress = startAddress + (i * 0x10);
s32 valX = valuexAxis;
segmentXAxis[0] = valX;
for (int j = 0; j < 16 / static_cast<s32>(displayType); j++)
for (int j = 0; j < 16 / displayTypeWidth; j++)
{
valX += charWidth;
const u32 thisSegmentsStart = currentRowAddress + (j * static_cast<s32>(displayType));
const u32 thisSegmentsStart = currentRowAddress + (j * displayTypeWidth);
segmentXAxis[j] = valX;
bool penDefault = false;
if ((selectedAddress & ~0xF) == currentRowAddress)
{
if (selectedAddress >= thisSegmentsStart && selectedAddress < (thisSegmentsStart + static_cast<s32>(displayType)))
if (selectedAddress >= thisSegmentsStart && selectedAddress < (thisSegmentsStart + displayTypeWidth))
{ // If the current byte and row we are drawing is selected
if (!selectedText)
{
s32 charsIntoSegment = ((selectedAddress - thisSegmentsStart) * 2) + ((selectedNibbleHI ? 0 : 1) ^ littleEndian);
s32 charsIntoSegment = 0;
if (displayType == MemoryViewType::FLOAT)
{
charsIntoSegment = selectedIndex;
}
else
{
charsIntoSegment = ((selectedAddress - thisSegmentsStart) * 2) + ((selectedNibbleHI ? 0 : 1) ^ littleEndian);
}
if (littleEndian)
charsIntoSegment = (static_cast<s32>(displayType) * 2) - charsIntoSegment - 1;
charsIntoSegment = displayTypeVisualWidth - charsIntoSegment - 1;
painter.setPen(QColor::fromRgb(205, 165, 0)); // SELECTED NIBBLE LINE COLOUR
const QPoint lineStart(valX + (charsIntoSegment * charWidth) + 1, y + (rowHeight * i));
painter.drawLine(lineStart, lineStart + QPoint(charWidth - 3, 0));
@@ -139,8 +150,19 @@ void MemoryViewTable::DrawTable(QPainter& painter, const QPalette& palette, s32
painter.drawText(valX, y + (rowHeight * i), valid ? FilledQStringFromValue(val, 16) : "????????????????");
break;
}
case MemoryViewType::FLOAT:
{
const u32 intVal = convertEndian<u32>(cpu.read32(thisSegmentsStart, valid));
float val = 0.0;
std::memcpy(&val, &intVal, sizeof(val));
if (penDefault && val == 0.0)
painter.setPen(QColor::fromRgb(145, 145, 155)); // ZERO BYTE COLOUR
QString floatStr = QString::number(val, 'g');
painter.drawText(valX, y + (rowHeight * i), valid ? QString("%1").arg(floatStr, displayTypeVisualWidth) : "??????????????");
break;
}
}
valX += charWidth * 2 * static_cast<s32>(displayType);
valX += charWidth * displayTypeVisualWidth;
}
// valX is our new X position after the hex values
@@ -183,7 +205,9 @@ void MemoryViewTable::SelectAt(QPoint pos)
const u32 selectedRow = (pos.y() - 2) / (rowHeight);
const s32 x = pos.x();
const s32 avgSegmentWidth = segmentXAxis[1] - segmentXAxis[0];
const u32 nibbleWidth = (avgSegmentWidth / (2 * (s32)displayType));
const s32 displayTypeWidth = MemoryViewTypeWidth[static_cast<s32>(displayType)];
const s32 displayTypeVisualWidth = MemoryViewTypeVisualWidth[static_cast<s32>(displayType)];
const u32 nibbleWidth = (avgSegmentWidth / displayTypeVisualWidth);
selectedAddress = (selectedRow * 0x10) + startAddress;
if (x <= segmentXAxis[0])
@@ -191,7 +215,7 @@ void MemoryViewTable::SelectAt(QPoint pos)
// The user clicked before the first segment
selectedText = false;
if (littleEndian)
selectedAddress += static_cast<s32>(displayType) - 1;
selectedAddress += displayTypeWidth - 1;
selectedNibbleHI = true;
}
else if (x > valuexAxis && x < textXAxis)
@@ -200,13 +224,23 @@ void MemoryViewTable::SelectAt(QPoint pos)
// The user clicked inside of the hexadecimal area
for (s32 i = 0; i < 16; i++)
{
if (i == ((16 / static_cast<s32>(displayType)) - 1) || (x >= segmentXAxis[i] && x < (segmentXAxis[i + 1])))
if (i == ((16 / displayTypeWidth) - 1) || (x >= segmentXAxis[i] && x < (segmentXAxis[i + 1])))
{
u32 indexInSegment = (x - segmentXAxis[i]) / nibbleWidth;
if (littleEndian)
indexInSegment = (static_cast<s32>(displayType) * 2) - indexInSegment - 1;
selectedAddress = selectedAddress + i * static_cast<s32>(displayType) + (indexInSegment / 2);
selectedNibbleHI = littleEndian ? indexInSegment & 1 : !(indexInSegment & 1);
indexInSegment = displayTypeVisualWidth - indexInSegment - 1;
selectedIndex = indexInSegment;
if (displayType == MemoryViewType::FLOAT)
{
// Selecting float always points to starting address of float
selectedAddress = selectedAddress + i * displayTypeWidth;
selectedNibbleHI = false;
}
else
{
selectedAddress = selectedAddress + i * displayTypeWidth + (indexInSegment / 2);
selectedNibbleHI = littleEndian ? indexInSegment & 1 : !(indexInSegment & 1);
}
break;
}
}
@@ -236,6 +270,9 @@ u128 MemoryViewTable::GetSelectedSegment(DebugInterface& cpu)
case MemoryViewType::DWORD:
val._u64[0] = convertEndian(cpu.read64(selectedAddress & ~7));
break;
case MemoryViewType::FLOAT:
val.lo = convertEndian(cpu.read32(selectedAddress & ~3));
break;
}
return val;
}
@@ -260,36 +297,111 @@ void MemoryViewTable::InsertIntoSelectedHexView(u8 value, DebugInterface& cpu)
});
}
bool MemoryViewTable::InsertFloatIntoSelectedHexView(DebugInterface& cpu)
{
// Get currently selected float as string
const u32 currentIntVal = GetSelectedSegment(cpu).lo;
float currentFloatVal = 0;
std::memcpy(&currentFloatVal, &currentIntVal, sizeof(currentFloatVal));
const QString currentfloatStr = QString("%1").arg(QString::number(currentFloatVal, 'g'), 14).trimmed();
// Prompt user to enter a new float value
bool isValidInput = false;
QString newFloatStr = QInputDialog::getText(parent, tr("Input New Float"), "",
QLineEdit::Normal, currentfloatStr, &isValidInput);
if (!isValidInput)
return false;
// Convert string into float value
bool isValidFloat = false;
const float newFloatVal = newFloatStr.toFloat(&isValidFloat);
if (!isValidFloat)
{
QMessageBox::warning(parent, tr("Input Error"), tr("Invalid float value"));
return false;
}
// Write new float value back to memory
u32 newIntVal = 0;
std::memcpy(&newIntVal, &newFloatVal, sizeof(newIntVal));
newIntVal = convertEndian(newIntVal);
const QPointer<MemoryViewTable> table(this);
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, val = newIntVal] {
cpu.write32(address, val);
QtHost::RunOnUIThread([table] {
if (!table)
return;
table->parent->update();
});
});
return true;
}
void MemoryViewTable::InsertAtCurrentSelection(const QString& text, DebugInterface& cpu)
{
if (!cpu.isValidAddress(selectedAddress))
return;
// If pasting into the hex view, also decode the input as hex bytes.
// This approach prevents one from pasting on a nibble boundary, but that is almost always
// user error, and we don't have an undo function in this view, so best to stay conservative.
QByteArray input = selectedText ? text.toUtf8() : QByteArray::fromHex(text.toUtf8());
const QPointer<MemoryViewTable> table(this);
const MemoryViewType display_type = displayType;
const bool little_endian = littleEndian;
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, input, display_type, little_endian] {
u32 currAddr = address;
for (int i = 0; i < input.size(); i++)
if (displayType == MemoryViewType::FLOAT)
{
// Convert string into float value
bool isValidFloat = false;
const float newFloatVal = text.toFloat(&isValidFloat);
if (!isValidFloat)
{
cpu.write8(currAddr, input[i]);
currAddr = nextAddress(currAddr, address, display_type, little_endian);
QMessageBox::warning(parent, tr("Input Error"), tr("Invalid float value"));
return;
}
u32 end_address = address + input.size();
QtHost::RunOnUIThread([table, end_address] {
if (!table)
return;
// Write new float value back to memory
u32 newIntVal = 0;
std::memcpy(&newIntVal, &newFloatVal, sizeof(newIntVal));
newIntVal = convertEndian(newIntVal);
table->UpdateSelectedAddress(end_address);
table->parent->update();
const QPointer<MemoryViewTable> table(this);
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, val = newIntVal] {
cpu.write32(address, val);
QtHost::RunOnUIThread([table] {
if (!table)
return;
table->parent->update();
});
});
});
}
else
{
// If pasting into the hex view, also decode the input as hex bytes.
// This approach prevents one from pasting on a nibble boundary, but that is almost always
// user error, and we don't have an undo function in this view, so best to stay conservative.
QByteArray input = selectedText ? text.toUtf8() : QByteArray::fromHex(text.toUtf8());
const QPointer<MemoryViewTable> table(this);
const MemoryViewType display_type = displayType;
const bool little_endian = littleEndian;
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, input, display_type, little_endian] {
u32 currAddr = address;
for (int i = 0; i < input.size(); i++)
{
cpu.write8(currAddr, input[i]);
currAddr = nextAddress(currAddr, address, display_type, little_endian);
}
u32 end_address = address + input.size();
QtHost::RunOnUIThread([table, end_address] {
if (!table)
return;
table->UpdateSelectedAddress(end_address);
table->parent->update();
});
});
}
}
u32 MemoryViewTable::nextAddress(u32 addr, u32 selected_address, MemoryViewType display_type, bool little_endian)
@@ -300,8 +412,8 @@ u32 MemoryViewTable::nextAddress(u32 addr, u32 selected_address, MemoryViewType
}
else
{
if (selected_address % static_cast<s32>(display_type) == 0)
return addr + (static_cast<s32>(display_type) * 2 - 1);
if (selected_address % MemoryViewTypeWidth[static_cast<s32>(display_type)] == 0)
return addr + (MemoryViewTypeWidth[static_cast<s32>(display_type)] * 2 - 1);
else
return addr - 1;
}
@@ -317,7 +429,7 @@ u32 MemoryViewTable::prevAddress(u32 addr, u32 selected_address, MemoryViewType
{
// It works
if ((addr & (static_cast<u32>(display_type) - 1)) == (static_cast<u32>(display_type) - 1))
return addr - (static_cast<s32>(display_type) * 2 - 1);
return addr - (MemoryViewTypeWidth[static_cast<s32>(display_type)] * 2 - 1);
else
return selected_address + 1;
}
@@ -325,39 +437,104 @@ u32 MemoryViewTable::prevAddress(u32 addr, u32 selected_address, MemoryViewType
void MemoryViewTable::ForwardSelection()
{
if (!littleEndian)
if (displayType == MemoryViewType::FLOAT)
{
if ((selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress + 1);
if (!littleEndian)
{
// Bump to next address if selection is at end of current float segment
if (selectedIndex >= MemoryViewTypeVisualWidth[static_cast<s32>(MemoryViewType::FLOAT)] - 1)
{
UpdateSelectedAddress(selectedAddress + 4);
selectedIndex = 0;
}
else
{
selectedIndex++;
}
}
else
{
if (selectedIndex <= 0)
{
UpdateSelectedAddress(selectedAddress + 4);
selectedIndex = MemoryViewTypeVisualWidth[static_cast<s32>(MemoryViewType::FLOAT)] - 1;
}
else
{
selectedIndex--;
}
}
}
else
{
if ((selectedNibbleHI = !selectedNibbleHI))
if (!littleEndian)
{
if (selectedAddress % static_cast<s32>(displayType) == 0)
UpdateSelectedAddress(selectedAddress + (static_cast<s32>(displayType) * 2 - 1));
else
UpdateSelectedAddress(selectedAddress - 1);
if ((selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress + 1);
}
else
{
if ((selectedNibbleHI = !selectedNibbleHI))
{
if (selectedAddress % MemoryViewTypeWidth[static_cast<s32>(displayType)] == 0)
UpdateSelectedAddress(selectedAddress + (MemoryViewTypeVisualWidth[static_cast<s32>(displayType)] - 1));
else
UpdateSelectedAddress(selectedAddress - 1);
}
}
}
}
void MemoryViewTable::BackwardSelection()
{
if (!littleEndian)
const s32 displayTypeWidth = MemoryViewTypeWidth[static_cast<s32>(displayType)];
const s32 displayTypeVisualWidth = MemoryViewTypeVisualWidth[static_cast<s32>(displayType)];
if (displayType == MemoryViewType::FLOAT)
{
if (!(selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress - 1);
if (!littleEndian)
{
// Bump to previous address if selection is at beginning of current float segment
if (selectedIndex <= 0)
{
UpdateSelectedAddress(selectedAddress - 4);
selectedIndex = displayTypeVisualWidth - 1;
}
else
{
selectedIndex--;
}
}
else
{
if (selectedIndex >= displayTypeVisualWidth - 1)
{
UpdateSelectedAddress(selectedAddress - 4);
selectedIndex = 0;
}
else
{
selectedIndex++;
}
}
}
else
{
if (!(selectedNibbleHI = !selectedNibbleHI))
if (!littleEndian)
{
// It works
if ((selectedAddress & (static_cast<u32>(displayType) - 1)) == (static_cast<u32>(displayType) - 1))
UpdateSelectedAddress(selectedAddress - (static_cast<s32>(displayType) * 2 - 1));
else
UpdateSelectedAddress(selectedAddress + 1);
if (!(selectedNibbleHI = !selectedNibbleHI))
UpdateSelectedAddress(selectedAddress - 1);
}
else
{
if (!(selectedNibbleHI = !selectedNibbleHI))
{
// It works
if ((selectedAddress & (static_cast<u32>(displayTypeWidth) - 1)) == (static_cast<u32>(displayTypeWidth) - 1))
UpdateSelectedAddress(selectedAddress - (displayTypeVisualWidth - 1));
else
UpdateSelectedAddress(selectedAddress + 1);
}
}
}
}
@@ -430,12 +607,15 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar, DebugInterface& cpu)
if (keyCharIsText)
{
// Check if key pressed is hex before insertion (QString conversion fails otherwise)
const u8 keyPressed = static_cast<u8>(QString(QChar(key)).toInt(&pressHandled, 16));
if (pressHandled)
if (displayType != MemoryViewType::FLOAT)
{
InsertIntoSelectedHexView(keyPressed, cpu);
ForwardSelection();
// Check if key pressed is hex before insertion (QString conversion fails otherwise)
const u8 keyPressed = static_cast<u8>(QString(QChar(key)).toInt(&pressHandled, 16));
if (pressHandled)
{
InsertIntoSelectedHexView(keyPressed, cpu);
ForwardSelection();
}
}
}
@@ -455,6 +635,13 @@ bool MemoryViewTable::KeyPress(int key, QChar keychar, DebugInterface& cpu)
BackwardSelection();
pressHandled = true;
break;
case Qt::Key::Key_Return:
case Qt::Key::Key_Enter:
if (displayType == MemoryViewType::FLOAT)
{
pressHandled = InsertFloatIntoSelectedHexView(cpu);
}
break;
default:
break;
}
@@ -549,7 +736,8 @@ bool MemoryView::fromJson(const JsonValueWrapper& json)
if (type == MemoryViewType::BYTE ||
type == MemoryViewType::BYTEHW ||
type == MemoryViewType::WORD ||
type == MemoryViewType::DWORD)
type == MemoryViewType::DWORD ||
type == MemoryViewType::FLOAT)
m_table.SetViewType(type);
}
@@ -647,6 +835,12 @@ void MemoryView::openContextMenu(QPoint pos)
connect(dword_action, &QAction::triggered, this, [this]() { m_table.SetViewType(MemoryViewType::DWORD); });
view_type_group->addAction(dword_action);
QAction* float_action = menu->addAction(tr("Show as float"));
float_action->setCheckable(true);
float_action->setChecked(current_view_type == MemoryViewType::FLOAT);
connect(float_action, &QAction::triggered, this, [this]() { m_table.SetViewType(MemoryViewType::FLOAT); });
view_type_group->addAction(float_action);
menu->addSeparator();
createEventActions<DebuggerEvents::AddToSavedAddresses>(menu, [this]() {
@@ -655,9 +849,16 @@ void MemoryView::openContextMenu(QPoint pos)
return std::optional(event);
});
connect(menu->addAction(tr("Copy Byte")), &QAction::triggered, this, &MemoryView::contextCopyByte);
QAction* copy_byte_action = menu->addAction(tr("Copy Byte"));
copy_byte_action->setEnabled(current_view_type != MemoryViewType::FLOAT);
connect(copy_byte_action, &QAction::triggered, this, &MemoryView::contextCopyByte);
connect(menu->addAction(tr("Copy Segment")), &QAction::triggered, this, &MemoryView::contextCopySegment);
connect(menu->addAction(tr("Copy Character")), &QAction::triggered, this, &MemoryView::contextCopyCharacter);
QAction* copy_char_action = menu->addAction(tr("Copy Character"));
copy_char_action->setEnabled(current_view_type != MemoryViewType::FLOAT);
connect(copy_char_action, &QAction::triggered, this, &MemoryView::contextCopyCharacter);
connect(menu->addAction(tr("Paste")), &QAction::triggered, this, &MemoryView::contextPaste);
menu->popup(this->mapToGlobal(pos));
@@ -673,7 +874,17 @@ void MemoryView::contextCopyByte()
void MemoryView::contextCopySegment()
{
QApplication::clipboard()->setText(QString::number(m_table.GetSelectedSegment(cpu()).lo, 16).toUpper());
if (m_table.GetViewType() == MemoryViewType::FLOAT)
{
u32 intVal = m_table.GetSelectedSegment(cpu()).lo;
float val = 0;
std::memcpy(&val, &intVal, sizeof(val));
QApplication::clipboard()->setText(QString::number(val, 'g'));
}
else
{
QApplication::clipboard()->setText(QString::number(m_table.GetSelectedSegment(cpu()).lo, 16).toUpper());
}
}
void MemoryView::contextCopyCharacter()
+23 -4
View File
@@ -20,10 +20,27 @@
enum class MemoryViewType
{
BYTE = 1,
BYTEHW = 2,
WORD = 4,
DWORD = 8,
BYTE = 0,
BYTEHW = 1,
WORD = 2,
DWORD = 3,
FLOAT = 4,
};
const s32 MemoryViewTypeWidth[] = {
1, // BYTE
2, // BYTEHW
4, // WORD
8, // DWORD
4, // FLOAT
};
const s32 MemoryViewTypeVisualWidth[] = {
2, // BYTE
4, // BYTEHW
8, // WORD
16, // DWORD
14, // FLOAT
};
class MemoryViewTable : public QObject
@@ -48,6 +65,7 @@ private:
bool selectedNibbleHI = false;
void InsertIntoSelectedHexView(u8 value, DebugInterface& cpu);
bool InsertFloatIntoSelectedHexView(DebugInterface& cpu);
template <class T>
T convertEndian(T in)
@@ -73,6 +91,7 @@ public:
u32 startAddress;
u32 selectedAddress;
s32 selectedIndex;
void UpdateStartAddress(u32 start);
void UpdateSelectedAddress(u32 selected, bool page = false);
+4
View File
@@ -131,6 +131,10 @@ namespace
// Fetch icon pixmap
const QRect r = option.rect;
const QPixmap pix = qvariant_cast<QPixmap>(index.data(Qt::DecorationRole));
if (pix.isNull())
return;
const int pix_width = static_cast<int>(pix.width() / pix.devicePixelRatio());
const int pix_height = static_cast<int>(pix.height() / pix.devicePixelRatio());
-32
View File
@@ -879,38 +879,6 @@ void EmuThread::endCapture()
MTGS::RunOnGSThread(&GSEndCapture);
}
void EmuThread::setAudioOutputVolume(int volume, int fast_forward_volume)
{
if (!isOnEmuThread())
{
QMetaObject::invokeMethod(this, "setAudioOutputVolume", Qt::QueuedConnection, Q_ARG(int, volume),
Q_ARG(int, fast_forward_volume));
return;
}
if (!VMManager::HasValidVM())
return;
EmuConfig.SPU2.OutputVolume = static_cast<u32>(volume);
EmuConfig.SPU2.FastForwardVolume = static_cast<u32>(fast_forward_volume);
SPU2::SetOutputVolume(SPU2::GetResetVolume());
}
void EmuThread::setAudioOutputMuted(bool muted)
{
if (!isOnEmuThread())
{
QMetaObject::invokeMethod(this, "setAudioOutputMuted", Qt::QueuedConnection, Q_ARG(bool, muted));
return;
}
if (!VMManager::HasValidVM())
return;
EmuConfig.SPU2.OutputMuted = muted;
SPU2::SetOutputVolume(SPU2::GetResetVolume());
}
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool recreate_window)
{
// Check if we're wanting to get exclusive fullscreen. This should be safe to read, since we're going to be calling from the GS thread.
-2
View File
@@ -112,8 +112,6 @@ public Q_SLOTS:
void queueSnapshot(quint32 gsdump_frames);
void beginCapture(const QString& path);
void endCapture();
void setAudioOutputVolume(int volume, int fast_forward_volume);
void setAudioOutputMuted(bool muted);
Q_SIGNALS:
bool messageConfirmed(const QString& title, const QString& message);
+18 -18
View File
@@ -73,21 +73,21 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
// for per-game, just use the normal path, since it needs to re-read/apply
if (!dialog()->isPerGameSettings())
{
m_ui.volume->setValue(dialog()->getEffectiveIntValue("SPU2/Output", "OutputVolume", 100));
m_ui.standardVolume->setValue(dialog()->getEffectiveIntValue("SPU2/Output", "StandardVolume", 100));
m_ui.fastForwardVolume->setValue(dialog()->getEffectiveIntValue("SPU2/Output", "FastForwardVolume", 100));
m_ui.muted->setChecked(dialog()->getEffectiveBoolValue("SPU2/Output", "OutputMuted", false));
connect(m_ui.volume, &QSlider::valueChanged, this, &AudioSettingsWidget::onOutputVolumeChanged);
connect(m_ui.standardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onStandardVolumeChanged);
connect(m_ui.fastForwardVolume, &QSlider::valueChanged, this, &AudioSettingsWidget::onFastForwardVolumeChanged);
connect(m_ui.muted, &QCheckBox::checkStateChanged, this, &AudioSettingsWidget::onOutputMutedChanged);
updateVolumeLabel();
}
else
{
SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.volume, m_ui.volumeLabel, tr("%"), "SPU2/Output", "OutputVolume", 100);
SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.standardVolume, m_ui.standardVolumeLabel, tr("%"), "SPU2/Output", "StandardVolume", 100);
SettingWidgetBinder::BindWidgetAndLabelToIntSetting(sif, m_ui.fastForwardVolume, m_ui.fastForwardVolumeLabel, tr("%"), "SPU2/Output", "FastForwardVolume", 100);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.muted, "SPU2/Output", "OutputMuted", false);
}
connect(m_ui.resetVolume, &QToolButton::clicked, this, [this]() { resetVolume(false); });
connect(m_ui.resetStandardVolume, &QToolButton::clicked, this, [this]() { resetVolume(false); });
connect(m_ui.resetFastForwardVolume, &QToolButton::clicked, this, [this]() { resetVolume(true); });
dialog()->registerWidgetHelp(
@@ -103,8 +103,8 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
m_ui.outputLatencyMS, tr("Output Latency"), tr("%1 ms").arg(AudioStreamParameters::DEFAULT_OUTPUT_LATENCY_MS),
tr("Determines the latency from the buffer to the host audio output. This can be set lower than the target latency "
"to reduce audio delay."));
dialog()->registerWidgetHelp(m_ui.volume, tr("Output Volume"), "100%",
tr("Controls the volume of the audio played on the host."));
dialog()->registerWidgetHelp(m_ui.standardVolume, tr("Standard Volume"), "100%",
tr("Controls the volume of the audio played on the host at normal speed."));
dialog()->registerWidgetHelp(m_ui.fastForwardVolume, tr("Fast Forward Volume"), "100%",
tr("Controls the volume of the audio played on the host when fast forwarding."));
dialog()->registerWidgetHelp(m_ui.muted, tr("Mute All Sound"), tr("Unchecked"),
@@ -118,9 +118,9 @@ AudioSettingsWidget::AudioSettingsWidget(SettingsWindow* settings_dialog, QWidge
tr("When running outside of 100% speed, adjusts the tempo on audio instead of dropping frames. Produces much nicer fast-forward/slowdown audio."));
dialog()->registerWidgetHelp(m_ui.stretchSettings, tr("Stretch Settings"), tr("N/A"),
tr("These settings fine-tune the behavior of the SoundTouch audio time stretcher when running outside of 100% speed."));
dialog()->registerWidgetHelp(m_ui.resetVolume, tr("Reset Volume"), tr("N/A"),
dialog()->isPerGameSettings() ? tr("Resets output volume back to the global/inherited setting.") :
tr("Resets output volume back to the default."));
dialog()->registerWidgetHelp(m_ui.resetStandardVolume, tr("Reset Standard Volume"), tr("N/A"),
dialog()->isPerGameSettings() ? tr("Resets standard volume back to the global/inherited setting.") :
tr("Resets standard volume back to the default."));
dialog()->registerWidgetHelp(m_ui.resetFastForwardVolume, tr("Reset Fast Forward Volume"), tr("N/A"),
dialog()->isPerGameSettings() ? tr("Resets fast forward volume back to the global/inherited setting.") :
tr("Resets fast forward volume back to the default."));
@@ -292,7 +292,7 @@ void AudioSettingsWidget::updateLatencyLabel()
void AudioSettingsWidget::updateVolumeLabel()
{
m_ui.volumeLabel->setText(tr("%1%").arg(m_ui.volume->value()));
m_ui.standardVolumeLabel->setText(tr("%1%").arg(m_ui.standardVolume->value()));
m_ui.fastForwardVolumeLabel->setText(tr("%1%").arg(m_ui.fastForwardVolume->value()));
}
@@ -303,18 +303,18 @@ void AudioSettingsWidget::onMinimalOutputLatencyChanged()
updateLatencyLabel();
}
void AudioSettingsWidget::onOutputVolumeChanged(int new_value)
void AudioSettingsWidget::onStandardVolumeChanged(const int new_value)
{
// only called for base settings
pxAssert(!dialog()->isPerGameSettings());
Host::SetBaseIntSettingValue("SPU2/Output", "OutputVolume", new_value);
Host::SetBaseIntSettingValue("SPU2/Output", "StandardVolume", new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
updateVolumeLabel();
}
void AudioSettingsWidget::onFastForwardVolumeChanged(int new_value)
void AudioSettingsWidget::onFastForwardVolumeChanged(const int new_value)
{
// only called for base settings
pxAssert(!dialog()->isPerGameSettings());
@@ -325,7 +325,7 @@ void AudioSettingsWidget::onFastForwardVolumeChanged(int new_value)
updateVolumeLabel();
}
void AudioSettingsWidget::onOutputMutedChanged(int new_state)
void AudioSettingsWidget::onOutputMutedChanged(const int new_state)
{
// only called for base settings
pxAssert(!dialog()->isPerGameSettings());
@@ -478,11 +478,11 @@ void AudioSettingsWidget::onStretchSettingsClicked()
dlg.exec();
}
void AudioSettingsWidget::resetVolume(bool fast_forward)
void AudioSettingsWidget::resetVolume(const bool fast_forward)
{
const char* key = fast_forward ? "FastForwardVolume" : "OutputVolume";
QSlider* const slider = fast_forward ? m_ui.fastForwardVolume : m_ui.volume;
QLabel* const label = fast_forward ? m_ui.fastForwardVolumeLabel : m_ui.volumeLabel;
const char* key = fast_forward ? "FastForwardVolume" : "StandardVolume";
QSlider* const slider = fast_forward ? m_ui.fastForwardVolume : m_ui.standardVolume;
QLabel* const label = fast_forward ? m_ui.fastForwardVolumeLabel : m_ui.standardVolumeLabel;
if (dialog()->isPerGameSettings())
{
+4 -4
View File
@@ -27,9 +27,9 @@ private Q_SLOTS:
void updateLatencyLabel();
void updateVolumeLabel();
void onMinimalOutputLatencyChanged();
void onOutputVolumeChanged(int new_value);
void onFastForwardVolumeChanged(int new_value);
void onOutputMutedChanged(int new_state);
void onStandardVolumeChanged(const int new_value);
void onFastForwardVolumeChanged(const int new_value);
void onOutputMutedChanged(const int new_state);
void onExpansionSettingsClicked();
void onStretchSettingsClicked();
@@ -38,7 +38,7 @@ private:
AudioBackend getEffectiveBackend() const;
AudioExpansionMode getEffectiveExpansionMode() const;
u32 getEffectiveExpansionBlockSize() const;
void resetVolume(bool fast_forward);
void resetVolume(const bool fast_forward);
Ui::AudioSettingsWidget m_ui;
u32 m_output_device_latency = 0;
+5 -5
View File
@@ -219,14 +219,14 @@
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Output Volume:</string>
<string>Standard Volume:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSlider" name="volume">
<widget class="QSlider" name="standardVolume">
<property name="maximum">
<number>200</number>
</property>
@@ -245,7 +245,7 @@
</widget>
</item>
<item>
<widget class="QLabel" name="volumeLabel">
<widget class="QLabel" name="standardVolumeLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
@@ -261,9 +261,9 @@
</widget>
</item>
<item>
<widget class="QToolButton" name="resetVolume">
<widget class="QToolButton" name="resetStandardVolume">
<property name="toolTip">
<string>Reset Volume</string>
<string>Reset Standard Volume</string>
</property>
<property name="icon">
<iconset theme="restart-line"/>
@@ -103,7 +103,7 @@
<item row="4" column="0" colspan="2">
<layout class="QGridLayout" name="optionLayout" rowstretch="0,0,0,0,0,0,0,0,0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<property name="topMargin">
<number>10</number>
@@ -251,6 +251,13 @@
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="showPatches">
<property name="text">
<string>Show Patches</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
@@ -317,10 +324,10 @@
<item row="3" column="0" colspan="2">
<spacer name="checkboxSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@@ -343,7 +350,7 @@
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
+15 -68
View File
@@ -71,15 +71,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
setupTab(m_capture, tr("Media Capture"));
m_advanced_tab = setupTab(m_advanced, tr("Advanced"));
#ifndef PCSX2_DEVBUILD
if (!dialog()->isPerGameSettings())
{
// We removed hardware fixes from global settings, but people in the past did set this stuff globally.
// So, just reset it all. We can remove this code at some point in the future.
resetManualHardwareFixes();
}
#endif
//////////////////////////////////////////////////////////////////////////
// Display Settings
//////////////////////////////////////////////////////////////////////////
@@ -243,6 +234,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showGSStats, "EmuCore/GS", "OsdShowGSStats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showIndicators, "EmuCore/GS", "OsdShowIndicators", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showSettings, "EmuCore/GS", "OsdShowSettings", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showPatches, "EmuCore/GS", "OsdshowPatches", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showInputs, "EmuCore/GS", "OsdShowInputs", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showFrameTimes, "EmuCore/GS", "OsdShowFrameTimes", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_osd.showVersion, "EmuCore/GS", "OsdShowVersion", false);
@@ -547,9 +539,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_hw.blending, tr("Blending Accuracy"), tr("Basic (Recommended)"),
tr("Control the accuracy level of the GS blending unit emulation.<br> "
"The higher the setting, the more blending is emulated in the shader accurately, and the higher the speed penalty will "
"be.<br> "
"Note that Direct3D 12's blending is reduced in capability compared to OpenGL/Vulkan/Direct3D 11."));
"The higher the setting, the more blending is emulated in the shader accurately, and the higher the speed penalty will be."));
dialog()->registerWidgetHelp(m_advanced.texturePreloading, tr("Texture Preloading"), tr("Full (Hash Cache)"),
tr("Uploads entire textures at once instead of in small pieces, avoiding redundant uploads when possible. "
@@ -756,6 +746,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_osd.showSettings, tr("Show Settings"), tr("Unchecked"),
tr("Displays various settings and the current values of those settings, useful for debugging."));
dialog()->registerWidgetHelp(m_osd.showPatches, tr("Show Patches"), tr("Unchecked"),
tr("Shows the amount of currently active patches/cheats on the bottom-right corner of the display."));
dialog()->registerWidgetHelp(m_osd.showInputs, tr("Show Inputs"), tr("Unchecked"),
tr("Shows the current controller state of the system in the bottom-left corner of the display."));
@@ -776,6 +769,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_osd.warnAboutUnsafeSettings, tr("Warn About Unsafe Settings"), tr("Checked"),
tr("Displays warnings when settings are enabled which may break games."));
connect(m_osd.showSettings, &QCheckBox::checkStateChanged, this,
&GraphicsSettingsWidget::onOsdShowSettingsToggled);
}
// Recording tab
@@ -1039,6 +1035,12 @@ void GraphicsSettingsWidget::onEnableVideoCaptureChanged()
m_capture.videoCaptureOptions->setEnabled(enabled);
}
void GraphicsSettingsWidget::onOsdShowSettingsToggled()
{
const bool enabled = dialog()->getEffectiveBoolValue("EmuCore/GS", "OsdShowSettings", false);
m_osd.showPatches->setEnabled(enabled);
}
void GraphicsSettingsWidget::onEnableVideoCaptureArgumentsChanged()
{
const bool enabled = dialog()->getEffectiveBoolValue("EmuCore/GS", "EnableVideoCaptureParameters", false);
@@ -1102,7 +1104,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
const bool is_software = (type == GSRendererType::SW);
const bool is_auto = (type == GSRendererType::Auto);
const bool is_vk = (type == GSRendererType::VK);
const bool is_disable_barriers = (type == GSRendererType::DX12 || type == GSRendererType::Metal || type == GSRendererType::SW);
const bool is_disable_barriers = (type == GSRendererType::Metal || type == GSRendererType::SW);
const bool hw_fixes = (is_hardware && m_hw.enableHWFixes && m_hw.enableHWFixes->checkState() == Qt::Checked);
QWidget* prev_tab;
@@ -1304,58 +1306,3 @@ void GraphicsSettingsWidget::onUpscaleMultiplierChanged()
dialog()->setFloatSettingValue("EmuCore/GS", "upscale_multiplier",
data.isValid() ? std::optional<float>(data.toFloat()) : std::optional<float>());
}
void GraphicsSettingsWidget::resetManualHardwareFixes()
{
bool changed = false;
{
auto lock = Host::GetSettingsLock();
SettingsInterface* const si = Host::Internal::GetBaseSettingsLayer();
auto check_bool = [&](const char* section, const char* key, bool expected) {
if (si->GetBoolValue(section, key, expected) != expected)
{
si->SetBoolValue(section, key, expected);
changed = true;
}
};
auto check_int = [&](const char* section, const char* key, s32 expected) {
if (si->GetIntValue(section, key, expected) != expected)
{
si->SetIntValue(section, key, expected);
changed = true;
}
};
check_bool("EmuCore/GS", "UserHacks", false);
check_int("EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
check_int("EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
check_int("EmuCore/GS", "UserHacks_GPUTargetCLUTMode", 0);
check_int("EmuCore/GS", "UserHacks_SkipDraw_Start", 0);
check_int("EmuCore/GS", "UserHacks_SkipDraw_End", 0);
check_bool("EmuCore/GS", "UserHacks_AutoFlush", false);
check_bool("EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
check_bool("EmuCore/GS", "UserHacks_DisableDepthSupport", false);
check_bool("EmuCore/GS", "UserHacks_Disable_Safe_Features", false);
check_bool("EmuCore/GS", "UserHacks_DisableRenderFixes", false);
check_bool("EmuCore/GS", "preload_frame_with_gs_data", false);
check_bool("EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
check_int("EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
check_bool("EmuCore/GS", "UserHacks_ReadTCOnClose", false);
check_bool("EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
check_bool("EmuCore/GS", "paltex", false);
check_int("EmuCore/GS", "UserHacks_HalfPixelOffset", 0);
check_int("EmuCore/GS", "UserHacks_native_scaling", static_cast<int>(GSNativeScaling::Off));
check_int("EmuCore/GS", "UserHacks_round_sprite_offset", 0);
check_int("EmuCore/GS", "UserHacks_TCOffsetX", 0);
check_int("EmuCore/GS", "UserHacks_TCOffsetY", 0);
check_bool("EmuCore/GS", "UserHacks_align_sprite_X", false);
check_bool("EmuCore/GS", "UserHacks_merge_pp_sprite", false);
check_bool("EmuCore/GS", "UserHacks_ForceEvenSpritePosition", false);
check_bool("EmuCore/GS", "UserHacks_BilinearHack", false);
}
if (changed)
Host::CommitBaseSettingChanges();
}
+1 -1
View File
@@ -54,12 +54,12 @@ private Q_SLOTS:
void onVideoCaptureAutoResolutionChanged();
void onEnableAudioCaptureChanged();
void onEnableAudioCaptureArgumentsChanged();
void onOsdShowSettingsToggled();
private:
GSRendererType getEffectiveRenderer() const;
void updateRendererDependentOptions();
void populateUpscaleMultipliers(u32 max_upscale_multiplier);
void resetManualHardwareFixes();
Ui::GraphicsSettingsHeader m_header;
Ui::GraphicsDisplaySettingsTab m_display;
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
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
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
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
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
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
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
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
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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

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