Compare commits

...

28 Commits

Author SHA1 Message Date
TJnotJT eb5e3fcd13 GS/HW: Make all vertices the same color in certain flat shading cases.
Fixes issues with AA1 with flat shading.
2026-07-10 10:16:51 +02:00
TheLastRar 8f8968cbb1 Deps: Revert workaround for MSVC ICE with PlutoVG
This reverts commit 7ade24fc77.
2026-07-09 18:40:47 -04:00
Silent ac83e93426 GameDB: Update Gran Turismo entries to use anchors and overrides 2026-07-09 15:53:38 -04:00
Silent 78d0cf4a4f YAML: Add support for resolving anchors, and use it for GameDB 2026-07-09 15:53:38 -04:00
lightningterror 2444c6090b GS: Fix Compiler warnings. 2026-07-09 18:06:50 +02:00
JordanTheToaster 7e49bc5a50 3rdparty: Update Vulkan-Headers to v1.4.356 2026-07-09 12:05:39 +02:00
JordanTheToaster 457719d0cc Deps: Update libjpegturbo to 3.2.0 2026-07-09 12:05:39 +02:00
JordanTheToaster c3e63d5a0e 3rdparty: Update CPUInfo to commit b1a5d63 2026-07-09 12:05:39 +02:00
TJnotJT 8c1b3f8ce3 QT: Remove ROV tabstop in graphics advanced section.
No longer used in this part of the GUI.
2026-07-09 11:44:18 +02:00
TJnotJT 55b37b794c GS:DX12: Index pipelines in correct shader cache table. 2026-07-09 11:43:44 +02:00
TJnotJT 1aca396e34 GS/DX12: Make sure null texture has correct descriptor type for slot. 2026-07-09 11:43:17 +02:00
PCSX2 Bot cb000a5101 [ci skip] Qt: Update Base Translation. 2026-07-08 20:48:48 -04:00
chaoticgd 1ae2a96747 Debugger: Allow strings to be edited from the symbol trees 2026-07-08 15:02:23 +02:00
SternXD 6834086c37 Achievements: Improve subset sidebar nav/focus behavior 2026-07-08 15:00:49 +02:00
SternXD b33b493c6e FullscreenUI: Use locale time on landing page instead of 24 hour 2026-07-08 15:00:49 +02:00
SternXD 437fe1e442 FullscreenUI: Move game info and cover art to top left of pause menu 2026-07-08 15:00:49 +02:00
TellowKrinkle 2a348dcff9 GS:MTL: Add ROV support for RW Textures Tier 1 hardware 2026-07-08 14:08:29 +02:00
TellowKrinkle 2821986fb2 GS:MTL: Implement ROV 2026-07-08 14:08:29 +02:00
TellowKrinkle 1a13f9707b GS:MTL: GPU ROV feature detection 2026-07-08 14:08:29 +02:00
TellowKrinkle ff1b47942c GS:MTL: Reduce unneccessary texture rebinds 2026-07-08 14:08:29 +02:00
TellowKrinkle 13e4973b48 GS:MTL: Fix texture flags 2026-07-08 14:08:29 +02:00
raid273 fd46054fb7 GameDB: Remove cpuSpriteRender from Mana-Khemia 2026-07-04 21:51:16 +02:00
raid273 f618f39bee GameDB: Various fixes 2026-07-04 21:51:16 +02:00
JordanTheToaster 603486ad2f Deps: Update SDL3 to v3.4.12 2026-07-04 21:49:31 +02:00
lightningterror a4b11651a4 GS: Better handle imgui texture creation hazards. 2026-07-04 20:40:55 +02:00
PCSX2 Bot b6e9a04433 [ci skip] Qt: Update Base Translation. 2026-07-03 20:22:06 -04:00
Mrlinkwii 05a2bbbf35 GameDB: remove gamefixes from silent hill 3 2026-07-03 16:09:21 -04:00
Oleg Tolmatcev 16b6572826 Misc: another COM code cleanup 2026-07-03 16:08:58 -04:00
94 changed files with 2303 additions and 1134 deletions
@@ -23,11 +23,11 @@ QTAPNG=1.3.0
FFMPEG=8.1
LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.4.1
LIBJPEGTURBO=3.2.0
LIBPNG=1.6.58
LIBWEBP=1.6.0
NVENC=13.0.19.0
SDL=SDL3-3.4.10
SDL=SDL3-3.4.12
LZ4=1.10.0
VULKAN=1.4.328.1
ZSTD=1.5.7
@@ -57,10 +57,10 @@ f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
96e5c2d7f2c482a60d5804da48a2eb9a0db0719b2c65dcc169fbfdcf37f3a45d libbacktrace-$LIBBACKTRACE.tar.gz
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7 $SDL.tar.gz
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.4.10.tar.gz",
"sha256": "12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785"
"url": "https://libsdl.org/release/SDL3-3.4.12.tar.gz",
"sha256": "f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7"
}
],
"cleanup": [
@@ -47,12 +47,12 @@ QT=6.11.1
QTAPNG=1.3.0
FREETYPE=2.14.3
SDL=SDL3-3.4.10
SDL=SDL3-3.4.12
HARFBUZZ=14.2.0
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.58
LIBJPEGTURBO=3.1.4.1
LIBJPEGTURBO=3.2.0
LIBWEBP=1.6.0
FFMPEG=8.1
MOLTENVK=1.4.1
@@ -93,14 +93,14 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7 $SDL.tar.gz
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c MoltenVK-$MOLTENVK.tar.gz
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
@@ -24,12 +24,12 @@ QT=6.11.1
QTAPNG=1.3.0
FREETYPE=2.14.3
SDL=SDL3-3.4.10
SDL=SDL3-3.4.12
HARFBUZZ=14.2.0
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.58
LIBJPEGTURBO=3.1.4.1
LIBJPEGTURBO=3.2.0
LIBWEBP=1.6.0
FFMPEG=8.1
MOLTENVK=1.4.1
@@ -69,14 +69,14 @@ b2bf6c6845ac175ed7f819145483ba4676f617aaa6a5012c8efee63c8bbac413 qtimageformats
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
12b34280415ec8418c864408b93d008a20a6530687ee613d60bfbd20411f2785 $SDL.tar.gz
f07b958a9ac5020fb7a44cadb957f658b2149c3c8abb4f63145fac9303249db7 $SDL.tar.gz
c652d5d94971031654ab3989891a490a895d3e3f2b71171c62692b28e94b1b93 harfbuzz-$HARFBUZZ.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 libpng-$LIBPNG-apng.patch.gz
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e libjpeg-turbo-$LIBJPEGTURBO.tar.gz
b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG.tar.xz
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c v$MOLTENVK.tar.gz
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
+4 -4
View File
@@ -60,8 +60,8 @@ set QTAPNG=1.3.0
set FREETYPE=2.14.3
set HARFBUZZ=14.2.0
set SDL=SDL3-3.4.10
set LIBJPEGTURBO=3.1.4.1
set SDL=SDL3-3.4.12
set LIBJPEGTURBO=3.2.0
set LIBPNG=1658
set LIBPNGLONG=1.6.58
set LZ4=1.10.0
@@ -92,9 +92,9 @@ call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip bb2f83255706b1c92d731541c7cefaf98bb5b93e8f76d16f6deda05225ff20ee || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1658.zip b32f170855dbbe3e6d9e645af40b538137041773672c3ba3e02db5816c82d376 || goto error
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e || 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" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d4de8967a49c0451e775a0c1e9022092c19fdef41ba38a83fcf031c5a6496e2 || 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://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
+4 -5
View File
@@ -77,8 +77,8 @@ set LIBX264=b35605ace3ddf7c1a5d67a2eb553f034aef41d55
set FREETYPE=2.14.3
set HARFBUZZ=14.2.0
set SDL=SDL3-3.4.10
set LIBJPEGTURBO=3.1.4.1
set SDL=SDL3-3.4.12
set LIBJPEGTURBO=3.2.0
set LIBPNG=1658
set LIBPNGLONG=1.6.58
set LZ4=1.10.0
@@ -120,9 +120,9 @@ call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip bb2f83255706b1c92d731541c7cefaf98bb5b93e8f76d16f6deda05225ff20ee || goto error
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1658.zip b32f170855dbbe3e6d9e645af40b538137041773672c3ba3e02db5816c82d376 || goto error
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz eee7dea22ed502868017971c86c63c4ed1e6085de0baebfdcc3d3322f00f3eb0 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 6f30092cef9fb839779646608f4ee14ae3cbac989c47fa05e841b0841f09878e || 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" be8dafef3ecc956e736cfd1c242c10737f3ad589a866f25ba437b7f2f6d5ced2 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 3d4de8967a49c0451e775a0c1e9022092c19fdef41ba38a83fcf031c5a6496e2 || 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://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
@@ -471,7 +471,6 @@ echo "Building PlutoVG..."
rmdir /S /Q "plutovg-%PLUTOVG%"
%SEVENZIP% x "plutovg-%PLUTOVG%.zip" || goto error
cd "plutovg-%PLUTOVG%" || goto error
%PATCH% -p1 < "%SCRIPTDIR%\plutovg-workaround-msvc-ice.patch" || goto error
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 || goto error
cmake --build build --parallel || goto error
ninja -C build install || goto error
@@ -1,52 +0,0 @@
diff --git a/source/plutovg-font.c b/source/plutovg-font.c
index 0ad89b1..110de7f 100644
--- a/source/plutovg-font.c
+++ b/source/plutovg-font.c
@@ -836,7 +836,8 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
size_t family_index = 0;
if(unicode_family_name) {
const stbtt_uint8* family_name = unicode_family_name;
- while(family_length) {
+ //while(family_length) {
+ for (int i = family_length; i > 0; i -= 2) {
stbtt_uint16 ch = family_name[0] * 256 + family_name[1];
if(ch < 0x80) {
entry->family[family_index++] = ch;
@@ -853,7 +854,8 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
entry->family[family_index++] = (0x80 + ((c) & 0x3f));
family_name += 2;
- family_length -= 2;
+ //family_length -= 2;
+ i -= 2;
} else {
entry->family[family_index++] = (0xe0 + (ch >> 12));
entry->family[family_index++] = (0x80 + ((ch >> 6) & 0x3f));
@@ -861,7 +863,7 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
}
family_name += 2;
- family_length -= 2;
+ //family_length -= 2;
}
entry->family[family_index] = '\0';
@@ -902,7 +904,8 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
};
const stbtt_uint8* family_name = roman_family_name;
- while(family_length) {
+ //while(family_length) {
+ for (int i2 = family_length; i2 > 0; i2--) {
stbtt_uint16 ch = MAC_ROMAN_TABLE[family_name[0]];
if(ch < 0x80) {
entry->family[family_index++] = ch;
@@ -916,7 +919,7 @@ int plutovg_font_face_cache_load_file(plutovg_font_face_cache_t* cache, const ch
}
family_name += 1;
- family_length -= 1;
+ //family_length -= 1;
}
entry->family[family_index] = '\0';
+2
View File
@@ -361,6 +361,8 @@ enum cpuinfo_uarch {
cpuinfo_uarch_raptor_cove = 0x0010020F,
/** Intel Redwood Cove microarchitecture (Granite Rapids). */
cpuinfo_uarch_redwood_cove = 0x00100210,
/** Intel Coyote Cove microarchitecture. */
cpuinfo_uarch_coyote_cove = 0x00100211,
/** Pentium 4 with Willamette, Northwood, or Foster cores. */
cpuinfo_uarch_willamette = 0x00100300,
+3
View File
@@ -1,3 +1,6 @@
/* for syscall() */
#define _DEFAULT_SOURCE
#include <stdbool.h>
#include <stddef.h>
+2
View File
@@ -36,6 +36,7 @@ enum cpuinfo_arm_chipset_vendor {
cpuinfo_arm_chipset_vendor_texas_instruments,
cpuinfo_arm_chipset_vendor_unisoc,
cpuinfo_arm_chipset_vendor_wondermedia,
cpuinfo_arm_chipset_vendor_google,
cpuinfo_arm_chipset_vendor_max,
};
@@ -73,6 +74,7 @@ enum cpuinfo_arm_chipset_series {
cpuinfo_arm_chipset_series_unisoc_t,
cpuinfo_arm_chipset_series_unisoc_ums,
cpuinfo_arm_chipset_series_wondermedia_wm,
cpuinfo_arm_chipset_series_google_tensor,
cpuinfo_arm_chipset_series_max,
};
+1 -1
View File
@@ -366,7 +366,7 @@ CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_f
CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_hardware_chipname(
const char ro_hardware_chipname[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_soc_model(
const char ro_soc_model[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
const char soc_model[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]);
#else
CPUINFO_INTERNAL struct cpuinfo_arm_chipset cpuinfo_arm_linux_decode_chipset_from_proc_cpuinfo_revision(
const char proc_cpuinfo_revision[restrict static CPUINFO_REVISION_VALUE_MAX]);
+61 -10
View File
@@ -3492,9 +3492,20 @@ struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_chipname(
};
}
/*
* Decodes chipset name from ro.soc.model Android system property.
*
* @param[in] soc_model - ro.soc.model value.
*
* @returns Decoded chipset name. If chipset could not be decoded, the resulting
* structure would use `unknown` vendor and series identifiers.
*/
struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_soc_model(
const char soc_model[restrict static CPUINFO_BUILD_PROP_VALUE_MAX]) {
struct cpuinfo_arm_chipset chipset;
struct cpuinfo_arm_chipset chipset = {
.vendor = cpuinfo_arm_chipset_vendor_unknown,
.series = cpuinfo_arm_chipset_series_unknown,
};
const size_t soc_model_length = strnlen(soc_model, CPUINFO_BUILD_PROP_VALUE_MAX);
const char* soc_model_end = soc_model + soc_model_length;
@@ -3516,10 +3527,28 @@ struct cpuinfo_arm_chipset cpuinfo_arm_android_decode_chipset_from_ro_soc_model(
return chipset;
}
return (struct cpuinfo_arm_chipset){
.vendor = cpuinfo_arm_chipset_vendor_unknown,
.series = cpuinfo_arm_chipset_series_unknown,
};
if (soc_model[0] != '\0') {
if (strncmp(soc_model, "Tensor", 6) == 0) {
chipset.vendor = cpuinfo_arm_chipset_vendor_google;
chipset.series = cpuinfo_arm_chipset_series_google_tensor;
const char* suffix_start = soc_model + 6;
while (*suffix_start == ' ') {
suffix_start++;
}
const size_t suffix_length = strnlen(suffix_start, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
if (suffix_length > 0) {
strncpy(chipset.suffix, suffix_start, suffix_length);
}
return chipset;
} else if (strncmp(soc_model, "GS201", 5) == 0) {
chipset.vendor = cpuinfo_arm_chipset_vendor_google;
chipset.series = cpuinfo_arm_chipset_series_google_tensor;
strncpy(chipset.suffix, "G2", 2);
return chipset;
}
}
return chipset;
}
#endif /* __ANDROID__ */
@@ -3858,6 +3887,7 @@ static const char* chipset_vendor_string[cpuinfo_arm_chipset_vendor_max] = {
[cpuinfo_arm_chipset_vendor_texas_instruments] = "Texas Instruments",
[cpuinfo_arm_chipset_vendor_unisoc] = "Unisoc",
[cpuinfo_arm_chipset_vendor_wondermedia] = "WonderMedia",
[cpuinfo_arm_chipset_vendor_google] = "Google",
};
/* Map from ARM chipset series ID to its string representation */
@@ -3895,6 +3925,7 @@ static const char* chipset_series_string[cpuinfo_arm_chipset_series_max] = {
[cpuinfo_arm_chipset_series_unisoc_t] = "T",
[cpuinfo_arm_chipset_series_unisoc_ums] = "UMS",
[cpuinfo_arm_chipset_series_wondermedia_wm] = "WM",
[cpuinfo_arm_chipset_series_google_tensor] = "Tensor",
};
/* Convert chipset name represented by cpuinfo_arm_chipset structure to a string
@@ -3913,14 +3944,35 @@ void cpuinfo_arm_chipset_to_string(
const char* vendor_string = chipset_vendor_string[vendor];
const char* series_string = chipset_series_string[series];
const uint32_t model = chipset->model;
const size_t suffix_length = strnlen(chipset->suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
if (model == 0) {
if (series == cpuinfo_arm_chipset_series_unknown) {
strncpy(name, vendor_string, CPUINFO_ARM_CHIPSET_NAME_MAX);
if (suffix_length > 0) {
if (series == cpuinfo_arm_chipset_series_unknown) {
snprintf(
name,
CPUINFO_ARM_CHIPSET_NAME_MAX,
"%s %.*s",
vendor_string,
(int)suffix_length,
chipset->suffix);
} else {
snprintf(
name,
CPUINFO_ARM_CHIPSET_NAME_MAX,
"%s %s %.*s",
vendor_string,
series_string,
(int)suffix_length,
chipset->suffix);
}
} else {
snprintf(name, CPUINFO_ARM_CHIPSET_NAME_MAX, "%s %s", vendor_string, series_string);
if (series == cpuinfo_arm_chipset_series_unknown) {
strncpy(name, vendor_string, CPUINFO_ARM_CHIPSET_NAME_MAX);
} else {
snprintf(name, CPUINFO_ARM_CHIPSET_NAME_MAX, "%s %s", vendor_string, series_string);
}
}
} else {
const size_t suffix_length = strnlen(chipset->suffix, CPUINFO_ARM_CHIPSET_SUFFIX_MAX);
snprintf(
name,
CPUINFO_ARM_CHIPSET_NAME_MAX,
@@ -4051,7 +4103,6 @@ static enum cpuinfo_arm_chipset_vendor disambiguate_chipset_vendor(
(vendor_a == cpuinfo_arm_chipset_vendor_spreadtrum && vendor_b == cpuinfo_arm_chipset_vendor_unisoc)) {
return cpuinfo_arm_chipset_vendor_unisoc;
}
return cpuinfo_arm_chipset_vendor_unknown;
}
+3 -3
View File
@@ -256,12 +256,12 @@ void cpuinfo_arm_linux_init(void) {
}
#if defined(__ANDROID__)
struct cpuinfo_android_properties android_properties;
struct cpuinfo_android_properties android_properties = {0};
cpuinfo_arm_android_parse_properties(&android_properties);
#else
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX];
char proc_cpuinfo_hardware[CPUINFO_HARDWARE_VALUE_MAX] = {0};
#endif
char proc_cpuinfo_revision[CPUINFO_REVISION_VALUE_MAX];
char proc_cpuinfo_revision[CPUINFO_REVISION_VALUE_MAX] = {0};
if (!cpuinfo_arm_linux_parse_proc_cpuinfo(
#if defined(__ANDROID__)
+49
View File
@@ -11,6 +11,11 @@ void cpuinfo_arm_decode_vendor_uarch(
#endif /* CPUINFO_ARCH_ARM */
enum cpuinfo_vendor vendor[RESTRICT_STATIC 1],
enum cpuinfo_uarch uarch[RESTRICT_STATIC 1]) {
/* Ensure the out-parameters are always initialized, including for
* implementers that are not handled in the switch below. */
*vendor = cpuinfo_vendor_unknown;
*uarch = cpuinfo_uarch_unknown;
switch (midr_get_implementer(midr)) {
case 'A':
*vendor = cpuinfo_vendor_arm;
@@ -438,6 +443,50 @@ void cpuinfo_arm_decode_vendor_uarch(
midr_get_part(midr));
}
break;
case 'a':
*vendor = cpuinfo_vendor_apple;
switch (midr_get_part(midr)) {
case 0x022:
case 0x024:
case 0x028:
*uarch = cpuinfo_uarch_icestorm;
break;
case 0x023:
case 0x025:
case 0x029:
*uarch = cpuinfo_uarch_firestorm;
break;
case 0x032:
case 0x034:
case 0x038:
*uarch = cpuinfo_uarch_blizzard;
break;
case 0x033:
case 0x035:
case 0x039:
*uarch = cpuinfo_uarch_avalanche;
break;
case 0x042:
case 0x044:
case 0x048:
*uarch = cpuinfo_uarch_coll_sawtooth;
break;
case 0x043:
case 0x045:
case 0x049:
*uarch = cpuinfo_uarch_coll_everest;
break;
case 0x052:
*uarch = cpuinfo_uarch_donan_sawtooth;
break;
case 0x053:
*uarch = cpuinfo_uarch_donan_everest;
break;
default:
cpuinfo_log_warning(
"unknown Apple CPU part 0x%03" PRIx32 " ignored", midr_get_part(midr));
}
break;
#if CPUINFO_ARCH_ARM
case 'V':
*vendor = cpuinfo_vendor_marvell;
+9
View File
@@ -307,6 +307,15 @@ static bool transform_token(char* token_start, char* token_end, struct parser_st
}
break;
case 4:
/*
* Erase everything starting with "with" on AMD
* processors, e.g. "AMD Ryzen 5 PRO 6650U with Radeon
* Graphics"
*/
if (erase_matching(token_start, token_length, "with")) {
return false;
}
/* Remember to erase "Dual Core" in "AMD Athlon(tm) 64
* X2 Dual Core Processor 3800+" */
if (memcmp(token_start, "Dual", token_length) == 0) {
+7
View File
@@ -270,8 +270,15 @@ enum cpuinfo_uarch cpuinfo_x86_decode_uarch(
return cpuinfo_uarch_prescott;
}
break;
case 0x12:
switch (model_info->model) {
case 0x01: // Nova Lake P-core (Coyote Cove)
return cpuinfo_uarch_coyote_cove;
}
break;
}
break;
case cpuinfo_vendor_amd:
switch (model_info->family) {
#if CPUINFO_ARCH_X86
-2
View File
@@ -265,8 +265,6 @@ BOOL CALLBACK cpuinfo_x86_windows_init(PINIT_ONCE init_once, PVOID parameter, PV
* APIC order */
const uint32_t core_id = cores_count++;
uint32_t smt_id = 0;
/* Reconstruct core part of APIC ID */
const uint32_t core_apic_id = (core_id & core_bits_mask) << x86_processor.topology.core_bits_offset;
/* Iterate processor groups and set the core & SMT parts of APIC
* ID */
for (uint32_t i = 0; i < core_info->Processor.GroupCount; i++) {
+4 -3
View File
@@ -1,12 +1,13 @@
Copyright 2015-2023 The Khronos Group Inc.
Copyright 2015-2026 The Khronos Group Inc.
Files in this repository fall under one of these licenses:
- `Apache-2.0`
- `MIT`
Note: With the exception of `parse_dependency.py` the files using `MIT` license
also fall under `Apache-2.0`. Example:
Note: With the exception of `parse_dependency.py`, which is based on an
external file, files using the `MIT` license also fall under `Apache-2.0`.
Example:
```
SPDX-License-Identifier: Apache-2.0 OR MIT
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -2
View File
@@ -2,8 +2,7 @@
* Copyright 2015-2023 The Khronos Group Inc.
* Copyright 2015-2023 Valve Corporation
* Copyright 2015-2023 LunarG, Inc.
*
* SPDX-License-Identifier: Apache-2.0
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#pragma once
+1 -2
View File
@@ -2,8 +2,7 @@
* Copyright 2015-2023 The Khronos Group Inc.
* Copyright 2015-2023 Valve Corporation
* Copyright 2015-2023 LunarG, Inc.
*
* SPDX-License-Identifier: Apache-2.0
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#pragma once
+1 -2
View File
@@ -3,8 +3,7 @@
//
/*
** Copyright 2014-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
+1 -2
View File
@@ -3,8 +3,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
#include "vk_platform.h"
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
+1 -1
View File
@@ -4,7 +4,7 @@
/*
** Copyright 2015-2026 The Khronos Group Inc.
**
** SPDX-License-Identifier: Apache-2.0
** SPDX-License-Identifier: Apache-2.0 OR MIT
*/
/*
File diff suppressed because it is too large Load Diff
-5
View File
@@ -2014,11 +2014,6 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
vtx.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
vtx.interior = 0;
#if !VS_IIP
// Get the provoking vertex color (first vertex in DX)
vtx.c = i0 == 0 ? vtx.c : (i1 == 0 ? other.c : opposite.c);
#endif
}
return vtx;
@@ -455,11 +455,6 @@ void main()
VSout.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
VSout.interior = 0;
#if !VS_IIP
// Get the provoking vertex color (last vertex in GL)
vtx.c = i0 == 2 ? vtx.c : (i1 == 2 ? other.c : opposite.c);
#endif
}
#endif
-5
View File
@@ -450,11 +450,6 @@ void main()
vsOut.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
vsOut.interior = 0;
#if !VS_IIP
// Get the provoking vertex color (last vertex in VK)
vtx.c = i0 == 2 ? vtx.c : (i1 == 2 ? other.c : opposite.c);
#endif
}
#endif
+39
View File
@@ -34,6 +34,34 @@ Value MemoryInterface::Read(u32 address, bool* valid)
return Value(0);
}
std::optional<std::string> MemoryInterface::ReadString(u32 address, u32 max_size, ReadStringFlags flags)
{
std::string string;
for (u32 i = 0; i < max_size; i++)
{
bool valid;
char c = Read8(address + i, &valid);
if (!valid)
return std::nullopt;
if (c == '\0')
return string;
else if (!(flags & ALLOW_NON_PRINTABLE_CHARACTERS) && (c < ' ' || c > '~'))
return std::nullopt;
string += c;
}
if (flags & ALLOW_LONG_STRINGS)
{
string += '~';
return string;
}
return std::nullopt;
}
template <MemoryAccessType Value>
bool MemoryInterface::Write(u32 address, Value value)
{
@@ -62,6 +90,17 @@ bool MemoryInterface::Write(u32 address, Value value)
return false;
}
bool MemoryInterface::WriteString(u32 address, std::string_view string)
{
if (!WriteBytes(address, string.data(), static_cast<u32>(string.size())))
return false;
if (!Write8(address + static_cast<u32>(string.size()), '\0'))
return false;
return true;
}
bool MemoryInterface::IdempotentWrite8(u32 address, u8 value)
{
bool valid;
+16 -2
View File
@@ -5,6 +5,9 @@
#include "Pcsx2Types.h"
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
template <typename Value>
@@ -31,16 +34,27 @@ public:
template <MemoryAccessType Value>
Value Read(u32 address, bool* valid = nullptr);
enum ReadStringFlags
{
NO_FLAGS = 0,
ALLOW_LONG_STRINGS = 1 << 0,
ALLOW_NON_PRINTABLE_CHARACTERS = 1 << 1
};
std::optional<std::string> ReadString(u32 address, u32 max_size = 256, ReadStringFlags flags = NO_FLAGS);
virtual bool Write8(u32 address, u8 value) = 0;
virtual bool Write16(u32 address, u16 value) = 0;
virtual bool Write32(u32 address, u32 value) = 0;
virtual bool Write64(u32 address, u64 value) = 0;
virtual bool Write128(u32 address, u128 value) = 0;
virtual bool WriteBytes(u32 address, void* src, u32 size) = 0;
virtual bool WriteBytes(u32 address, const void* src, u32 size) = 0;
template <MemoryAccessType Value>
bool Write(u32 address, Value value);
bool WriteString(u32 address, std::string_view string);
bool IdempotentWrite8(u32 address, u8 value);
bool IdempotentWrite16(u32 address, u16 value);
bool IdempotentWrite32(u32 address, u32 value);
@@ -51,5 +65,5 @@ public:
template <MemoryAccessType Value>
bool IdempotentWrite(u32 address, Value value);
virtual bool CompareBytes(u32 address, void* src, u32 size) = 0;
virtual bool CompareBytes(u32 address, const void* src, u32 size) = 0;
};
+88 -26
View File
@@ -17,7 +17,7 @@ struct RapidYAMLContext
Error* error = nullptr;
};
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error)
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error, bool resolve_anchors)
{
RapidYAMLContext context;
context.error = error;
@@ -28,40 +28,93 @@ std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr
callbacks.set_user_data(static_cast<void*>(&context));
callbacks.set_error_basic([](ryml::csubstr msg, const ryml::ErrorDataBasic& errdata, void* user_data) {
std::string description;
auto callback = [&description](ryml::csubstr string) {
description.append(string.str, string.len);
};
ryml::err_basic_format(std::move(callback), msg, errdata);
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
// This scope needs to stay, so all objects destruct before std::longjump
{
std::string description;
auto callback = [&description](ryml::csubstr string) {
description.append(string.str, string.len);
};
ryml::err_basic_format(std::move(callback), msg, errdata);
// We might have already returned, so don't try to recover.
pxFailRel(description.c_str());
std::abort();
if (context != nullptr)
{
Error::SetString(context->error, std::move(description));
}
else
{
pxFailRel(description.c_str());
}
}
if (context != nullptr)
{
std::longjmp(context->env, 1);
}
else
{
std::terminate();
}
});
callbacks.set_error_parse([](ryml::csubstr msg, const ryml::ErrorDataParse& errdata, void* user_data) {
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
// This scope needs to stay, so all objects destruct before std::longjump
{
std::string description;
auto callback = [&description](ryml::csubstr string) {
description.append(string.str, string.len);
};
ryml::err_parse_format(std::move(callback), msg, errdata);
std::string description;
auto callback = [&description](ryml::csubstr string) {
description.append(string.str, string.len);
};
ryml::err_parse_format(std::move(callback), msg, errdata);
if (context != nullptr)
{
Error::SetString(context->error, std::move(description));
}
else
{
pxFailRel(description.c_str());
}
}
Error::SetString(context->error, std::move(description));
std::longjmp(context->env, 1);
if (context != nullptr)
{
std::longjmp(context->env, 2);
}
else
{
std::terminate();
}
});
callbacks.set_error_visit([](ryml::csubstr msg, const ryml::ErrorDataVisit& errdata, void* user_data) {
std::string description;
auto callback = [&description](ryml::csubstr string) {
description.append(string.str, string.len);
};
ryml::err_visit_format(std::move(callback), msg, errdata);
RapidYAMLContext* context = static_cast<RapidYAMLContext*>(user_data);
// This scope needs to stay, so all objects destruct before std::longjump
{
std::string description;
auto callback = [&description](ryml::csubstr string) {
description.append(string.str, string.len);
};
ryml::err_visit_format(std::move(callback), msg, errdata);
// We've probably already returned, so don't try to recover.
pxFailRel(description.c_str());
std::abort();
if (context != nullptr)
{
Error::SetString(context->error, std::move(description));
}
else
{
pxFailRel(description.c_str());
}
}
if (context != nullptr)
{
std::longjmp(context->env, 3);
}
else
{
std::terminate();
}
});
#else
callbacks.m_user_data = static_cast<void*>(&context);
@@ -76,15 +129,24 @@ std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr
ryml::EventHandlerTree event_handler(callbacks);
ryml::Parser parser(&event_handler);
ryml::Tree tree;
ryml::Tree tree(callbacks);
// The only options RapidYAML provides for recovering from errors are
// throwing an exception or using setjmp/longjmp. Since we have exceptions
// disabled we have to use the latter option.
if (setjmp(context.env))
if (setjmp(context.env) != 0)
return std::nullopt;
ryml::parse_in_arena(&parser, file_name, yaml, &tree);
if (resolve_anchors)
{
tree.resolve();
}
// Callbacks passed to ryml::Tree are used for value parsing errors later,
// so we need to clear the context before it goes out of scope.
callbacks.set_user_data(nullptr);
tree.callbacks(callbacks);
return tree;
}
+3 -1
View File
@@ -14,4 +14,6 @@
/// parsing errors (as is recommended by the documentation for cases where
/// exceptions are disabled). The file_name parameter is only used for error
/// messages, which are returned via the error parameter.
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error);
/// If resolve_anchors is set to true, YAML anchors and aliases will be resolved.
/// It's a potentially slow operation, so it's opt-in.
std::optional<ryml::Tree> ParseYAMLFromString(ryml::csubstr yaml, ryml::csubstr file_name, Error* error, bool resolve_anchors = false);
@@ -7,6 +7,8 @@
#include "Debugger/SymbolTree/SymbolTreeModel.h"
#include "Debugger/SymbolTree/TypeString.h"
#include "common/StringUtil.h"
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QDoubleSpinBox>
@@ -44,12 +46,28 @@ QWidget* SymbolTreeValueDelegate::createEditor(QWidget* parent, const QStyleOpti
const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first;
QVariant value = node->readValueAsVariant(physical_type, m_cpu, database);
const ccc::ast::Node& type = *logical_type->physical_type(database).first;
switch (type.descriptor)
switch (physical_type.descriptor)
{
case ccc::ast::ARRAY:
{
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;;
if (array.element_count > 0 &&
element_type.name == "char" &&
node->location.type == SymbolTreeLocation::MEMORY &&
!value.isNull())
{
QLineEdit* editor = new QLineEdit(parent);
editor->setText(value.toString());
result = editor;
}
break;
}
case ccc::ast::BUILTIN:
{
const ccc::ast::BuiltIn& builtin = type.as<ccc::ast::BuiltIn>();
const ccc::ast::BuiltIn& builtin = physical_type.as<ccc::ast::BuiltIn>();
switch (builtin.bclass)
{
@@ -111,7 +129,7 @@ QWidget* SymbolTreeValueDelegate::createEditor(QWidget* parent, const QStyleOpti
}
case ccc::ast::ENUM:
{
const ccc::ast::Enum& enumeration = type.as<ccc::ast::Enum>();
const ccc::ast::Enum& enumeration = physical_type.as<ccc::ast::Enum>();
QComboBox* combo_box = new QComboBox(parent);
bool named = false;
@@ -186,12 +204,30 @@ void SymbolTreeValueDelegate::setModelData(QWidget* editor, QAbstractItemModel*
if (!logical_type)
return;
const ccc::ast::Node& type = *logical_type->physical_type(database).first;
switch (type.descriptor)
const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first;
switch (physical_type.descriptor)
{
case ccc::ast::ARRAY:
{
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
QLineEdit* line_edit = qobject_cast<QLineEdit*>(editor);
if (array.element_count > 0 &&
element_type.name == "char" &&
node->location.type == SymbolTreeLocation::MEMORY &&
line_edit)
{
value = line_edit->text();
}
break;
}
case ccc::ast::BUILTIN:
{
const ccc::ast::BuiltIn& builtin = type.as<ccc::ast::BuiltIn>();
const ccc::ast::BuiltIn& builtin = physical_type.as<ccc::ast::BuiltIn>();
switch (builtin.bclass)
{
+60 -10
View File
@@ -67,7 +67,7 @@ bool SymbolTreeNode::writeToVM(
if (logical_type)
{
const ccc::ast::Node& physical_type = *logical_type->physical_type(database).first;
writeValueFromVariant(m_value, physical_type, cpu);
writeValueFromVariant(m_value, physical_type, cpu, database);
}
data_changed |= updateDisplayString(cpu, database, display_options);
@@ -76,10 +76,29 @@ bool SymbolTreeNode::writeToVM(
return data_changed;
}
QVariant SymbolTreeNode::readValueAsVariant(const ccc::ast::Node& physical_type, DebugInterface& cpu, const ccc::SymbolDatabase& database) const
QVariant SymbolTreeNode::readValueAsVariant(
const ccc::ast::Node& physical_type,
DebugInterface& cpu,
const ccc::SymbolDatabase& database) const
{
switch (physical_type.descriptor)
{
case ccc::ast::ARRAY:
{
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
if (array.element_count > 0 &&
element_type.name == "char" &&
location.type == SymbolTreeLocation::MEMORY)
{
std::optional<std::string> string = cpu.ReadString(location.address, 256);
if (string.has_value())
return QString::fromStdString(*string);
}
break;
}
case ccc::ast::BUILTIN:
{
const ccc::ast::BuiltIn& builtIn = physical_type.as<ccc::ast::BuiltIn>();
@@ -134,10 +153,36 @@ QVariant SymbolTreeNode::readValueAsVariant(const ccc::ast::Node& physical_type,
return QVariant();
}
bool SymbolTreeNode::writeValueFromVariant(QVariant value, const ccc::ast::Node& physical_type, DebugInterface& cpu) const
bool SymbolTreeNode::writeValueFromVariant(
QVariant value,
const ccc::ast::Node& physical_type,
DebugInterface& cpu,
const ccc::SymbolDatabase& database) const
{
if (value.isNull())
return false;
switch (physical_type.descriptor)
{
case ccc::ast::ARRAY:
{
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
if (array.element_count > 0 &&
element_type.name == "char" &&
location.type == SymbolTreeLocation::MEMORY)
{
QByteArray byte_array = value.toString().toLatin1();
std::string_view view(
byte_array.data(),
std::min(static_cast<s32>(byte_array.size()), array.element_count - 1));
cpu.WriteString(location.address, view);
break;
}
return false;
}
case ccc::ast::BUILTIN:
{
const ccc::ast::BuiltIn& built_in = physical_type.as<ccc::ast::BuiltIn>();
@@ -191,6 +236,7 @@ bool SymbolTreeNode::writeValueFromVariant(QVariant value, const ccc::ast::Node&
return false;
}
}
break;
}
case ccc::ast::ENUM:
@@ -266,11 +312,14 @@ QString SymbolTreeNode::generateDisplayString(
const ccc::ast::Array& array = physical_type.as<ccc::ast::Array>();
const ccc::ast::Node& element_type = *array.element_type->physical_type(database).first;
if (element_type.name == "char" && location.type == SymbolTreeLocation::MEMORY)
if (array.element_count > 0 &&
element_type.name == "char" &&
location.type == SymbolTreeLocation::MEMORY)
{
char* string = cpu.stringFromPointer(location.address);
if (string)
return QString("\"%1\"").arg(string);
std::optional<std::string> string = cpu.ReadString(
location.address, 256, MemoryInterface::ALLOW_LONG_STRINGS);
if (string.has_value())
return QString("\"%1\"").arg(*string);
}
QString result;
@@ -412,9 +461,10 @@ QString SymbolTreeNode::generateDisplayString(
if (pointer_or_reference.is_pointer && value_type.name == "char")
{
const char* string = cpu.stringFromPointer(address);
if (string)
result += QString(" \"%1\"").arg(string);
std::optional<std::string> string = cpu.ReadString(
address, 256, MemoryInterface::ALLOW_LONG_STRINGS);
if (string.has_value())
result += QString(" \"%1\"").arg(*string);
}
else if (depth == 0)
{
+11 -2
View File
@@ -62,11 +62,20 @@ public:
const ccc::SymbolDatabase& database,
const SymbolTreeDisplayOptions& display_options);
QVariant readValueAsVariant(const ccc::ast::Node& physical_type, DebugInterface& cpu, const ccc::SymbolDatabase& database) const;
bool writeValueFromVariant(QVariant value, const ccc::ast::Node& physical_type, DebugInterface& cpu) const;
QVariant readValueAsVariant(
const ccc::ast::Node& physical_type,
DebugInterface& cpu,
const ccc::SymbolDatabase& database) const;
bool writeValueFromVariant(
QVariant value,
const ccc::ast::Node& physical_type,
DebugInterface& cpu,
const ccc::SymbolDatabase& database) const;
bool updateDisplayString(
DebugInterface& cpu, const ccc::SymbolDatabase& database, const SymbolTreeDisplayOptions& display);
QString generateDisplayString(
const ccc::ast::Node& physical_type,
DebugInterface& cpu,
@@ -365,7 +365,6 @@
<tabstop>gsDumpCompression</tabstop>
<tabstop>texturePreloading</tabstop>
<tabstop>exclusiveFullscreenControl</tabstop>
<tabstop>rov</tabstop>
<tabstop>extendedUpscales</tabstop>
<tabstop>spinGPUDuringReadbacks</tabstop>
<tabstop>spinCPUDuringReadbacks</tabstop>
File diff suppressed because it is too large Load Diff
+34 -23
View File
@@ -183,7 +183,7 @@ namespace Achievements
static void DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, bool is_self, float rank_column_width,
float name_column_width, float time_column_width, float column_spacing);
static void OpenSubset(const rc_client_subset_t* subset);
static bool DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool leaderboards_only);
static bool DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool& focus_sidebar, bool leaderboards_only);
static void DrawSubsetSidebarFooter(bool sidebar_has_focus);
static void OpenLeaderboard(const rc_client_leaderboard_t* lboard);
static void LeaderboardFetchNearbyCallback(
@@ -2437,7 +2437,7 @@ bool Achievements::PrepareAchievementsWindow()
return true;
}
bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool leaderboards_only)
bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focus, bool& focus_sidebar, bool leaderboards_only)
{
if (!s_subset_list)
return false;
@@ -2452,7 +2452,7 @@ bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focu
bool subset_selected = false;
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), 0, 0))
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
{
sidebar_has_focus = ImGui::IsWindowFocused();
ImGuiFullscreen::BeginMenuButtons();
@@ -2475,6 +2475,11 @@ bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focu
ImGuiFullscreen::MenuButtonFrame(id_str, true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
&visible, &hovered, &bb.Min, &bb.Max, 0, 1.0f);
if (focus_sidebar && ((s_open_subset && s_open_subset->id == subset->id) || (!s_open_subset && i == 0)))
{
ImGui::SetItemDefaultFocus();
}
if (s_open_subset && s_open_subset->id == subset->id)
{
ImGui::GetWindowDrawList()->AddRectFilled(
@@ -2517,6 +2522,10 @@ bool Achievements::DrawSubsetSidebar(float sidebar_width, bool& sidebar_has_focu
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
ImGui::SameLine();
ImGui::PopStyleVar();
if (focus_sidebar)
focus_sidebar = false;
return subset_selected;
}
@@ -2538,7 +2547,8 @@ void Achievements::DrawSubsetSidebarFooter(bool sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Subsets")),
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Subsets")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
}
@@ -2556,7 +2566,8 @@ void Achievements::DrawSubsetSidebarFooter(bool sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Subsets")),
std::make_pair(ICON_PF_ARROW_LEFT, TRANSLATE_SV("Achievements", "Subsets")),
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
});
}
}
@@ -2627,16 +2638,11 @@ void Achievements::DrawAchievementsWindow()
const bool wants_close = ImGuiFullscreen::WantsToCloseMenu();
if (ImGuiFullscreen::FloatingButton(ICON_FA_SQUARE_XMARK, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font) ||
(wants_close && (!has_multiple_subsets || sidebar_has_focus)))
wants_close)
{
sidebar_has_focus = false;
FullscreenUI::ReturnToPreviousWindow();
}
else if (has_multiple_subsets && !sidebar_has_focus && wants_close)
{
sidebar_has_focus = true;
focus_sidebar = true;
}
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + GetLineHeight(g_large_font)));
text.assign(s_open_subset ? s_open_subset->title : s_game_title);
@@ -2731,9 +2737,8 @@ void Achievements::DrawAchievementsWindow()
if (focus_sidebar)
{
ImGui::SetNextWindowFocus();
focus_sidebar = false;
}
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, false))
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, focus_sidebar, false))
ImGui::SetNextWindowFocus();
}
@@ -2744,8 +2749,14 @@ void Achievements::DrawAchievementsWindow()
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), 0, 0))
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
{
if (has_multiple_subsets && !sidebar_has_focus &&
(ImGui::IsKeyPressed(ImGuiKey_GamepadDpadLeft, false) || ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false)))
{
sidebar_has_focus = true;
focus_sidebar = true;
}
static const char* bucket_names[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = {
TRANSLATE_NOOP("Achievements", "Unknown"),
@@ -3152,16 +3163,11 @@ void Achievements::DrawLeaderboardsWindow()
{
const bool wants_close = ImGuiFullscreen::WantsToCloseMenu();
if (ImGuiFullscreen::FloatingButton(ICON_FA_SQUARE_XMARK, 10.0f, 10.0f, -1.0f, -1.0f, 1.0f, 0.0f, true, g_large_font) ||
(wants_close && (!has_multiple_subsets || sidebar_has_focus)))
wants_close)
{
sidebar_has_focus = false;
FullscreenUI::ReturnToPreviousWindow();
}
else if (has_multiple_subsets && !sidebar_has_focus && wants_close)
{
sidebar_has_focus = true;
focus_sidebar = true;
}
}
else
{
@@ -3330,9 +3336,8 @@ void Achievements::DrawLeaderboardsWindow()
if (focus_sidebar)
{
ImGui::SetNextWindowFocus();
focus_sidebar = false;
}
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, true))
if (DrawSubsetSidebar(sidebar_width, sidebar_has_focus, focus_sidebar, true))
ImGui::SetNextWindowFocus();
}
@@ -3343,8 +3348,14 @@ void Achievements::DrawLeaderboardsWindow()
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
if (ImGui::BeginChild("leaderboards_content", ImVec2(content_width, 0.0f), 0, 0))
if (ImGui::BeginChild("leaderboards_content", ImVec2(content_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
{
if (has_multiple_subsets && !sidebar_has_focus &&
(ImGui::IsKeyPressed(ImGuiKey_GamepadDpadLeft, false) || ImGui::IsKeyPressed(ImGuiKey_LeftArrow, false)))
{
sidebar_has_focus = true;
focus_sidebar = true;
}
if (s_restore_leaderboard_scroll)
{
+3 -3
View File
@@ -376,9 +376,11 @@ bool TAPGetWin32Adapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter,
HRESULT cohr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
if (cohr == RPC_E_CHANGED_MODE)
cohr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (!SUCCEEDED(cohr))
if (FAILED(cohr))
return false;
wil::unique_couninitialize_call uninit;
PIP_ADAPTER_ADDRESSES bridgeAdapter = nullptr;
//Create Instance of INetCfg
@@ -472,8 +474,6 @@ bool TAPGetWin32Adapter(const std::string& name, PIP_ADAPTER_ADDRESSES adapter,
}
}
CoUninitialize();
if (bridgeAdapter != nullptr)
{
*adapter = *bridgeAdapter;
+6 -37
View File
@@ -63,37 +63,6 @@ void DebugInterface::resumeCpu()
VMManager::SetPaused(false);
}
char* DebugInterface::stringFromPointer(u32 p)
{
const int BUFFER_LEN = 64;
static char buf[BUFFER_LEN] = {0};
if (!isValidAddress(p))
return NULL;
// This is going to blow up if it hits a TLB miss..
// Hopefully the checks in isValidAddress() are sufficient.
for (u32 i = 0; i < BUFFER_LEN; i++)
{
char c = Read8(p + i);
buf[i] = c;
if (c == 0)
{
return i > 0 ? buf : NULL;
}
else if (c < 0x20 || c >= 0x7f)
{
// non printable character
return NULL;
}
}
buf[BUFFER_LEN - 1] = 0;
buf[BUFFER_LEN - 2] = '~';
return buf;
}
std::optional<u32> DebugInterface::getCallerStackPointer(const ccc::Function& currentFunction)
{
u32 sp = getRegister(EECAT_GPR, 29);
@@ -336,12 +305,12 @@ bool R5900DebugInterface::Write128(u32 address, u128 value)
return isValidAddress(address) && vtlb_memSafeWriteBytes(address, &value, sizeof(value));
}
bool R5900DebugInterface::WriteBytes(u32 address, void* src, u32 size)
bool R5900DebugInterface::WriteBytes(u32 address, const void* src, u32 size)
{
return vtlb_memSafeWriteBytes(address, src, size);
}
bool R5900DebugInterface::CompareBytes(u32 address, void* src, u32 size)
bool R5900DebugInterface::CompareBytes(u32 address, const void* src, u32 size)
{
return vtlb_memSafeCmpBytes(address, src, size) == 0;
}
@@ -839,12 +808,12 @@ bool R3000DebugInterface::Write128(u32 address, u128 value)
return true;
}
bool R3000DebugInterface::WriteBytes(u32 address, void* src, u32 size)
bool R3000DebugInterface::WriteBytes(u32 address, const void* src, u32 size)
{
return iopMemSafeWriteBytes(address, src, size);
}
bool R3000DebugInterface::CompareBytes(u32 address, void* src, u32 size)
bool R3000DebugInterface::CompareBytes(u32 address, const void* src, u32 size)
{
return iopMemSafeCmpBytes(address, src, size) == 0;
}
@@ -1185,12 +1154,12 @@ bool ElfMemoryReader::Write128(u32 address, u128 value)
return false;
}
bool ElfMemoryReader::WriteBytes(u32 address, void* src, u32 size)
bool ElfMemoryReader::WriteBytes(u32 address, const void* src, u32 size)
{
return false;
}
bool ElfMemoryReader::CompareBytes(u32 address, void* src, u32 size)
bool ElfMemoryReader::CompareBytes(u32 address, const void* src, u32 size)
{
std::optional<std::span<const u8>> bytes = m_elf.get_virtual(address, size);
if (!bytes.has_value())
+6 -7
View File
@@ -80,7 +80,6 @@ public:
bool isCpuPaused();
void pauseCpu();
void resumeCpu();
char* stringFromPointer(u32 p);
std::optional<u32> getCallerStackPointer(const ccc::Function& currentFunction);
std::optional<u32> getStackFrameSize(const ccc::Function& currentFunction);
@@ -115,9 +114,9 @@ public:
bool Write32(u32 address, u32 value) override;
bool Write64(u32 address, u64 value) override;
bool Write128(u32 address, u128 value) override;
bool WriteBytes(u32 address, void* src, u32 size) override;
bool WriteBytes(u32 address, const void* src, u32 size) override;
bool CompareBytes(u32 address, void* src, u32 size) override;
bool CompareBytes(u32 address, const void* src, u32 size) override;
// register stuff
int getRegisterCategoryCount() override;
@@ -161,9 +160,9 @@ public:
bool Write32(u32 address, u32 value) override;
bool Write64(u32 address, u64 value) override;
bool Write128(u32 address, u128 value) override;
bool WriteBytes(u32 address, void* src, u32 size) override;
bool WriteBytes(u32 address, const void* src, u32 size) override;
bool CompareBytes(u32 address, void* src, u32 size) override;
bool CompareBytes(u32 address, const void* src, u32 size) override;
// register stuff
int getRegisterCategoryCount() override;
@@ -210,9 +209,9 @@ public:
bool Write32(u32 address, u32 value) override;
bool Write64(u32 address, u64 value) override;
bool Write128(u32 address, u128 value) override;
bool WriteBytes(u32 address, void* src, u32 size) override;
bool WriteBytes(u32 address, const void* src, u32 size) override;
bool CompareBytes(u32 address, void* src, u32 size) override;
bool CompareBytes(u32 address, const void* src, u32 size) override;
protected:
const ccc::ElfFile& m_elf;
+54 -55
View File
@@ -502,68 +502,67 @@ void GSDevice::UpdateImGuiTextures()
case ImTextureStatus_Destroyed:
continue;
case ImTextureStatus_WantCreate:
{
GSTexture* gs_tex = g_gs_device->CreateTexture(im_tex->Width, im_tex->Height, 1, GSTexture::Format::Color);
if (!gs_tex)
pxFailRel("Failed to create ImGui texture");
im_tex->SetTexID(reinterpret_cast<ImTextureID>(gs_tex->GetNativeHandle()));
im_tex->BackendUserData = gs_tex;
[[fallthrough]];
}
case ImTextureStatus_WantUpdates:
{
// If we fell through from WantCreate, then we are uploading the full size
// Otherwise, we are just updating the specified region
// clange-format off
const int upload_x = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.x;
const int upload_y = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.y;
const int upload_w = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Width : im_tex->UpdateRect.w;
const int upload_h = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Height : im_tex->UpdateRect.h;
const int upload_pitch = upload_w * im_tex->BytesPerPixel;
// clange-format on
const GSVector4i rect{
upload_x,
upload_y,
upload_x + upload_w,
upload_y + upload_h,
};
GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData);
GSTexture::GSMap map;
if (gs_tex->Map(map, &rect))
if (GSTexture* gs_tex = g_gs_device->CreateTexture(im_tex->Width, im_tex->Height, 1, GSTexture::Format::Color))
{
for (int y = 0; y < upload_h; y++)
std::memcpy(map.bits + map.pitch * y, im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
gs_tex->Unmap();
im_tex->SetTexID(reinterpret_cast<ImTextureID>(gs_tex->GetNativeHandle()));
im_tex->BackendUserData = gs_tex;
}
else
{
for (int y = 0; y < upload_h; y++)
gs_tex->Update({rect.left, rect.top + y, rect.right, rect.top + y + 1},
im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
}
im_tex->Status = ImTextureStatus_OK;
break;
}
case ImTextureStatus_WantDestroy:
{
GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData);
if (gs_tex == nullptr)
pxFailRel("Failed to create ImGui texture");
break;
}
[[fallthrough]];
case ImTextureStatus_WantUpdates:
if (GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData))
{
// If we fell through from WantCreate, then we are uploading the full size
// Otherwise, we are just updating the specified region
// clange-format off
const int upload_x = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.x;
const int upload_y = (im_tex->Status == ImTextureStatus_WantCreate) ? 0 : im_tex->UpdateRect.y;
const int upload_w = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Width : im_tex->UpdateRect.w;
const int upload_h = (im_tex->Status == ImTextureStatus_WantCreate) ? im_tex->Height : im_tex->UpdateRect.h;
const int upload_pitch = upload_w * im_tex->BytesPerPixel;
// clange-format on
// While it's unlikely we're going to reuse the same size as imgui for rendering,
// imgui may request a new atlas of the same size if old font sizes are evicted.
Recycle(gs_tex);
const GSVector4i rect{
upload_x,
upload_y,
upload_x + upload_w,
upload_y + upload_h,
};
im_tex->SetTexID(ImTextureID_Invalid);
im_tex->BackendUserData = nullptr;
im_tex->Status = ImTextureStatus_Destroyed;
GSTexture::GSMap map;
if (gs_tex->Map(map, &rect))
{
for (int y = 0; y < upload_h; y++)
std::memcpy(map.bits + map.pitch * y, im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
gs_tex->Unmap();
}
else
{
for (int y = 0; y < upload_h; y++)
gs_tex->Update({rect.left, rect.top + y, rect.right, rect.top + y + 1},
im_tex->GetPixelsAt(rect.x, rect.y + y), upload_pitch);
}
im_tex->Status = ImTextureStatus_OK;
}
break;
case ImTextureStatus_WantDestroy:
if (GSTexture* gs_tex = static_cast<GSTexture*>(im_tex->BackendUserData))
{
// While it's unlikely we're going to reuse the same size as imgui for rendering,
// imgui may request a new atlas of the same size if old font sizes are evicted.
Recycle(gs_tex);
im_tex->SetTexID(ImTextureID_Invalid);
im_tex->BackendUserData = nullptr;
im_tex->Status = ImTextureStatus_Destroyed;
}
break;
}
default:
pxAssert(false);
break;
@@ -1111,7 +1110,7 @@ void GSDevice::BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea)
// Create a temporary RT and copy the area needed for the draw.
const int w = ds->GetWidth();
const int h = ds->GetHeight();
if (m_ds_as_rt = g_gs_device->CreateFeedbackTarget(w, h, GSTexture::Format::DepthColor, false, true))
if ((m_ds_as_rt = g_gs_device->CreateFeedbackTarget(w, h, GSTexture::Format::DepthColor, false, true)))
{
const GSVector4 dRect(drawarea);
const GSVector4 sRect(dRect.x / w, dRect.y / h, dRect.z / w, dRect.w / h);
+1 -1
View File
@@ -899,7 +899,7 @@ struct alignas(16) GSHWDrawConfig
__fi bool HasDepthROV() const
{
return rov_depth == PS_ROV_DEPTH::READ_ONLY || rov_depth == PS_ROV_DEPTH::READ_WRITE;
return rov_depth != PS_ROV_DEPTH::NONE;
}
__fi bool HasDepthROVWrite() const
+1 -1
View File
@@ -605,6 +605,6 @@ bool D3D12ShaderCache::AddPipelineToBlob(const CacheIndexKey& key, ID3D12Pipelin
return false;
}
m_shader_index.emplace(key, data);
m_pipeline_index.emplace(key, data);
return true;
}
+5 -4
View File
@@ -3626,7 +3626,7 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state, Res
}
else
{
handle = m_null_texture->GetSRVDescriptor();
handle = GetResourceDescriptor(m_null_texture.get(), type);
}
if (m_tfx_textures[i] == handle)
@@ -3675,7 +3675,7 @@ void GSDevice12::PSSetROVs(GSTexture* rt, GSTexture* ds, bool write_rt, bool wri
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TEXTURE_RT_UAV, nullptr, false);
PSSetShaderResource(TEXTURE_RT_UAV, nullptr, false, ResourceType::UAV);
}
if (d12Ds)
@@ -3699,7 +3699,7 @@ void GSDevice12::PSSetROVs(GSTexture* rt, GSTexture* ds, bool write_rt, bool wri
else
{
// Unbind to avoid conflicts with OM targets.
PSSetShaderResource(TEXTURE_DEPTH_UAV, nullptr, false);
PSSetShaderResource(TEXTURE_DEPTH_UAV, nullptr, false, ResourceType::UAV);
}
}
@@ -3777,7 +3777,8 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
// RT / primid / depth
for (u32 i = TEXTURE_RT; i <= TEXTURE_DEPTH; i++)
{
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor())
if (m_tfx_textures[i] == tex->GetSRVDescriptor() || m_tfx_textures[i] == tex->GetFBLDescriptor() ||
m_tfx_textures[i] == tex->GetUAVDescriptor())
{
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
m_dirty_flags |= DIRTY_FLAG_TFX_RT_TEXTURES;
+25 -20
View File
@@ -5331,31 +5331,36 @@ bool GSRendererHW::VerifyIndices()
return true;
}
// Fix the colors in vertices in case the API only supports "provoking first vertex"
// (i.e., when using flat shading the color comes from the first vertex, unlike PS2
// which is "provoking last vertex").
void GSRendererHW::HandleProvokingVertexFirst()
void GSRendererHW::HandleFlatShadedVertices()
{
// Early exit conditions:
if (g_gs_device->Features().provoking_vertex_last || // device supports provoking last vertex
m_conf.vs.iip || // we are doing Gouraud shading
m_vt.m_primclass == GS_POINT_CLASS || // drawing points (one vertex per primitive; color is unambiguous)
m_vt.m_primclass == GS_SPRITE_CLASS) // drawing sprites (handled by the sprites -> triangles expand shader)
// These cases might need fixing.
const bool maybe_fix_vertices = !m_conf.vs.iip &&
(!g_gs_device->Features().provoking_vertex_last || IsCoverageAlphaSupported());
// These cases definitely don't need fixing.
const bool dont_fix_vertices = m_vt.m_primclass == GS_POINT_CLASS || m_vt.m_primclass == GS_SPRITE_CLASS;
if (!maybe_fix_vertices || dont_fix_vertices)
return;
const int n = GSUtil::GetClassVertexCount(m_vt.m_primclass);
// If all first/last vertices have the same color there is nothing to do.
bool first_eq_last = true;
// If all vertices of each prim have the same color there is nothing to do.
bool prims_flat = true;
for (u32 i = 0; i < m_index->tail; i += n)
{
if (m_vertex->buff[m_index->buff[i]].RGBAQ.U32[0] != m_vertex->buff[m_index->buff[i + n - 1]].RGBAQ.U32[0])
for (u32 j = 0; j < n - 1; j++)
{
first_eq_last = false;
break;
if (m_vertex->buff[m_index->buff[i + j]].RGBAQ.U32[0] != m_vertex->buff[m_index->buff[i + n - 1]].RGBAQ.U32[0])
{
prims_flat = false;
break;
}
}
if (!prims_flat)
break;
}
if (first_eq_last)
if (prims_flat)
return;
// De-index the vertices using the copy buffer
@@ -5369,11 +5374,11 @@ void GSRendererHW::HandleProvokingVertexFirst()
std::swap(m_vertex->buff, m_vertex->buff_copy);
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
// Put correct color in the first vertex
// Make all vertices the same color to simplify handling in expand shaders.
for (u32 i = 0; i < m_index->tail; i += n)
{
m_vertex->buff[i].RGBAQ.U32[0] = m_vertex->buff[i + n - 1].RGBAQ.U32[0];
m_vertex->buff[i + n - 1].RGBAQ.U32[0] = 0xff; // Make last vertex red for debugging if used improperly
for (u32 j = 0; j < n - 1; j++)
m_vertex->buff[i + j].RGBAQ.U32[0] = m_vertex->buff[i + n - 1].RGBAQ.U32[0];
}
}
@@ -9450,7 +9455,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
DetermineROVUsage(rt, ds);
ConvertTextureTypeROV(rt, ds);
// Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA.
// Barriers must be determined before indices are modified via HandleFlatShadedVertices/SetupIA.
// This also computes the drawlist if needed.
DetermineBarriers(rt, tex);
@@ -9475,7 +9480,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_conf.scissor = (date_options.enabled && !date_options.barrier) ? m_conf.drawarea : scissor;
HandleProvokingVertexFirst();
HandleFlatShadedVertices();
SetupIA(rtscale, vs_scale_x, vs_scale_y, m_channel_shuffle_width != 0, no_rt);
+1 -1
View File
@@ -215,7 +215,7 @@ private:
void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm);
void ResetStates();
void HandleProvokingVertexFirst();
void HandleFlatShadedVertices();
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup, const bool no_rt);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
u32 EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
+1 -1
View File
@@ -5297,7 +5297,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// If it was matched to an old target, make sure to clear the other type and update its information.
// Also update information if the buffer width of the new data is larger than the existing target.
if (dst->m_was_dst_matched || (Common::AlignUpPow2(w, 64) / GSLocalMemory::m_psm[new_TEX0.PSM].pgs.x) > dst->m_TEX0.TBW)
if (dst->m_was_dst_matched || static_cast<u32>(Common::AlignUpPow2(w, 64) / GSLocalMemory::m_psm[new_TEX0.PSM].pgs.x) > dst->m_TEX0.TBW)
{
dst->m_TEX0 = new_TEX0;
}
+11
View File
@@ -279,6 +279,7 @@ public:
std::unordered_map<PipelineSelectorMTL, MRCOwned<id<MTLRenderPipelineState>>> m_hw_pipeline;
MRCOwned<MTLRenderPassDescriptor*> m_render_pass_desc[16];
MRCOwned<MTLRenderPassDescriptor*> m_full_rov_render_pass_desc;
MRCOwned<id<MTLSamplerState>> m_sampler_hw[1 << 8];
@@ -316,12 +317,14 @@ public:
// Clear line (Things below here are tracked by `has` and don't need to be cleared to reset)
SamplerSelector sampler_sel;
u8 blend_color;
struct { u32 w, h; } full_rov_size;
GSVector4i scissor;
PipelineSelectorMTL pipeline_sel;
GSHWDrawConfig::VSConstantBuffer cb_vs;
GSHWDrawConfig::PSConstantBuffer cb_ps;
MainRenderEncoder(const MainRenderEncoder&) = delete;
MainRenderEncoder() = default;
bool is_full_rov() const { return encoder && !color_target && !depth_target && !stencil_target; }
} m_current_render;
MRCOwned<id<MTLCommandBuffer>> m_texture_upload_cmdbuf;
MRCOwned<id<MTLBlitCommandEncoder>> m_texture_upload_encoder;
@@ -330,6 +333,8 @@ public:
MRCOwned<id<MTLBlitCommandEncoder>> m_vertex_upload_encoder;
id<MTLTexture> m_ds_as_rt_texture = nil;
GSTexture* m_ds_as_rt_gstexture = nullptr;
MRCOwned<id<MTLTexture>> m_rov_dummy_texture;
struct { u32 w, h; } m_rov_dummy_texture_size = {};
struct DebugEntry
{
@@ -372,8 +377,12 @@ public:
void FlushEncodersForReadback();
/// End current render pass without flushing
void EndRenderPass();
/// Prepare to begin a new render pass
void PrepareBeginRenderPass();
/// Begin a new render pass (may reuse existing)
void BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil = nullptr, MTLLoadAction stencil_load = MTLLoadActionDontCare, bool rt1 = false);
/// Begin a new full-ROV render pass (may reuse existing)
void BeginFullROV(NSString* name, uint32_t width, uint32_t height);
/// Call at the end of each frame
void FrameCompleted();
@@ -442,6 +451,7 @@ public:
void MRESetDSS(id<MTLDepthStencilState> dss);
void MRESetSampler(SamplerSelector sel);
void MRESetTexture(GSTexture* tex, int pos);
void MRESetTexture(id<MTLTexture> tex, int pos);
void MRESetVertices(id<MTLBuffer> buffer, size_t offset);
void MRESetVSIndices(id<MTLBuffer> buffer, size_t offset);
void MRESetScissor(const GSVector4i& scissor);
@@ -455,6 +465,7 @@ public:
// MARK: Render HW
void SetupDestinationAlpha(GSTexture* rt, GSTexture* ds, const GSVector4i& r, SetDATM datm);
void PrepareROVTexture(GSTexture** ptex);
void RenderHW(GSHWDrawConfig& config) override;
void SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder> enc, id<MTLBuffer> buffer, size_t off,
bool one_barrier, bool full_barrier);
+161 -29
View File
@@ -419,6 +419,19 @@ static GSVector4 GetRTLoadInfo(GSTextureMTL* tex, MTLLoadAction* load_action)
return {};
};
void GSDeviceMTL::PrepareBeginRenderPass()
{
m_encoders_in_current_cmdbuf++;
if (m_late_texture_upload_encoder)
{
[m_late_texture_upload_encoder endEncoding];
m_late_texture_upload_encoder = nullptr;
}
EndRenderPass();
}
void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load,
GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil, MTLLoadAction stencil_load, bool rt1)
{
@@ -455,14 +468,6 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
return;
}
m_encoders_in_current_cmdbuf++;
if (m_late_texture_upload_encoder)
{
[m_late_texture_upload_encoder endEncoding];
m_late_texture_upload_encoder = nullptr;
}
int idx = 0;
if (mc) idx |= 1;
if (md) idx |= 2;
@@ -502,7 +507,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
color1.clearColor = MTLClearColorMake(depth_load == MTLLoadActionClear ? depth_clear.x : -1, 0, 0, 0);
}
EndRenderPass();
PrepareBeginRenderPass();
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
m_current_render.name = (__bridge void*)name;
[m_current_render.encoder setLabel:name];
@@ -516,6 +521,61 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
pxAssertRel(m_current_render.encoder, "Failed to create render encoder!");
}
void GSDeviceMTL::BeginFullROV(NSString* name, uint32_t width, uint32_t height)
{
bool needs_new = !m_current_render.is_full_rov()
|| width > m_current_render.full_rov_size.w
|| height > m_current_render.full_rov_size.h;
if (!needs_new)
{
if (m_current_render.name != (__bridge void*)name)
{
m_current_render.name = (__bridge void*)name;
[m_current_render.encoder setLabel:name];
}
return;
}
if (m_current_render.is_full_rov())
{
// Render was too small. Don't let it shrink in one dimension while growing in the other.
width = std::max(width, m_current_render.full_rov_size.w);
height = std::max(height, m_current_render.full_rov_size.h);
}
m_current_render.full_rov_size.w = width;
m_current_render.full_rov_size.h = height;
MTLRenderPassDescriptor* desc = m_full_rov_render_pass_desc;
[desc setRenderTargetWidth:width];
[desc setRenderTargetHeight:height];
if (m_dev.features.rov_requires_rt && (width > m_rov_dummy_texture_size.w || height > m_rov_dummy_texture_size.h))
{
m_rov_dummy_texture_size.w = std::max(m_rov_dummy_texture_size.w, width);
m_rov_dummy_texture_size.h = std::max(m_rov_dummy_texture_size.h, height);
MTLTextureDescriptor* tdesc = [MTLTextureDescriptor
texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
width:m_rov_dummy_texture_size.w
height:m_rov_dummy_texture_size.h
mipmapped:NO];
[tdesc setUsage:MTLTextureUsageRenderTarget];
[tdesc setStorageMode:MTLStorageModePrivate];
id<MTLTexture> tex = m_rov_dummy_texture = MRCTransfer([m_dev.dev newTextureWithDescriptor:tdesc]);
[tex setLabel:@"ROV Dummy Texture"];
[[[desc colorAttachments] objectAtIndexedSubscript:0] setTexture:tex];
}
PrepareBeginRenderPass();
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
m_current_render.name = (__bridge void*)name;
[m_current_render.encoder setLabel:name];
if (!m_dev.features.unified_memory)
[m_current_render.encoder waitForFence:m_draw_sync_fence
beforeStages:MTLRenderStageVertex];
}
void GSDeviceMTL::FrameCompleted()
{
if (m_spin_timer)
@@ -564,11 +624,10 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Usage usage, int width, int hei
[desc setStorageMode:MTLStorageModePrivate];
MTLTextureUsage mtl_usage = MTLTextureUsageShaderRead;
bool needs_rov_tex = false;
if (GSTexture::IsRenderTarget(usage))
{
if (GSTexture::IsRenderTargetOrDepthStencil(usage))
mtl_usage |= MTLTextureUsageRenderTarget;
}
if ((usage & GSTexture::FeedbackTarget) == GSTexture::FeedbackTarget)
{
@@ -576,17 +635,23 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Usage usage, int width, int hei
mtl_usage |= MTLTextureUsagePixelFormatView; // Force color compression off by including PixelFormatView
}
if (GSTexture::IsShaderWrite(usage))
if (usage == GSTexture::ShaderWriteTarget && format == GSTexture::Format::Color && m_dev.features.rov_requires_r32)
{
mtl_usage |= MTLTextureUsageShaderWrite;
// Need to make an R32 view of the texture for ROV
mtl_usage |= MTLTextureUsagePixelFormatView;
needs_rov_tex = true;
}
if (GSTexture::IsShaderWrite(usage))
mtl_usage |= MTLTextureUsageShaderWrite;
[desc setUsage:mtl_usage];
MRCOwned<id<MTLTexture>> tex = MRCTransfer([m_dev.dev newTextureWithDescriptor:desc]);
if (tex)
{
GSTextureMTL* t = new GSTextureMTL(this, tex, usage, format);
MRCOwned<id<MTLTexture>> rov_tex = needs_rov_tex ? MRCTransfer([tex newTextureViewWithPixelFormat:MTLPixelFormatR32Uint]) : nil;
GSTextureMTL* t = new GSTextureMTL(this, tex, rov_tex, usage, format);
if (GSTexture::IsRenderTarget(usage))
{
ClearRenderTarget(t, 0);
@@ -1003,12 +1068,14 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_features.test_and_sample_depth = true;
m_features.depth_feedback = getDepthFeedback(m_dev, m_features.framebuffer_fetch);
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand;
m_features.rov = m_dev.features.rov && !m_features.framebuffer_fetch;
m_max_texture_size = m_dev.features.max_texsize;
// Init metal stuff
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
setFnConstantB(m_fn_constants, m_dev.features.rov_requires_r32, GSMTLConstantIndex_ROV_NEEDS_R32);
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
[m_draw_sync_fence setLabel:@"Draw Sync Fence"];
@@ -1066,6 +1133,17 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
}
m_render_pass_desc[i] = desc;
}
if (m_features.rov)
{
m_full_rov_render_pass_desc = MRCTransfer([MTLRenderPassDescriptor new]);
[m_full_rov_render_pass_desc setDefaultRasterSampleCount:1];
if (m_dev.features.rov_requires_rt)
{
MTLRenderPassColorAttachmentDescriptor* color0 = [[m_full_rov_render_pass_desc colorAttachments] objectAtIndexedSubscript:0];
[color0 setLoadAction:MTLLoadActionDontCare];
[color0 setStoreAction:MTLStoreActionDontCare];
}
}
// Init samplers
m_sampler_hw[SamplerSelector::Linear().key] = CreateSampler(m_dev.dev, SamplerSelector::Linear());
@@ -1299,6 +1377,19 @@ bool GSDeviceMTL::UpdateWindow()
bool GSDeviceMTL::SupportsExclusiveFullscreen() const { return false; }
static const char* GetROVString(const GSMTLDevice::Features& features)
{
if (!features.rov)
return "Unsupported";
if (features.rov_requires_r32 && features.rov_requires_rt)
return "With R32 Reinterpret and RT Texture";
if (features.rov_requires_r32)
return "With R32 Reinterpret";
if (features.rov_requires_rt)
return "With RT Texture";
return "Supported";
}
std::string GSDeviceMTL::GetDriverInfo() const
{ @autoreleasepool {
std::string desc([[m_dev.dev description] UTF8String]);
@@ -1308,6 +1399,7 @@ std::string GSDeviceMTL::GetDriverInfo() const
desc += "\n Memoryless Textures: " + std::string(m_dev.features.memoryless_textures ? "Supported" : "Unsupported");
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
desc += "\n Depth Feedback: " + std::string(m_dev.features.depth_feedback ? "Supported" : "Unsupported");
desc += "\n ROV: " + std::string(GetROVString(m_dev.features));
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
return desc;
@@ -1968,7 +2060,10 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantI(m_fn_constants, pssel.aa1, GSMTLConstantIndex_PS_AA1);
setFnConstantB(m_fn_constants, pssel.abe, GSMTLConstantIndex_PS_ABE);
setFnConstantI(m_fn_constants, pssel.sw_aniso, GSMTLConstantIndex_PS_SW_ANISO);
auto newps = LoadShader(@"ps_main");
setFnConstantB(m_fn_constants, pssel.rov_color, GSMTLConstantIndex_PS_ROV_COLOR);
setFnConstantI(m_fn_constants, pssel.rov_depth, GSMTLConstantIndex_PS_ROV_DEPTH);
bool eft = pssel.HasColorROV() && !pssel.HasDepthROV() && !pssel.HasDepthOutput();
auto newps = LoadShader(eft ? @"ps_main_rov_eft" : @"ps_main");
ps = newps;
m_hw_ps.insert(std::make_pair(pssel, std::move(newps)));
}
@@ -2007,6 +2102,12 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
[color1 setPixelFormat:MTLPixelFormatR32Float];
}
if (m_dev.features.rov_requires_rt && extras.rt == GSTexture::Format::Invalid && !extras.has_depth && !extras.has_stencil)
{
// Dummy texture
[color setPixelFormat:MTLPixelFormatR8Unorm];
[color setWriteMask:MTLColorWriteMaskNone];
}
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%llx.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
@@ -2048,14 +2149,21 @@ static void textureBarrier(id<MTLRenderCommandEncoder> enc)
void GSDeviceMTL::MRESetTexture(GSTexture* tex, int pos)
{
if (tex == m_current_render.tex[pos])
if (!tex || tex == m_current_render.tex[pos])
return;
m_current_render.tex[pos] = tex;
if (GSTextureMTL* mtex = static_cast<GSTextureMTL*>(tex))
{
[m_current_render.encoder setFragmentTexture:mtex->GetTexture() atIndex:pos];
mtex->m_last_read = m_current_draw;
}
GSTextureMTL* mtex = static_cast<GSTextureMTL*>(tex);
[m_current_render.encoder setFragmentTexture:mtex->GetTexture() atIndex:pos];
mtex->m_last_read = m_current_draw;
}
void GSDeviceMTL::MRESetTexture(id<MTLTexture> tex, int pos)
{
GSTexture* gstex = reinterpret_cast<GSTexture*>(tex); // No one actually dereferences this
if (!tex || gstex == m_current_render.tex[pos])
return;
m_current_render.tex[pos] = gstex;
[m_current_render.encoder setFragmentTexture:tex atIndex:pos];
}
void GSDeviceMTL::MRESetVertices(id<MTLBuffer> buffer, size_t offset)
@@ -2100,6 +2208,7 @@ void GSDeviceMTL::MRESetScissor(const GSVector4i& scissor)
void GSDeviceMTL::MREClearScissor()
{
assert(!m_current_render.is_full_rov());
if (!m_current_render.has.scissor)
return;
m_current_render.has.scissor = false;
@@ -2227,6 +2336,15 @@ void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
MRESetVSIndices(verts.gpu_buffer, verts.gpu_offset + config.nverts * sizeof(*config.verts));
}
__fi void GSDeviceMTL::PrepareROVTexture(GSTexture** ptex)
{
GSTextureMTL* tex = static_cast<GSTextureMTL*>(*ptex);
FlushClears(tex);
tex->m_last_read = m_current_draw;
tex->m_last_write = m_current_draw;
*ptex = nullptr;
}
void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
{ @autoreleasepool {
if (config.tex && (config.ds == config.tex || config.rt == config.tex))
@@ -2363,7 +2481,7 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
}
// Try to reduce render pass restarts
if (!config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
if (!config.ps.HasColorROV() && !config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
config.ds = m_current_render.depth_target;
if (!rt && config.ds == m_current_render.depth_target && m_current_render.color_target != config.tex)
rt = m_current_render.color_target;
@@ -2378,16 +2496,30 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
}
const bool feedback_depth = config.ps.IsFeedbackLoopDepth();
const bool rt1 = feedback_depth && !m_features.depth_feedback;
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad, rt1);
const bool rt1 = feedback_depth && !m_features.depth_feedback && !config.ps.HasDepthROV();
GSTexture* rt_bind = rt;
GSTexture* ds_bind = config.ds;
GSTexture* rt_size = rt_bind ? rt_bind : ds_bind;
if (config.ps.HasColorROV() && rt_bind) PrepareROVTexture(&rt_bind);
if (config.ps.HasDepthROV() && ds_bind) PrepareROVTexture(&ds_bind);
if (!rt_bind && !ds_bind && !stencil)
BeginFullROV(@"RenderHWROV", rt_size->GetWidth(), rt_size->GetHeight());
else
BeginRenderPass(@"RenderHW", rt_bind, MTLLoadActionLoad, ds_bind, MTLLoadActionLoad, stencil, MTLLoadActionLoad, rt1);
id<MTLRenderCommandEncoder> mtlenc = m_current_render.encoder;
FlushDebugEntries(mtlenc);
if (usesStencil(config.destination_alpha))
[mtlenc setStencilReferenceValue:1];
MREInitHWDraw(config, allocation);
if (config.require_one_barrier || config.require_full_barrier)
if (config.ps.HasColorROV() && m_dev.features.rov_requires_r32)
MRESetTexture(reinterpret_cast<GSTextureMTL*>(rt)->GetROVTexture(), GSMTLTextureIndexRenderTarget);
else if (config.require_one_barrier || config.require_full_barrier || config.ps.HasColorROV())
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
if (feedback_depth)
if (config.ps.HasDepthROV())
{
MRESetTexture(config.ds, GSMTLTextureIndexDepthTarget);
}
else if (feedback_depth)
{
GSTexture* tex = !m_features.depth_feedback && !m_features.framebuffer_fetch ? m_ds_as_rt : config.ds;
MRESetTexture(tex, GSMTLTextureIndexDepthTarget);
@@ -34,6 +34,9 @@ struct GSMTLDevice
bool has_fast_half : 1;
bool memoryless_textures : 1;
bool depth_feedback : 1;
bool rov : 1;
bool rov_requires_rt : 1;
bool rov_requires_r32 : 1;
MetalVersion shader_version;
int max_texsize;
};
@@ -166,6 +166,9 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
features.depth_feedback = false;
features.rov = [dev areRasterOrderGroupsSupported];
features.rov_requires_r32 = [dev readWriteTextureSupport] < MTLReadWriteTextureTier2;
NSString* name = [dev name];
if ([name containsString:@"Intel"])
{
@@ -193,6 +196,9 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
if (@available(macOS 13, iOS 16, *))
if ([dev supportsFamily:MTLGPUFamilyMetal3])
features.depth_feedback = true;
// ROV doesn't work on AMD GPUs with no render target
// Ideally the driver would insert a dummy texture for us (it does on Linux) but the Metal driver doesn't.
features.rov_requires_rt = true;
}
else if ([name containsString:@"NVIDIA"])
{
@@ -219,6 +225,9 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
else
features.slow_color_compression = [[dev name] containsString:@"AMD"] || [[dev name] isEqualToString:@"Intel HD Graphics 4000"];
if (char* env = getenv("MTL_ROV_WITH_RT"))
features.rov_requires_rt = env[0] == '1' || env[0] == 'y' || env[0] == 'Y';
features.max_texsize = GetMaxTextureSize(dev);
this->dev = std::move(dev);
@@ -165,6 +165,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_CAS_SHARPEN_ONLY,
GSMTLConstantIndex_FRAMEBUFFER_FETCH,
GSMTLConstantIndex_DEPTH_FEEDBACK,
GSMTLConstantIndex_ROV_NEEDS_R32,
GSMTLConstantIndex_FST,
GSMTLConstantIndex_IIP,
GSMTLConstantIndex_VS_POINT_SIZE,
@@ -227,4 +228,6 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_AA1,
GSMTLConstantIndex_PS_ABE,
GSMTLConstantIndex_PS_SW_ANISO,
GSMTLConstantIndex_PS_ROV_COLOR,
GSMTLConstantIndex_PS_ROV_DEPTH,
};
+3 -1
View File
@@ -21,12 +21,13 @@ class GSTextureMTL : public GSTexture
{
GSDeviceMTL* m_dev;
MRCOwned<id<MTLTexture>> m_texture;
MRCOwned<id<MTLTexture>> m_rov_texture;
bool m_has_mipmaps = false;
public:
u64 m_last_read = 0; ///< Last time this texture was read by a draw
u64 m_last_write = 0; ///< Last time this texture was written by a draw
GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, Usage usage, Format format);
GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, MRCOwned<id<MTLTexture>> rov_texture, Usage usage, Format format);
~GSTextureMTL();
/// For making fake backbuffers
@@ -42,6 +43,7 @@ public:
void Unmap() override;
void GenerateMipmap() override;
id<MTLTexture> GetTexture() { return m_texture; }
id<MTLTexture> GetROVTexture() { return m_rov_texture; }
#ifdef PCSX2_DEVBUILD
void SetDebugName(std::string_view name) override;
+4 -1
View File
@@ -13,9 +13,10 @@
// Uploads/downloads need 32-byte alignment for AVX2.
static constexpr u32 PITCH_ALIGNMENT = 32;
GSTextureMTL::GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, Usage usage, Format format)
GSTextureMTL::GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, MRCOwned<id<MTLTexture>> rov_texture, Usage usage, Format format)
: m_dev(dev)
, m_texture(std::move(texture))
, m_rov_texture(std::move(rov_texture))
{
m_usage = usage;
m_format = format;
@@ -141,6 +142,8 @@ void GSTextureMTL::SetDebugName(std::string_view name)
length:static_cast<NSUInteger>(name.length())
encoding:NSUTF8StringEncoding];
[m_texture setLabel:label];
if (m_rov_texture)
[m_rov_texture setLabel:[label stringByAppendingString:@".ROV"]];
}
m_debug_name = std::move(name);
+149 -45
View File
@@ -13,6 +13,7 @@ constant uint SHUFFLE_READWRITE = 3;
constant bool HAS_FBFETCH [[function_constant(GSMTLConstantIndex_FRAMEBUFFER_FETCH)]];
constant bool DEPTH_FEEDBACK [[function_constant(GSMTLConstantIndex_DEPTH_FEEDBACK)]];
constant bool ROV_NEEDS_R32 [[function_constant(GSMTLConstantIndex_ROV_NEEDS_R32)]];
constant bool FST [[function_constant(GSMTLConstantIndex_FST)]];
constant bool IIP [[function_constant(GSMTLConstantIndex_IIP)]];
constant bool VS_POINT_SIZE [[function_constant(GSMTLConstantIndex_VS_POINT_SIZE)]];
@@ -75,17 +76,21 @@ constant uint PS_SCANMSK [[function_constant(GSMTLConstantIndex_PS_SC
constant uint PS_AA1_RAW [[function_constant(GSMTLConstantIndex_PS_AA1)]];
constant bool PS_ABE [[function_constant(GSMTLConstantIndex_PS_ABE)]];
constant uint PS_SW_ANISO [[function_constant(GSMTLConstantIndex_PS_SW_ANISO)]];
constant bool PS_ROV_COLOR [[function_constant(GSMTLConstantIndex_PS_ROV_COLOR)]];
constant uint PS_ROV_DEPTH_RAW [[function_constant(GSMTLConstantIndex_PS_ROV_DEPTH)]];
using GSShader::VSExpand;
using AFAIL = GSShader::PS_AFAIL;
using ATST = GSShader::PS_ATST;
using GSShader::ZTST;
using AA1 = GSShader::PS_AA1;
using ROV_DEPTH = GSShader::PS_ROV_DEPTH;
constant VSExpand VS_EXPAND_TYPE = static_cast<VSExpand>(VS_EXPAND_TYPE_RAW);
constant AFAIL PS_AFAIL = static_cast<AFAIL>(PS_AFAIL_RAW);
constant ATST PS_ATST = static_cast<ATST>(PS_ATST_RAW);
constant ZTST PS_ZTST = static_cast<ZTST>(PS_ZTST_RAW);
constant AA1 PS_AA1 = static_cast<AA1>(PS_AA1_RAW);
constant ROV_DEPTH PS_ROV_DEPTH = static_cast<ROV_DEPTH>(PS_ROV_DEPTH_RAW);
#if defined(__METAL_MACOS__) && __METAL_VERSION__ >= 220
#define PRIMID_SUPPORT 1
@@ -119,9 +124,9 @@ constant bool NEEDS_DEPTH_FOR_ZTST = PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST
constant bool NEEDS_DEPTH_FOR_AA1 = PS_AA1 == AA1::TRIANGLE_SW_Z;
constant bool SW_DEPTH = NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1;
constant bool PS_COLOR0 = !PS_NO_COLOR;
constant bool PS_COLOR1 = !PS_NO_COLOR1;
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH;
constant bool PS_OUTPUT_COLOR0 = !PS_NO_COLOR && !PS_ROV_COLOR;
constant bool PS_OUTPUT_COLOR1 = !PS_NO_COLOR1 && !PS_ROV_COLOR;
constant bool PS_ZOUTPUT = (PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH) && PS_ROV_DEPTH == ROV_DEPTH::NONE;
constant bool PS_ZOUTPUT_LESS = PS_ZOUTPUT && !SW_DEPTH;
constant bool PS_ZOUTPUT_ANY = PS_ZOUTPUT && SW_DEPTH;
constant bool PS_ZOUTPUT_COLOR = PS_ZOUTPUT_ANY && !DEPTH_FEEDBACK;
@@ -165,21 +170,32 @@ struct MainPSIn
uint interior [[function_constant(PS_INTERIOR)]];
};
struct MainResult
{
float4 c0;
float4 c1;
float depth;
};
struct MainPSOut
{
float4 c0 [[color(0), index(0), function_constant(PS_COLOR0)]];
float4 c1 [[color(0), index(1), function_constant(PS_COLOR1)]];
float4 c0 [[color(0), index(0), function_constant(PS_OUTPUT_COLOR0)]];
float4 c1 [[color(0), index(1), function_constant(PS_OUTPUT_COLOR1)]];
float depthColor [[color(1), function_constant(PS_ZOUTPUT_COLOR)]];
float depthLess [[depth(less), function_constant(PS_ZOUTPUT_LESS)]];
float depthAny [[depth(any), function_constant(PS_ZOUTPUT_ANY)]];
void setDepth(float depth)
MainPSOut(MainResult res)
{
if (PS_OUTPUT_COLOR0)
c0 = res.c0;
if (PS_OUTPUT_COLOR1)
c1 = res.c1;
if (PS_ZOUTPUT_LESS)
depthLess = depth;
depthLess = res.depth;
if (PS_ZOUTPUT_ANY)
depthAny = depth;
depthAny = res.depth;
if (PS_ZOUTPUT_COLOR)
depthColor = depth;
depthColor = res.depth;
}
};
@@ -539,12 +555,6 @@ vertex MainVSOut vs_main_expand(
out.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
out.interior = 0;
if (NOT_IIP)
{
// Get the provoking vertex color (first vertex in Metal)
out.fc = i0 == 0 ? out.fc : (i1 == 0 ? other.fc : opposite.fc);
}
}
return out;
@@ -564,11 +574,37 @@ struct PSMain
float4 current_color;
float current_depth;
uint prim_id;
bool color_discarded = false;
bool depth_discarded = false;
const thread MainPSIn& in;
constant GSMTLMainPSUniform& cb;
PSMain(const thread MainPSIn& in, constant GSMTLMainPSUniform& cb): in(in), cb(cb) {}
void discard()
{
if (PS_ROV_COLOR || PS_ROV_DEPTH != ROV_DEPTH::NONE)
color_discarded = depth_discarded = true;
else
discard_fragment();
}
void discard_color(thread float4& output)
{
if (PS_ROV_COLOR)
color_discarded = true;
else
output = current_color;
}
void discard_depth(thread float& output)
{
if (PS_ROV_DEPTH == ROV_DEPTH::READ_WRITE)
depth_discarded = true;
else
output = current_depth;
}
template <typename... Args>
float4 sample_tex(Args... args)
{
@@ -1454,9 +1490,9 @@ struct PSMain
}
}
MainPSOut ps_main()
MainResult ps_main()
{
MainPSOut out = {};
MainResult out = {};
float input_z = in.p.z;
if (PS_ZFLOOR)
input_z = floor(input_z * 0x1p32) * 0x1p-32;
@@ -1464,15 +1500,15 @@ struct PSMain
if (PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER)
{
if (PS_ZTST == ZTST::GEQUAL && input_z < current_depth)
discard_fragment();
discard();
if (PS_ZTST == ZTST::GREATER && input_z <= current_depth)
discard_fragment();
discard();
}
if (PS_SCANMSK & 2)
{
if ((uint(in.p.y) & 1) == (PS_SCANMSK & 1))
discard_fragment();
discard();
}
if (PS_DATE >= 5)
@@ -1482,7 +1518,7 @@ struct PSMain
bool bad = PS_RTA_CORRECTION ? ((PS_DATE & 3) == 1 ? (rt_a > (254.5f / 255.f)) : (rt_a < (254.5f / 255.f))) : ((PS_DATE & 3) == 1 ? (rt_a > 0.5) : (rt_a < 0.5));
if (bad)
discard_fragment();
discard();
}
if (PS_DATE == 3)
@@ -1491,7 +1527,7 @@ struct PSMain
// Note prim_id == stencil_ceil will be the primitive that will update
// the bad alpha value so we must keep it.
if (float(prim_id) > stencil_ceil)
discard_fragment();
discard();
}
float4 C = ps_color();
@@ -1500,9 +1536,9 @@ struct PSMain
if (PS_AA1 != AA1::NONE)
{
float cov = PS_AA1 == AA1::LINE ?
saturate(cb.line_cov_scale * (1.f - abs(in.inv_cov))) : // Blur only outer part of the line by scaling coverage.
saturate(1.f - abs(in.inv_cov));
float cov = PS_AA1 == AA1::LINE
? saturate(cb.line_cov_scale * (1.f - abs(in.inv_cov))) // Blur only outer part of the line by scaling coverage.
: saturate(1.f - abs(in.inv_cov));
if (!PS_ABE || floor(C.a) == 128.f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.f * cov;
}
@@ -1514,7 +1550,7 @@ struct PSMain
bool atst_pass = atst(C);
if (PS_AFAIL == AFAIL::KEEP && !atst_pass)
discard_fragment();
discard();
float4 alpha_blend = float4(0.f);
if (SW_AD_TO_HW)
@@ -1620,32 +1656,32 @@ struct PSMain
if (PS_AFAIL == AFAIL::RGB_ONLY_DSB)
alpha_blend.a = float(atst_pass);
if (PS_COLOR0)
if (!PS_NO_COLOR)
{
out.c0.a = PS_RTA_CORRECTION ? C.a / 128.f : C.a / 255.f;
out.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.f) : C.rgb / 255.f;
}
if (PS_COLOR1)
if (!PS_NO_COLOR1)
out.c1 = alpha_blend;
if (PS_ZCLAMP)
input_z = min(input_z, cb.max_depth);
if (PS_AA1 == AA1::TRIANGLE_SW_Z && !in.interior)
input_z = current_depth; // No depth update for triangle edges.
discard_depth(input_z); // No depth update for triangle edges.
if (!atst_pass)
{
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::RGB_ONLY)
out.c0.a = current_color.a;
out.c0.a = current_color.a; // discard alpha
else if (PS_AFAIL == AFAIL::ZB_ONLY)
out.c0 = current_color;
discard_color(out.c0);
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::FB_ONLY)
input_z = current_depth;
discard_depth(input_z);
}
out.setDepth(input_z);
out.depth = input_z;
return out;
}
@@ -1657,16 +1693,19 @@ fragment float4 fbfetch_test(float4 in [[color(0), raster_order_group(0)]])
return in * 2;
}
constant bool NEEDS_RT_TEX = NEEDS_RT && !HAS_FBFETCH;
constant bool NEEDS_RT_FBF = NEEDS_RT && HAS_FBFETCH;
constant bool NEEDS_DS_FBF = SW_DEPTH && HAS_FBFETCH && !DEPTH_FEEDBACK;
constant bool NEEDS_RT_TEX = NEEDS_RT && !HAS_FBFETCH && !PS_ROV_COLOR;
constant bool NEEDS_RT_FBF = NEEDS_RT && HAS_FBFETCH && !PS_ROV_COLOR;
constant bool NEEDS_DS_FBF = SW_DEPTH && HAS_FBFETCH && !DEPTH_FEEDBACK && PS_ROV_DEPTH == ROV_DEPTH::NONE;
#else
constant bool NEEDS_RT_TEX = NEEDS_RT;
constant bool NEEDS_RT_TEX = NEEDS_RT && !PS_ROV_COLOR;
constant bool NEEDS_DS_FBF = false;
constant float ds_fbf = 0;
#endif
constant bool NEEDS_DS_TEX = SW_DEPTH && !DEPTH_FEEDBACK && !NEEDS_DS_FBF;
constant bool NEEDS_DS_DEPTH = SW_DEPTH && DEPTH_FEEDBACK || NEEDS_DS_FBF;
constant bool NEEDS_DS_TEX = SW_DEPTH && !DEPTH_FEEDBACK && !NEEDS_DS_FBF && PS_ROV_DEPTH == ROV_DEPTH::NONE;
constant bool NEEDS_DS_DEPTH = (SW_DEPTH && DEPTH_FEEDBACK || NEEDS_DS_FBF) && PS_ROV_DEPTH == ROV_DEPTH::NONE;
constant bool NEEDS_RT_ROV = PS_ROV_COLOR && !ROV_NEEDS_R32;
constant bool NEEDS_RT_U32 = PS_ROV_COLOR && ROV_NEEDS_R32;
constant bool NEEDS_DS_ROV = PS_ROV_DEPTH != ROV_DEPTH::NONE;
fragment MainPSOut ps_main(
MainPSIn in [[stage_in]],
@@ -1685,7 +1724,10 @@ fragment MainPSOut ps_main(
texture2d<float> rt [[texture(GSMTLTextureIndexRenderTarget), function_constant(NEEDS_RT_TEX)]],
texture2d<float> primidtex [[texture(GSMTLTextureIndexPrimIDs), function_constant(PS_PRIM_CHECKING_READ)]],
texture2d<float> ds_tex [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_TEX)]],
depth2d<float> ds_depth [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_DEPTH)]])
depth2d<float> ds_depth [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_DEPTH)]],
texture2d<float, access::read_write> rt_rov [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_ROV)]],
texture2d<uint, access::read_write> rt_u32 [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_U32)]],
texture2d<float, access::read_write> ds_rov [[texture(GSMTLTextureIndexDepthTarget), raster_order_group(1), function_constant(NEEDS_DS_ROV)]])
{
PSMain main(in, cb);
main.tex_sampler = s;
@@ -1706,7 +1748,9 @@ fragment MainPSOut ps_main(
if (SW_DEPTH)
{
if (DEPTH_FEEDBACK)
if (PS_ROV_DEPTH != ROV_DEPTH::NONE)
main.current_depth = ds_rov.read(coord).x;
else if (DEPTH_FEEDBACK)
main.current_depth = ds_depth.read(coord);
else if (NEEDS_DS_FBF)
main.current_depth = ds_fbf < 0 ? ds_depth.read(coord) : ds_fbf;
@@ -1714,20 +1758,80 @@ fragment MainPSOut ps_main(
main.current_depth = ds_tex.read(coord).x;
}
if (NEEDS_RT)
if (NEEDS_RT || (PS_ROV_COLOR && any(cb.fbmask == 0xff)))
{
if (PS_ROV_COLOR)
{
if (ROV_NEEDS_R32)
main.current_color = unpack_unorm4x8_to_float(rt_u32.read(coord).x);
else
main.current_color = rt_rov.read(coord);
}
else
{
#if FBFETCH_SUPPORT
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(coord);
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(coord);
#else
main.current_color = rt.read(coord);
main.current_color = rt.read(coord);
#endif
}
}
else
{
main.current_color = 0;
}
return main.ps_main();
MainResult out = main.ps_main();
if (PS_ROV_DEPTH == ROV_DEPTH::READ_WRITE && !main.depth_discarded)
ds_rov.write(out.depth, coord);
if (PS_ROV_COLOR && !main.color_discarded)
{
if (!PS_FBMASK)
out.c0 = select(out.c0, main.current_color, cb.fbmask == 0xff);
if (ROV_NEEDS_R32)
rt_u32.write(pack_float_to_unorm4x8(out.c0), coord);
else
rt_rov.write(out.c0, coord);
}
return out;
}
// Metal doesn't let you toggle eft with function constants so we need a separate function for it
[[early_fragment_tests]]
fragment void ps_main_rov_eft(
MainPSIn in [[stage_in]],
constant GSMTLMainPSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]],
sampler s [[sampler(0)]],
texture2d<float> tex [[texture(GSMTLTextureIndexTex), function_constant(PS_TEX_IS_COLOR)]],
depth2d<float> depth [[texture(GSMTLTextureIndexTex), function_constant(PS_TEX_IS_DEPTH)]],
texture2d<float> palette [[texture(GSMTLTextureIndexPalette), function_constant(PS_HAS_PALETTE)]],
texture2d<float, access::read_write> rt_rov [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_ROV)]],
texture2d<uint, access::read_write> rt_u32 [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_U32)]])
{
PSMain main(in, cb);
main.tex_sampler = s;
if (PS_TEX_IS_COLOR)
main.tex = tex;
else
main.tex_depth = depth;
if (PS_HAS_PALETTE)
main.palette = palette;
uint2 coord = uint2(in.p.xy);
if (ROV_NEEDS_R32)
main.current_color = unpack_unorm4x8_to_float(rt_u32.read(coord).x);
else
main.current_color = rt_rov.read(coord);
MainPSOut out = main.ps_main();
if (!main.color_discarded)
{
if (!PS_FBMASK)
out.c0 = select(out.c0, main.current_color, cb.fbmask == 0xff);
if (ROV_NEEDS_R32)
rt_u32.write(pack_float_to_unorm4x8(out.c0), coord);
else
rt_rov.write(out.c0, coord);
}
}
#if PRIMID_SUPPORT
+1 -1
View File
@@ -1262,7 +1262,7 @@ void GSDeviceOGL::DestroyPipelineStatisticsQueries()
glEndQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB);
}
for (int i = 0; i < m_pipeline_statistics_queries.size(); i++)
for (size_t i = 0; i < m_pipeline_statistics_queries.size(); i++)
{
glDeleteQueries(2, m_pipeline_statistics_queries[i].data());
m_pipeline_statistics_queries[i].fill(0);
+1 -1
View File
@@ -1030,7 +1030,7 @@ void GameDatabase::initDatabase()
const ryml::csubstr yaml = ryml::to_csubstr(*buffer);
Error error;
std::optional<ryml::Tree> tree = ParseYAMLFromString(yaml, ryml::to_csubstr(name), &error);
std::optional<ryml::Tree> tree = ParseYAMLFromString(yaml, ryml::to_csubstr(name), &error, true);
if (!tree.has_value())
{
Console.ErrorFmt("GameDB: Failed to parse game database file {}:", path);
+10 -24
View File
@@ -1298,9 +1298,11 @@ void FullscreenUI::DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size)
#else
localtime_r(&utc_time_t, &tm_local);
#endif
heading_str.format(FSUI_FSTR("{:%H:%M}"), tm_local);
char buf[256];
std::strftime(buf, sizeof(buf), "%X", &tm_local);
heading_str.assign(buf);
const ImVec2 time_size = heading_font.first->CalcTextSizeA(heading_font.second, FLT_MAX, 0.0f, "00:00");
const ImVec2 time_size = heading_font.first->CalcTextSizeA(heading_font.second, FLT_MAX, 0.0f, heading_str.c_str());
time_pos = ImVec2(heading_size.x - LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING) - time_size.x,
LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING));
ImGuiFullscreen::AddTextWithShadow(
@@ -1563,20 +1565,10 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
const float image_width = 60.0f;
const float image_height = 90.0f;
const std::string_view path_string(Path::GetFileName(s_current_disc_path));
const ImVec2 title_size(
g_large_font.first->CalcTextSizeA(g_large_font.second, std::numeric_limits<float>::max(), -1.0f, s_current_game_title.c_str()));
const ImVec2 path_size(path_string.empty() ?
ImVec2(0.0f, 0.0f) :
g_medium_font.first->CalcTextSizeA(g_medium_font.second, std::numeric_limits<float>::max(), -1.0f,
path_string.data(), path_string.data() + path_string.length()));
const ImVec2 subtitle_size(g_medium_font.first->CalcTextSizeA(
g_medium_font.second, std::numeric_limits<float>::max(), -1.0f, s_current_game_subtitle.c_str()));
ImVec2 title_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - title_size.x,
display_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - LayoutScale(10.0f + image_height));
ImVec2 path_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - path_size.x,
title_pos.y + g_large_font.second + LayoutScale(4.0f));
ImVec2 subtitle_pos(display_size.x - LayoutScale(10.0f + image_width + 20.0f) - subtitle_size.x,
ImVec2 title_pos(LayoutScale(10.0f + image_width + 20.0f), LayoutScale(10.0f));
ImVec2 path_pos(LayoutScale(10.0f + image_width + 20.0f), title_pos.y + g_large_font.second + LayoutScale(4.0f));
ImVec2 subtitle_pos(LayoutScale(10.0f + image_width + 20.0f),
(path_string.empty() ? title_pos.y + g_large_font.second : path_pos.y + g_medium_font.second) + LayoutScale(4.0f));
float rp_height = 0.0f;
@@ -1593,12 +1585,8 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
// Add a small extra gap if any Rich Presence is displayed
rp_height = rp_size.y - g_medium_font.second + LayoutScale(2.0f);
const ImVec2 rp_pos(display_size.x - LayoutScale(20.0f + 50.0f + 20.0f) - rp_size.x,
subtitle_pos.y + g_medium_font.second + LayoutScale(4.0f) - rp_height);
title_pos.y -= rp_height;
path_pos.y -= rp_height;
subtitle_pos.y -= rp_height;
const ImVec2 rp_pos(LayoutScale(10.0f + image_width + 20.0f),
subtitle_pos.y + g_medium_font.second + LayoutScale(4.0f));
DrawShadowedText(dl, g_medium_font, rp_pos, text_color, rp.data(), rp.data() + rp.length(), wrap_width);
}
@@ -1611,8 +1599,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
}
DrawShadowedText(dl, g_medium_font, subtitle_pos, text_color, s_current_game_subtitle.c_str());
const ImVec2 image_min(display_size.x - LayoutScale(10.0f + image_width),
display_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - LayoutScale(10.0f + image_height) - rp_height);
const ImVec2 image_min(LayoutScale(10.0f), LayoutScale(10.0f));
const ImVec2 image_max(image_min.x + LayoutScale(image_width), image_min.y + LayoutScale(image_height) + rp_height);
{
auto lock = GameList::GetLock();
@@ -4080,7 +4067,6 @@ TRANSLATE_NOOP("FullscreenUI", "Downloading covers...");
TRANSLATE_NOOP("FullscreenUI", "An error occurred while deleting empty game settings:\n{}");
TRANSLATE_NOOP("FullscreenUI", "An error occurred while saving game settings:\n{}");
TRANSLATE_NOOP("FullscreenUI", "{} is not a valid disc image.");
TRANSLATE_NOOP("FullscreenUI", "{:%H:%M}");
TRANSLATE_NOOP("FullscreenUI", "This Session: {}");
TRANSLATE_NOOP("FullscreenUI", "All Time: {}");
TRANSLATE_NOOP("FullscreenUI", "Save Slot {0}");
+2 -2
View File
@@ -607,12 +607,12 @@ bool IOPMemoryInterface::Write128(u32 address, u128 value)
return false;
}
bool IOPMemoryInterface::WriteBytes(u32 address, void* src, u32 size)
bool IOPMemoryInterface::WriteBytes(u32 address, const void* src, u32 size)
{
return iopMemSafeWriteBytes(address, src, size);
}
bool IOPMemoryInterface::CompareBytes(u32 address, void* src, u32 size)
bool IOPMemoryInterface::CompareBytes(u32 address, const void* src, u32 size)
{
return iopMemSafeCmpBytes(address, src, size) == 0;
}
+2 -2
View File
@@ -136,7 +136,7 @@ public:
bool Write32(u32 address, u32 value) override;
bool Write64(u32 address, u64 value) override;
bool Write128(u32 address, u128 value) override;
bool WriteBytes(u32 address, void* src, u32 size) override;
bool WriteBytes(u32 address, const void* src, u32 size) override;
bool CompareBytes(u32 address, void* src, u32 size) override;
bool CompareBytes(u32 address, const void* src, u32 size) override;
};
+2 -2
View File
@@ -1257,12 +1257,12 @@ bool EEMemoryInterface::Write128(u32 address, u128 value)
return true;
}
bool EEMemoryInterface::WriteBytes(u32 address, void* src, u32 size)
bool EEMemoryInterface::WriteBytes(u32 address, const void* src, u32 size)
{
return vtlb_memSafeWriteBytes(address, src, size);
}
bool EEMemoryInterface::CompareBytes(u32 address, void* src, u32 size)
bool EEMemoryInterface::CompareBytes(u32 address, const void* src, u32 size)
{
return vtlb_memSafeCmpBytes(address, src, size) == 0;
}
+2 -2
View File
@@ -206,7 +206,7 @@ public:
bool Write32(u32 address, u32 value) override;
bool Write64(u32 address, u64 value) override;
bool Write128(u32 address, u128 value) override;
bool WriteBytes(u32 address, void* src, u32 size) override;
bool WriteBytes(u32 address, const void* src, u32 size) override;
bool CompareBytes(u32 address, void* src, u32 size) override;
bool CompareBytes(u32 address, const void* src, u32 size) override;
};
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 106; // Last changed in PR 14643
static constexpr u32 SHADER_CACHE_VERSION = 107; // Last changed in PR 14634
+6 -9
View File
@@ -13,6 +13,7 @@
#include "USB/USB.h"
#include <jpeglib.h>
#include <wil/com.h>
#ifdef __clang__
#pragma clang diagnostic push
@@ -57,16 +58,15 @@ namespace usb_eyetoy
{
std::vector<std::pair<std::string, std::string>> devList;
ICreateDevEnum* pCreateDevEnum = nullptr;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum));
if (FAILED(hr))
wil::com_ptr_nothrow<ICreateDevEnum> pCreateDevEnum = wil::CoCreateInstanceNoThrow<ICreateDevEnum>(CLSID_SystemDeviceEnum);
if (!pCreateDevEnum)
{
Console.Warning("Camera: Error Creating Device Enumerator");
return devList;
}
wil::com_ptr_nothrow<IEnumMoniker> pEnum;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
HRESULT hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0);
if (hr != S_OK)
{
Console.Warning("Camera: You have no video capture hardware");
@@ -97,8 +97,6 @@ namespace usb_eyetoy
}
}
CoUninitialize();
return devList;
}
@@ -136,9 +134,8 @@ namespace usb_eyetoy
}
// enumerate all video capture devices
ICreateDevEnum* pCreateDevEnum = nullptr;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pCreateDevEnum));
if (FAILED(hr))
wil::com_ptr_nothrow<ICreateDevEnum> pCreateDevEnum = wil::CoCreateInstanceNoThrow<ICreateDevEnum>(CLSID_SystemDeviceEnum);
if (!pCreateDevEnum)
{
Console.Warning("Camera: Error Creating Device Enumerator");
return -1;
+2 -2
View File
@@ -21,9 +21,9 @@ public:
MOCK_METHOD(bool, Write32, (u32 address, u32 value), (override));
MOCK_METHOD(bool, Write64, (u32 address, u64 value), (override));
MOCK_METHOD(bool, Write128, (u32 address, u128 value), (override));
MOCK_METHOD(bool, WriteBytes, (u32 address, void* src, u32 size), (override));
MOCK_METHOD(bool, WriteBytes, (u32 address, const void* src, u32 size), (override));
MOCK_METHOD(bool, CompareBytes, (u32 address, void* src, u32 size), (override));
MOCK_METHOD(bool, CompareBytes, (u32 address, const void* src, u32 size), (override));
struct SetValidOutParameterAction
{
+5 -10
View File
@@ -6,7 +6,6 @@
#include "common/FileSystem.h"
#include "common/Console.h"
#include "common/ScopedGuard.h"
#include "common/StringUtil.h"
#include "common/ProgressCallback.h"
#include "common/RedtapeWilCom.h"
@@ -18,6 +17,7 @@
#include <thread>
#include <wil/resource.h>
#include <wil/result.h>
#include <wil/win32_helpers.h>
#pragma comment(lib, "synchronization.lib")
@@ -76,7 +76,7 @@ private:
wil::com_ptr_nothrow<ITaskbarList3> m_taskbar_list;
int m_last_progress_percent = -1;
bool m_com_initialized = false;
wil::unique_couninitialize_call m_com_initialized{false};
static inline const UINT s_uTBBC = RegisterWindowMessageW(L"TaskbarButtonCreated");
static constexpr UINT WMAPP_SETTASKBARPROGRESS = WM_USER+0;
@@ -192,7 +192,7 @@ bool Win32ProgressCallback::Create()
class_registered = true;
}
m_com_initialized = SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
m_com_initialized = wil::CoInitializeEx_failfast(COINIT_APARTMENTTHREADED);
m_window_hwnd =
CreateWindowExW(WS_EX_CLIENTEDGE, CLASS_NAME, L"Win32ProgressCallback", WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX, CW_USEDEFAULT,
@@ -222,8 +222,7 @@ void Win32ProgressCallback::Destroy()
m_progress_hwnd = {};
}
if (m_com_initialized)
CoUninitialize();
m_com_initialized.reset();
}
void Win32ProgressCallback::PumpMessages()
@@ -420,11 +419,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLi
{
Win32ProgressCallback progress;
const bool com_initialized = SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
const ScopedGuard com_guard = [com_initialized]() {
if (com_initialized)
CoUninitialize();
};
wil::unique_couninitialize_call uninit = wil::CoInitializeEx_failfast(COINIT_MULTITHREADED);
int argc = 0;
wil::unique_hlocal_ptr<LPWSTR[]> argv(CommandLineToArgvW(GetCommandLineW(), &argc));