Compare commits

...

103 Commits

Author SHA1 Message Date
JordanTheToaster
380c316869 Deps: Update SDL3 3.2.16 2025-06-29 16:40:27 -04:00
Florin9doi
31dacc2d21 Update Negcon to support Wipeout Fusion 2025-06-29 16:40:17 -04:00
Ty
b557a82009 [ci skip] 2.5.x Development Cycle 2025-06-29 16:34:51 -04:00
Ty
e4af1c4244 [ci-skip] 2.4 Release 2025-06-29 15:47:12 -04:00
JordanTheToaster
7c26ac5578 GameDB: Various fixes 2025-06-29 21:07:07 +02:00
JordanTheToaster
2948d50b0d GameDB: Shadow Hearts fixes 2025-06-29 10:32:13 +02:00
lightningterror
48fefddcb2 GS/HW: Fix -Wunused-variable warnings. 2025-06-29 10:17:57 +02:00
lightningterror
f4d8af2f0d GS/GL: Don't re enable blend after SetupDATE.
Redundant calls, blending will be enabled if needed.
2025-06-29 10:17:57 +02:00
lightningterror
afa3108623 GS/DX11: Adjust blend/depth stencil state.
OMSetDepthStencilState:
If state is nullptr, new state is also nullptr but stencil ref changes no need to set a new OMSetDepthStencilState.

OMSetBlendState:
If state is nullptr, new state is also nullptr but blend factor changes no need to set a new OMSetBlendState.
2025-06-29 10:17:57 +02:00
lightningterror
6fcfddf19a GS/GL/DX11: Resolve potential rtv/srv conflict for primid date.
primid_texture texture was already bound to rtv, so we either need to unbind it first from rtv, or we can bind it to srv after rtv bind has been changed.

Note: DX11 is fine since gpu state will be updated when DrawIndexedPrimitive is executed but it's nice to keep code parity and visual a visual state
what the pipeline order should do.
2025-06-29 10:17:57 +02:00
lightningterror
6d2f442cbd GS/DX11: Backports the avoid framebuffer optimization from GL.
Unlike OpenGL, we don't need to worry about adding extra barriers here
since we already do fb copies so rt tex won't be bound as an rtv and srv
at the same time, we also check for conflicts beforehand.
2025-06-29 10:17:57 +02:00
PCSX2 Bot
e8260e7191 [ci skip] Qt: Update Base Translation. 2025-06-28 20:04:49 -04:00
Ty
48a4367a2c New translations pcsx2-qt_en.ts (Spanish, Latin America)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
edd3540f34 New translations pcsx2-qt_en.ts (Guarani)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
111c32c5ed New translations pcsx2-qt_en.ts (Quechua)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
af22ca3ae0 New translations pcsx2-qt_en.ts (Hindi)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
5e2caa326c New translations pcsx2-qt_en.ts (Latvian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
50f9f60341 New translations pcsx2-qt_en.ts (Croatian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
65f61b143a New translations pcsx2-qt_en.ts (Persian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
10f4892b7e New translations pcsx2-qt_en.ts (Indonesian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
f71d518f75 New translations pcsx2-qt_en.ts (Portuguese, Brazilian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
c5b11fe484 New translations pcsx2-qt_en.ts (Vietnamese)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
068fd04f15 New translations pcsx2-qt_en.ts (Chinese Traditional)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
a461750b55 New translations pcsx2-qt_en.ts (Chinese Simplified)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
291df6a0e4 New translations pcsx2-qt_en.ts (Ukrainian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
8afeb4ea61 New translations pcsx2-qt_en.ts (Turkish)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
b30f246900 New translations pcsx2-qt_en.ts (Swedish)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
7fd042b197 New translations pcsx2-qt_en.ts (Serbian (Cyrillic))
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
b6b3e364f2 New translations pcsx2-qt_en.ts (Russian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
70e5f18971 New translations pcsx2-qt_en.ts (Portuguese)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
1fb35593d5 New translations pcsx2-qt_en.ts (Polish)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
b1b9c32644 New translations pcsx2-qt_en.ts (Norwegian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
8eeed7d42c New translations pcsx2-qt_en.ts (Dutch)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
584493d945 New translations pcsx2-qt_en.ts (Lithuanian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
da8e551e77 New translations pcsx2-qt_en.ts (Korean)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
a717930d5a New translations pcsx2-qt_en.ts (Georgian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
abc000f614 New translations pcsx2-qt_en.ts (Japanese)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
cd51f1def3 New translations pcsx2-qt_en.ts (Italian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
f5573cf0ab New translations pcsx2-qt_en.ts (Hungarian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
1d036a4897 New translations pcsx2-qt_en.ts (Hebrew)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
f9eced6244 New translations pcsx2-qt_en.ts (Finnish)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
90a7253c66 New translations pcsx2-qt_en.ts (Greek)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
31e065f83a New translations pcsx2-qt_en.ts (German)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
571f443339 New translations pcsx2-qt_en.ts (Danish)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
4a4157919c New translations pcsx2-qt_en.ts (Czech)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
e73aa0e81b New translations pcsx2-qt_en.ts (Catalan)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
2343246315 New translations pcsx2-qt_en.ts (Bulgarian)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
e5f41ce175 New translations pcsx2-qt_en.ts (Arabic)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
8184f2eaa9 New translations pcsx2-qt_en.ts (Afrikaans)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
e0bb465945 New translations pcsx2-qt_en.ts (Spanish)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
55749a63eb New translations pcsx2-qt_en.ts (French)
[ci skip]
2025-06-28 13:31:32 -04:00
Ty
aa1b8db3ea New translations pcsx2-qt_en.ts (Romanian)
[ci skip]
2025-06-28 13:31:32 -04:00
JordanTheToaster
023713fd67 GameDB: The Mummy fixes 2025-06-28 13:05:00 -04:00
refractionpcsx2
35093c3e37 GS/HW: Avoid single pixel overlap on lookups + improve buffer splitting 2025-06-27 18:04:29 +02:00
refractionpcsx2
4e5dac3e25 GameDB: Add fixes for MLB 11 and Toro to Kyuujitsu 2025-06-27 18:03:48 +02:00
refractionpcsx2
c52e84ac41 GS/HW: Add ability to detect 16bit clears with 32bit draw 2025-06-27 18:03:48 +02:00
refractionpcsx2
7f8488771d GS/TC: Improve heuristics for sequenced draws inside targets 2025-06-27 18:03:48 +02:00
refractionpcsx2
57ff271f4b GS/HW: Fix up offset Z behaviours and copy ranges 2025-06-27 18:03:48 +02:00
refractionpcsx2
43703755f8 GS/HW: Improve double half clear detection heuristics 2025-06-27 18:03:48 +02:00
refractionpcsx2
17b6cc00ab GS/HW: When expanding a target for display, expand valid area 2025-06-27 18:03:19 +02:00
JordanTheToaster
a03563b366 GameDB: The Golden Compass fixes 2025-06-26 13:44:29 +02:00
lightningterror
ff9da17498 GS/DX11: Allow to pick whenever to update sr or ss, and some reordering.
No need to call sampler update when updating conflicting srvs.
2025-06-26 13:43:51 +02:00
lightningterror
722bc94270 GS/DX11: Cache shader resource and sampler.
Might help speed things up, requires srv and rtv conflicts to be resolved.
2025-06-26 13:43:51 +02:00
lightningterror
d51a5db5b1 GS/GL: Add missing texture barrier count. 2025-06-26 09:20:54 +02:00
PCSX2 Bot
04541ae2ab [ci skip] Qt: Update Base Translation. 2025-06-25 07:15:25 +02:00
JordanTheToaster
c58a67815b GameDB: Stuart Little 3 loading screen fix 2025-06-25 00:03:28 +02:00
JordanTheToaster
085f964cd9 CI/Linux: Link ffmpeg for the appimage 2025-06-24 06:18:38 +02:00
Mrlinkwii
a8b6e448eb GameDB: remove reference to old GSC 2025-06-24 06:13:14 +02:00
PCSX2 Bot
78ab8381d9 [ci skip] PAD: Update to latest controller database. 2025-06-24 06:12:26 +02:00
PCSX2 Bot
49a5d82086 [ci skip] Qt: Update Base Translation. 2025-06-24 06:12:04 +02:00
TheLastRar
4ed129ccac FSUI: Improve layout scaling in games list selected preview 2025-06-24 06:11:49 +02:00
lightningterror
ed09dca17e GS/DX11: Also check DrawMultiStretchRects copy for srv conflicts with rtv.
Also adjust the naming.
2025-06-23 15:09:03 +02:00
TheLastRar
e64fbb2f0e FSUI: Centre disc/exe icon in game list selected preview
The ImGui::Image path was missing the centring logic when drawing svg icons.
Covers where already correctly centred.
2025-06-23 15:07:56 +02:00
lightningterror
e37f8a6521 IopBios: Fix -Wcompare warnings. 2025-06-23 10:18:00 +02:00
lightningterror
b54bcc0e20 FullscreenUI: Fix -Wunused-variable warnings. 2025-06-23 10:18:00 +02:00
lightningterror
db37d51bb1 GS: Silence 3rdparty vk_mem_alloc warnings.
-Wunused-private-field
2025-06-23 10:18:00 +02:00
lightningterror
197b9fc560 GS/HW: Check if primid texture exists instead if it's a primid draw.
Makes local testing easier to null out primid tex.
Also match naming between gl and dx11.
Change some longs to warning level.
2025-06-23 09:59:22 +02:00
lightningterror
fd30b00205 GS/DX11: Make sure no SRVs are bound using the same texture before binding it to a RTV.
Fixes api hazard warnings.
2025-06-23 09:59:22 +02:00
TheLastRar
f182379d24 FSUI: Don't attempt to translate input profile names in game properties 2025-06-23 09:53:25 +02:00
refractionpcsx2
32ba08980d GS: Treat Q == 0 in STQ as FLT_MIN 2025-06-23 09:46:03 +02:00
PCSX2 Bot
1e1f08abb9 [ci skip] Qt: Update Base Translation. 2025-06-23 06:28:14 +02:00
refractionpcsx2
cbe20c0eed GS/HW: Fix dirty check on SWPrimRenderer check 2025-06-23 06:27:41 +02:00
refractionpcsx2
ec288ffa62 GS/TC: Fix CanTranslate rect BWs, disallow block inside target lookup 2025-06-23 06:27:41 +02:00
JordanTheToaster
7e18c02c7e GameDB: Xenosaga Episode III fixes 2025-06-23 06:27:13 +02:00
twingofan
4a505cc239 GameDB: Add memcardFilters to Armored Core: Last Raven NTSC-U SLUS-21338. (#12891) 2025-06-23 06:26:16 +02:00
Kuan-Wei Chiu
35c81106c6 Fix invalid comparator in FullscreenUI game list sort
The game list comparator previously used >= when sorting by CRC in
descending order. This violates the requirements of a strict weak
ordering, as defined by the C++ standard, which mandates that the
comparator must be irreflexive: comp(x, x) must always return false.

Using >= causes comp(a, b) and comp(b, a) to both return true when
a == b, which breaks the irreflexivity and can invalidate the
assumptions made by std::sort. This may lead to undefined behavior,
including memory corruption or segmentation faults.

Replace >= with > to ensure the comparator satisfies the strict weak
ordering requirement, restoring correctness and stability in game list
sorting behavior.
2025-06-22 14:44:34 -04:00
Ziemas
1094222d3f Debugger: Don't check frame count in isAlive 2025-06-22 20:19:13 +02:00
TheLastRar
065d0db4c9 FSUI: Replace various icons with SVG files
Co-Authored-By: KamFretoZ <14798312+kamfretoz@users.noreply.github.com>
2025-06-22 20:15:03 +02:00
JordanTheToaster
bc51537080 GS/HW: Purge Hitman Blood Money CRC 2025-06-22 20:14:09 +02:00
JordanTheToaster
b7dfcb282b ImGuiOverlays: Minor OSD text changes 2025-06-22 20:14:09 +02:00
JordanTheToaster
78a9411766 GameDB: Various Fixes Part 5 2025-06-22 20:14:09 +02:00
TellowKrinkle
4724f67596 GS: Min alpha for AA1 is 0, not 128 2025-06-22 20:13:15 +02:00
JordanTheToaster
e0851bb86f [ci skip] Github: Update app bug report OS list 2025-06-21 22:00:10 -04:00
JordanTheToaster
08e68d9563 GitHub: Update bug report OS list 2025-06-21 15:50:02 -04:00
refractionpcsx2
0b6dccae51 GS/HW: Resize validity to draw size when most of target is covered 2025-06-21 05:52:50 +02:00
refractionpcsx2
60adfd5046 GS/TC: Fix region area on new source interpreted for shuffle 2025-06-21 05:52:50 +02:00
chaoticgd
3e782d355d Docs: Add missing third party license for KDBindings 2025-06-20 22:22:51 +02:00
PCSX2 Bot
22a7324b69 [ci skip] Qt: Update Base Translation. 2025-06-20 02:02:47 +02:00
refractionpcsx2
f3b4c50909 GS/HW: Improve Native Scaling detection + Include direct mem reads 2025-06-20 02:02:30 +02:00
lightningterror
a45f27e6e9 GS/DX: Don't output color for datm/stencil date shaders.
RTV(Render target view) is not bound so no need
to output anything, just clip/discard if needed.

Fixes dx11 api warning that pixel shader writes color
output but no rtv is bound.
2025-06-20 02:01:47 +02:00
lightningterror
2f84bf0cca GS/TC: Don't enable Frame buffer conversion on PSMT8.
We have dedicated shader now, no need.
2025-06-20 02:01:28 +02:00
refractionpcsx2
3e2c3e5075 GS/TC: Slight fix for rect translation with block offset 2025-06-20 02:00:51 +02:00
refractionpcsx2
154259d0a6 GS/HW: Don't resize target if only writing to alpha when RGB is valid 2025-06-20 02:00:51 +02:00
95 changed files with 176171 additions and 134461 deletions

View File

@@ -70,6 +70,8 @@ body:
- Windows 11
- Windows 10 (64bit)
- Linux (64bit) - Specify distro below
- macOS 26 (Tahoe)
- macOS 15 (Sequoia)
- macOS 14 (Sonoma)
- macOS 13 (Ventura)
- macOS 12 (Monterey)

View File

@@ -87,6 +87,8 @@ body:
- Windows 11
- Windows 10 (64bit)
- Linux (64bit) - Specify distro below
- macOS 26 (Tahoe)
- macOS 15 (Sequoia)
- macOS 14 (Sonoma)
- macOS 13 (Ventura)
- macOS 12 (Monterey)
@@ -104,14 +106,14 @@ body:
id: cpu
attributes:
label: CPU
placeholder: "Example: i5-7600"
placeholder: "Example: Intel i5 12400F"
validations:
required: true
- type: input
id: gpu
attributes:
label: GPU
placeholder: "Example: GTX 1070"
placeholder: "Example: Nvidia RTX 4060"
validations:
required: true
- type: textarea

View File

@@ -144,6 +144,7 @@ jobs:
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DENABLE_SETCAP=OFF \
-DDISABLE_ADVANCE_SIMD=TRUE \
-DUSE_LINKED_FFMPEG=ON \
-DCMAKE_DISABLE_PRECOMPILE_HEADERS=ON \
$ADDITIONAL_CMAKE_ARGS

View File

@@ -18,7 +18,7 @@ LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.0
LIBPNG=1.6.48
LIBWEBP=1.5.0
SDL=SDL3-3.2.14
SDL=SDL3-3.2.16
QT=6.9.0
LZ4=1.10.0
ZSTD=1.5.7
@@ -39,7 +39,7 @@ fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.
9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
46fd06ff37db1db64c0dc288d78a3f5efd23ad9ac41561193f983e20937ece03 libpng-$LIBPNG.tar.xz
7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c libwebp-$LIBWEBP.tar.gz
b7e7dc05011b88c69170fe18935487b2559276955e49113f8c1b6b72c9b79c1f $SDL.tar.gz
6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8 $SDL.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
c1800c2ea835801af04a05d4a32321d79a93954ee3ae2172bbeacf13d1f0598c qtbase-everywhere-src-$QT.tar.xz

View File

@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.2.14.tar.gz",
"sha256": "b7e7dc05011b88c69170fe18935487b2559276955e49113f8c1b6b72c9b79c1f"
"url": "https://libsdl.org/release/SDL3-3.2.16.tar.gz",
"sha256": "6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8"
}
],
"cleanup": [

View File

@@ -40,7 +40,7 @@ fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
SDL=SDL3-3.2.14
SDL=SDL3-3.2.16
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.48
@@ -79,7 +79,7 @@ CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
b7e7dc05011b88c69170fe18935487b2559276955e49113f8c1b6b72c9b79c1f $SDL.tar.gz
6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8 $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
46fd06ff37db1db64c0dc288d78a3f5efd23ad9ac41561193f983e20937ece03 libpng-$LIBPNG.tar.xz

View File

@@ -22,7 +22,7 @@ fi
FREETYPE=2.13.3
HARFBUZZ=11.2.0
SDL=SDL3-3.2.14
SDL=SDL3-3.2.16
ZSTD=1.5.7
LZ4=1.10.0
LIBPNG=1.6.48
@@ -59,7 +59,7 @@ CMAKE_COMMON=(
cat > SHASUMS <<EOF
0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 freetype-$FREETYPE.tar.xz
16c0204704f3ebeed057aba100fe7db18d71035505cb10e595ea33d346457fc8 harfbuzz-$HARFBUZZ.tar.gz
b7e7dc05011b88c69170fe18935487b2559276955e49113f8c1b6b72c9b79c1f $SDL.tar.gz
6340e58879b2d15830c8460d2f589a385c444d1faa2a4828a9626c7322562be8 $SDL.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
46fd06ff37db1db64c0dc288d78a3f5efd23ad9ac41561193f983e20937ece03 libpng-$LIBPNG.tar.xz

View File

@@ -46,7 +46,7 @@ set FREETYPE=2.13.3
set HARFBUZZ=11.2.0
set LIBJPEGTURBO=3.1.0
set LIBPNG=1648
set SDL=SDL3-3.2.14
set SDL=SDL3-3.2.16
set QT=6.9.0
set QTMINOR=6.9
set LZ4=1.10.0
@@ -68,7 +68,7 @@ call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuz
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1648.zip 2e5f080360f77376eb2bfa9e2ed773b9c7728159aba47b638ad53ca839379040 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 46a17d3ea71fe2580a7f43ca7da286c5b9106dd761e2fd5533bb113e5d86b633 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 0cc7430fb827c1f843e31b8b26ba7f083b1eeb8f6315a65d3744fd4d25b6c373 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 513df15a6365a40f6230ec9463ad8c71b824e181d4b661dac9707e103b24ae0c || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" d428fd17a0d3f92c48a30f1d23806bf20352fbce2e80e5bbee27fa80576480ee || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 54bf06afeb67035f1c6afcd00beec755c0d776626b4cce9ab56992a55215ba69 || goto error

View File

@@ -44,7 +44,7 @@ set FREETYPE=2.13.3
set HARFBUZZ=11.2.0
set LIBJPEGTURBO=3.1.0
set LIBPNG=1648
set SDL=SDL3-3.2.14
set SDL=SDL3-3.2.16
set QT=6.9.0
set QTMINOR=6.9
set LZ4=1.10.0
@@ -66,7 +66,7 @@ call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuz
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1648.zip 2e5f080360f77376eb2bfa9e2ed773b9c7728159aba47b638ad53ca839379040 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 9564c72b1dfd1d6fe6274c5f95a8d989b59854575d4bbee44ade7bc17aa9bc93 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 7d6fab70cf844bf6769077bd5d7a74893f8ffd4dfb42861745750c63c2a5c92c || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 46a17d3ea71fe2580a7f43ca7da286c5b9106dd761e2fd5533bb113e5d86b633 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 0cc7430fb827c1f843e31b8b26ba7f083b1eeb8f6315a65d3744fd4d25b6c373 || goto error
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 513df15a6365a40f6230ec9463ad8c71b824e181d4b661dac9707e103b24ae0c || goto error
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" d428fd17a0d3f92c48a30f1d23806bf20352fbce2e80e5bbee27fa80576480ee || goto error
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 54bf06afeb67035f1c6afcd00beec755c0d776626b4cce9ab56992a55215ba69 || goto error

View File

@@ -24,6 +24,7 @@
<li><a href="#googletest">GoogleTest</a></li>
<li><a href="#harfbuzz">HarfBuzz</a></li>
<li><a href="#jsonformoderncpp">JSON for Modern C++</a></li>
<li><a href="#kdbindings">KDBindings</a></li>
<li><a href="#kddockwidgets">KDDockWidgets</a></li>
<li><a href="#kissfft">kissfft</a></li>
<li><a href="#libbacktrace">libbacktrace</a></li>
@@ -1910,6 +1911,7 @@ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
</pre>
</div>
<!-- Transitive dependency of KDDockWidgets. -->
<div id="jsonformoderncpp">
<h3>JSON for Modern C++ - <a href="https://json.nlohmann.me">https://json.nlohmann.me</a></h3>
<pre>
@@ -1937,6 +1939,34 @@ SOFTWARE.
</pre>
</div>
<!-- Transitive dependency of KDDockWidgets. -->
<div id="kdbindings">
<h3>KDBindings - <a href="https://github.com/KDAB/KDBindings">https://github.com/KDAB/KDBindings</a></h3>
<pre>
MIT License
Copyright 2021 Klarälvdalens Datakonsult AB, a KDAB Group company &lt;info@kdab.com&gt;
Copyright 2021 Jeremy Burns
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the &quot;Software&quot;), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</pre>
</div>
<div id="kddockwidgets">
<h3>KDDockWidgets - <a href="https://github.com/KDAB/KDDockWidgets">https://github.com/KDAB/KDDockWidgets</a></h3>
<pre>

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg
fill="#000000"
width="800px"
height="800px"
viewBox="0 0 32 32"
version="1.1"
id="svg1"
sodipodi:docname="applications-system.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#505050"
inkscape:zoom="0.97875"
inkscape:cx="399.48914"
inkscape:cy="400"
inkscape:window-width="1920"
inkscape:window-height="1010"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<defs
id="defs1" />
<path
d="M30.015 12.97l-2.567-0.569c-0.2-0.64-0.462-1.252-0.762-1.841l1.389-2.313c0.518-0.829 0.78-2.047 0-2.829l-1.415-1.414c-0.78-0.781-2.098-0.64-2.894-0.088l-2.251 1.434c-0.584-0.303-1.195-0.563-1.829-0.768l-0.576-2.598c-0.172-0.953-1.005-1.984-2.11-1.984h-2c-1.104 0-1.781 1.047-2 2l-0.642 2.567c-0.678 0.216-1.328 0.492-1.948 0.819l-2.308-1.47c-0.795-0.552-2.114-0.692-2.894 0.088l-1.415 1.414c-0.781 0.782-0.519 2 0 2.828l1.461 2.435c-0.274 0.552-0.517 1.123-0.705 1.72l-2.566 0.569c-0.953 0.171-1.984 1.005-1.984 2.109v2c0 1.105 1.047 1.782 2 2l2.598 0.649c0.179 0.551 0.404 1.080 0.658 1.593l-1.462 2.438c-0.518 0.828-0.78 2.047 0 2.828l1.415 1.414c0.78 0.782 2.098 0.64 2.894 0.089l2.313-1.474c0.623 0.329 1.277 0.608 1.96 0.823l0.64 2.559c0.219 0.953 0.896 2 2 2h2c1.105 0 1.938-1.032 2.11-1.985l0.577-2.604c0.628-0.203 1.23-0.459 1.808-0.758l2.256 1.438c0.796 0.552 2.114 0.692 2.895-0.089l1.415-1.414c0.78-0.782 0.518-2 0-2.828l-1.39-2.317c0.279-0.549 0.521-1.12 0.716-1.714l2.599-0.649c0.953-0.219 2-0.895 2-2v-2c0-1.104-1.031-1.938-1.985-2.11zM30.001 16.939c-0.085 0.061-0.245 0.145-0.448 0.192l-3.708 0.926-0.344 1.051c-0.155 0.474-0.356 0.954-0.597 1.428l-0.502 0.986 1.959 3.267c0.125 0.2 0.183 0.379 0.201 0.485l-1.316 1.314c-0.127-0.040-0.271-0.092-0.341-0.14l-3.292-2.099-1.023 0.529c-0.493 0.256-0.999 0.468-1.503 0.631l-1.090 0.352-0.824 3.723c-0.038 0.199-0.145 0.36-0.218 0.417h-1.8c-0.061-0.085-0.145-0.245-0.191-0.448l-0.921-3.681-1.066-0.338c-0.549-0.173-1.097-0.404-1.63-0.684l-1.028-0.543-3.293 2.099c-0.135 0.091-0.279 0.143-0.409 0.143l-1.311-1.276c0.018-0.104 0.072-0.274 0.181-0.449l2.045-3.408-0.487-0.98c-0.227-0.462-0.407-0.895-0.547-1.325l-0.343-1.052-3.671-0.918c-0.231-0.052-0.398-0.139-0.485-0.2v-1.86c0.001 0.001 0.002 0.001 0.005 0.001 0.034 0 0.198-0.117 0.335-0.142l3.772-0.835 0.346-1.103c0.141-0.449 0.333-0.917 0.588-1.43l0.487-0.98-2.024-3.373c-0.125-0.201-0.184-0.38-0.201-0.485l1.315-1.314c0.128 0.041 0.271 0.093 0.34 0.14l3.354 2.138 1.027-0.542c0.527-0.278 1.073-0.507 1.622-0.682l1.063-0.338 0.912-3.649c0.053-0.231 0.138-0.398 0.2-0.485h1.859c-0.014 0.020 0.115 0.195 0.142 0.339l0.84 3.794 1.089 0.352c0.511 0.165 1.023 0.38 1.523 0.639l1.023 0.532 3.224-2.053c0.135-0.092 0.279-0.143 0.409-0.143l1.313 1.276c-0.017 0.104-0.072 0.276-0.181 0.45l-1.98 3.296 0.505 0.988c0.273 0.533 0.48 1.033 0.635 1.529l0.346 1.104 3.697 0.82c0.224 0.041 0.398 0.171 0.434 0.241zM16.013 9.99c-3.321 0-6.023 2.697-6.023 6.010s2.702 6.010 6.023 6.010 6.023-2.697 6.023-6.009c0-3.313-2.702-6.010-6.023-6.010zM16 20c-2.205 0-4-1.794-4-4s1.794-4 4-4c2.206 0 4 1.794 4 4s-1.794 4-4 4z"
id="path1"
style="fill:#ffffff" />
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#ffffff" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#ffffff" width="800px" height="800px" viewBox="0 0 16 16" id="window-16px" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#ffffff" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" id="SVGRoot" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" fill="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg height="800px" width="800px" version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve" fill="#ffffff">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#ffffff" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800px" height="800px" viewBox="0 0 72 72" enable-background="new 0 0 72 72" xml:space="preserve">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,9 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg fill="#ffffff" width="800px" height="800px" viewBox="-2 -2 24 24" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin" class="jam jam-microchip">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier">

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,7 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#000000">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -329,6 +329,7 @@
030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,
030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00009601000000000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,

View File

@@ -110,48 +110,24 @@ uint ps_convert_rgba8_16bits(PS_INPUT input) : SV_Target0
return ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
}
PS_OUTPUT ps_datm1(PS_INPUT input)
void ps_datm1(PS_INPUT input)
{
PS_OUTPUT output;
clip(sample_c(input.t).a - 127.5f / 255); // >= 0x80 pass
output.c = 0;
return output;
}
PS_OUTPUT ps_datm0(PS_INPUT input)
void ps_datm0(PS_INPUT input)
{
PS_OUTPUT output;
clip(127.5f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
output.c = 0;
return output;
}
PS_OUTPUT ps_datm1_rta_correction(PS_INPUT input)
void ps_datm1_rta_correction(PS_INPUT input)
{
PS_OUTPUT output;
clip(sample_c(input.t).a - 254.5f / 255); // >= 0x80 pass
output.c = 0;
return output;
}
PS_OUTPUT ps_datm0_rta_correction(PS_INPUT input)
void ps_datm0_rta_correction(PS_INPUT input)
{
PS_OUTPUT output;
clip(254.5f / 255 - sample_c(input.t).a); // < 0x80 pass (== 0x80 should not pass)
output.c = 0;
return output;
}
PS_OUTPUT ps_rta_correction(PS_INPUT input)

View File

@@ -104,7 +104,6 @@ void ps_datm1()
{
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -44,7 +44,7 @@ bool DebugInterface::m_pause_on_entry = false;
bool DebugInterface::isAlive()
{
return VMManager::HasValidVM() && g_FrameCount > 0;
return VMManager::HasValidVM();
}
bool DebugInterface::isCpuPaused()

View File

@@ -575,7 +575,7 @@ void GSState::GIFPackedRegHandlerSTQ(const GIFPackedReg* RESTRICT r)
// Vexx (character shadow)
// q = 0 (st also 0 on the first 16 vertices), setting it to 1.0f to avoid div by zero later
q = q.blend8(GSVector4i::cast(GSVector4::m_one), q == GSVector4i::zero());
q = q.blend8(GSVector4i::cast(GSVector4(FLT_MIN)), q == GSVector4i::zero());
// Suikoden 4
// creates some nan for Q. Let's avoid undefined behavior (See GIFRegHandlerRGBAQ)
@@ -671,7 +671,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u3
GSVector4i q = GSVector4i::loadl(&r[0].U64[1]);
const GSVector4i rgba = (GSVector4i::load<false>(&r[1]) & GSVector4i::x000000ff()).ps32().pu16();
q = q.blend8(GSVector4i::cast(GSVector4::m_one), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
q = q.blend8(GSVector4i::cast(GSVector4(FLT_MIN)), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
m_v.m[0] = st.upl64(rgba.upl32(q)); // TODO: only store the last one
@@ -705,7 +705,7 @@ void GSState::GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32
GSVector4i q = GSVector4i::loadl(&r[0].U64[1]);
const GSVector4i rgba = (GSVector4i::load<false>(&r[1]) & GSVector4i::x000000ff()).ps32().pu16();
q = q.blend8(GSVector4i::cast(GSVector4::m_one), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
q = q.blend8(GSVector4i::cast(GSVector4(FLT_MIN)), q == GSVector4i::zero()); // see GIFPackedRegHandlerSTQ
m_v.m[0] = st.upl64(rgba.upl32(q)); // TODO: only store the last one
@@ -4237,7 +4237,10 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
if (IsCoverageAlpha())
{
min = 128;
// HW renderer doesn't currently support AA, so its min is 128.
// If we add AA support to the HW renderer, this will need to be changed.
// (Will probably only be supported with ROV/FBFetch so we would want to check for that.)
min = GSIsHardwareRenderer() ? 128 : 0;
max = 128;
}
else

View File

@@ -551,9 +551,18 @@ void GSDevice11::Destroy()
m_rs.reset();
if (m_state.rt_view)
{
m_state.rt_view->Release();
m_state.rt_view = nullptr;
}
m_state.cached_rt_view = nullptr;
if (m_state.dsv)
{
m_state.dsv->Release();
m_state.dsv = nullptr;
}
m_state.cached_dsv = nullptr;
m_shader_cache.Close();
@@ -950,11 +959,13 @@ GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip)
m_state.rt_view->Release();
m_state.rt_view = m_swap_chain_rtv.get();
m_state.rt_view->AddRef();
m_state.cached_rt_view = nullptr;
if (m_state.dsv)
{
m_state.dsv->Release();
m_state.dsv = nullptr;
}
m_state.cached_dsv = nullptr;
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
@@ -1105,14 +1116,14 @@ float GSDevice11::GetAndResetAccumulatedGPUTime()
void GSDevice11::DrawPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
PSUpdateShaderState();
PSUpdateShaderState(true, true);
m_ctx->Draw(m_vertex.count, m_vertex.start);
}
void GSDevice11::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
PSUpdateShaderState();
PSUpdateShaderState(true, true);
m_ctx->DrawIndexed(m_index.count, m_index.start, m_vertex.start);
}
@@ -1120,7 +1131,7 @@ void GSDevice11::DrawIndexedPrimitive(int offset, int count)
{
pxAssert(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
PSUpdateShaderState();
PSUpdateShaderState(true, true);
m_ctx->DrawIndexed(count, m_index.start + offset, m_vertex.start);
}
@@ -1280,6 +1291,9 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
GSVector2i ds;
if (dTex)
{
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
ds = dTex->GetSize();
if (draw_in_depth)
OMSetRenderTargets(nullptr, dTex);
@@ -1292,6 +1306,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
}
// om
if (draw_in_depth)
OMSetDepthStencilState(m_convert.dss_write.get(), 0);
else
@@ -1329,7 +1344,7 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
PSSetSamplerState(linear ? m_convert.ln.get() : m_convert.pt.get());
PSSetShader(ps, ps_cb);
//
// draw
DrawPrimitive();
}
@@ -1341,6 +1356,9 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
GSVector2i ds;
if (dTex)
{
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
ds = dTex->GetSize();
OMSetRenderTargets(dTex, nullptr);
}
@@ -1356,6 +1374,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
m_ctx->UpdateSubresource(m_present.ps_cb.get(), 0, nullptr, &cb, 0, 0);
// om
OMSetDepthStencilState(m_convert.dss.get(), 0);
OMSetBlendState(m_convert.bs[D3D11_COLOR_WRITE_ENABLE_ALL].get(), 0);
@@ -1388,7 +1407,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
PSSetSamplerState(linear ? m_convert.ln.get() : m_convert.pt.get());
PSSetShader(m_present.ps[static_cast<u32>(shader)].get(), m_present.ps_cb.get());
//
// draw
DrawPrimitive();
}
@@ -1454,6 +1473,7 @@ void GSDevice11::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_re
VSSetShader(m_convert.vs.get(), nullptr);
PSSetShader(m_convert.ps[static_cast<int>(shader)].get(), nullptr);
PSUnbindConflictingSRVs(dTex);
OMSetDepthStencilState(dTex->IsRenderTarget() ? m_convert.dss.get() : m_convert.dss_write.get(), 0);
OMSetRenderTargets(dTex->IsRenderTarget() ? dTex : nullptr, dTex->IsDepthStencil() ? dTex : nullptr);
@@ -2110,7 +2130,7 @@ void GSDevice11::RenderImGui()
// Since we don't have the GSTexture...
m_state.ps_sr_views[0] = reinterpret_cast<ID3D11ShaderResourceView*>(pcmd->GetTexID());
PSUpdateShaderState();
PSUpdateShaderState(true, true);
m_ctx->DrawIndexed(pcmd->ElemCount, m_index.start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset);
}
@@ -2130,6 +2150,10 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(ds), D3D11_CLEAR_STENCIL, 0.0f, 0);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(ds);
// om
OMSetDepthStencilState(m_date.dss.get(), 1);
@@ -2147,11 +2171,12 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
VSSetShader(m_convert.vs.get(), nullptr);
// ps
PSSetShaderResource(0, rt);
PSSetSamplerState(m_convert.pt.get());
PSSetShader(m_convert.ps[SetDATMShader(datm)].get(), nullptr);
//
// draw
DrawPrimitive();
}
@@ -2357,15 +2382,68 @@ void GSDevice11::PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb)
}
}
void GSDevice11::PSUpdateShaderState()
void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
{
m_ctx->PSSetShaderResources(0, m_state.ps_sr_views.size(), m_state.ps_sr_views.data());
m_ctx->PSSetSamplers(0, m_state.ps_ss.size(), m_state.ps_ss.data());
// Shader resource caching requires srv/rtv hazards to be resolved, ensure PSUnbindConflictingSRVs handle.
if (sr_update)
{
bool sr_changed = false;
for (size_t i = 0; i < m_state.ps_sr_views.size(); ++i)
{
if (m_state.ps_cached_sr_views[i] != m_state.ps_sr_views[i])
{
sr_changed = true;
break;
}
}
if (sr_changed)
{
m_state.ps_cached_sr_views = m_state.ps_sr_views;
m_ctx->PSSetShaderResources(0, m_state.ps_sr_views.size(), m_state.ps_sr_views.data());
}
}
if (ss_update)
{
bool ss_changed = false;
for (size_t i = 0; i < m_state.ps_ss.size(); ++i)
{
if (m_state.ps_cached_ss[i] != m_state.ps_ss[i])
{
ss_changed = true;
break;
}
}
if (ss_changed)
{
m_state.ps_cached_ss = m_state.ps_ss;
m_ctx->PSSetSamplers(0, m_state.ps_ss.size(), m_state.ps_ss.data());
}
}
}
void GSDevice11::PSUnbindConflictingSRVs(GSTexture* tex1, GSTexture* tex2)
{
// Make sure no SRVs are bound using the same texture before binding it to a RTV.
bool changed = false;
for (size_t i = 0; i < m_state.ps_sr_views.size(); i++)
{
if ((tex1 && m_state.ps_sr_views[i] == *(GSTexture11*)tex1) || (tex2 && m_state.ps_sr_views[i] == *(GSTexture11*)tex2))
{
m_state.ps_sr_views[i] = nullptr;
changed = true;
}
}
if (changed)
PSUpdateShaderState(true, false);
}
void GSDevice11::OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref)
{
if (m_state.dss != dss || m_state.sref != sref)
if (m_state.dss != dss || (dss && m_state.sref != sref))
{
m_state.dss = dss;
m_state.sref = sref;
@@ -2376,7 +2454,7 @@ void GSDevice11::OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref)
void GSDevice11::OMSetBlendState(ID3D11BlendState* bs, u8 bf)
{
if (m_state.bs != bs || m_state.bf != bf)
if (m_state.bs != bs || (bs && m_state.bf != bf))
{
m_state.bs = bs;
m_state.bf = bf;
@@ -2413,6 +2491,7 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
if (rtv)
rtv->AddRef();
m_state.rt_view = rtv;
m_state.cached_rt_view = rt;
}
if (m_state.dsv != dsv)
{
@@ -2421,6 +2500,7 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
if (dsv)
dsv->AddRef();
m_state.dsv = dsv;
m_state.cached_dsv = ds;
}
if (changed)
m_ctx->OMSetRenderTargets(1, &rtv, dsv);
@@ -2523,18 +2603,18 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
}
GSTexture* primid_tex = nullptr;
GSTexture* primid_texture = nullptr;
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{
primid_tex = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
if (!primid_tex)
primid_texture = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
if (!primid_texture)
{
Console.WriteLn("D3D11: Failed to allocate DATE image, aborting draw.");
Console.Warning("D3D11: Failed to allocate DATE image, aborting draw.");
return;
}
StretchRect(colclip_rt ? colclip_rt : config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
primid_texture, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
}
else if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne)
@@ -2596,6 +2676,9 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
IASetPrimitiveTopology(topology);
// Should be called before changing local srv state.
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, config.ds);
if (config.tex)
{
CommitClear(config.tex);
@@ -2645,24 +2728,44 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
if (primid_texture)
{
OMDepthStencilSelector dss = config.depth;
dss.zwe = 0;
const OMBlendSelector blend(GSHWDrawConfig::ColorMaskSelector(1),
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_tex, config.ds, &config.scissor);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor);
DrawIndexedPrimitive();
config.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
SetupPS(config.ps, nullptr, config.sampler);
PSSetShaderResource(3, primid_tex);
}
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds = config.ds;
// Make sure no tex is bound as both rtv and srv at the same time.
// All conflicts should've been taken care of by PSUnbindConflictingSRVs.
// It is fine to do the optimiation when on slot 0 tex is fb, tex is ds, and slot 2 sw blend as they are copies bound to srv.
if (!draw_rt && draw_ds && m_state.rt_view && m_state.cached_rt_view && m_state.rt_view == *(GSTexture11*)m_state.cached_rt_view &&
m_state.cached_dsv == draw_ds && config.tex != m_state.cached_rt_view && m_state.cached_rt_view->GetSize() == draw_ds->GetSize())
{
draw_rt = m_state.cached_rt_view;
}
else if (!draw_ds && draw_rt && m_state.dsv && m_state.cached_dsv && m_state.dsv == *(GSTexture11*)m_state.cached_dsv &&
m_state.cached_rt_view == draw_rt && config.tex != m_state.cached_dsv && m_state.cached_dsv->GetSize() == draw_rt->GetSize())
{
draw_ds = m_state.cached_dsv;
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor);
if (primid_texture)
PSSetShaderResource(3, primid_texture);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
OMSetRenderTargets(colclip_rt ? colclip_rt : config.rt, config.ds, &config.scissor);
DrawIndexedPrimitive();
if (config.blend_multi_pass.enable)
@@ -2697,8 +2800,8 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (draw_ds_clone)
Recycle(draw_ds_clone);
if (primid_tex)
Recycle(primid_tex);
if (primid_texture)
Recycle(primid_texture);
if (colclip_rt)
{

View File

@@ -145,9 +145,11 @@ private:
ID3D11VertexShader* vs;
ID3D11Buffer* vs_cb;
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_sr_views;
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_cached_sr_views;
ID3D11PixelShader* ps;
ID3D11Buffer* ps_cb;
std::array<ID3D11SamplerState*, MAX_SAMPLERS> ps_ss;
std::array<ID3D11SamplerState*, MAX_SAMPLERS> ps_cached_ss;
GSVector2i viewport;
GSVector4i scissor;
u32 vb_stride;
@@ -157,6 +159,8 @@ private:
u8 bf;
ID3D11RenderTargetView* rt_view;
ID3D11DepthStencilView* dsv;
GSTexture* cached_rt_view;
GSTexture* cached_dsv;
} m_state;
std::array<std::array<wil::com_ptr_nothrow<ID3D11Query>, 3>, NUM_TIMESTAMP_QUERIES> m_timestamp_queries = {};
@@ -324,7 +328,8 @@ public:
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb);
void PSUpdateShaderState();
void PSUpdateShaderState(const bool sr_update, const bool ss_update);
void PSUnbindConflictingSRVs(GSTexture* tex1 = nullptr, GSTexture* tex2 = nullptr);
void PSSetSamplerState(ID3D11SamplerState* ss0);
void OMSetDepthStencilState(ID3D11DepthStencilState* dss, u8 sref);

View File

@@ -3903,7 +3903,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
config.rt = backup_rt;
if (!date_image)
{
Console.WriteLn("D3D12: Failed to allocate DATE image, aborting draw.");
Console.Warning("D3D12: Failed to allocate DATE image, aborting draw.");
return;
}
}

View File

@@ -822,23 +822,6 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
return true;
}
bool GSHwHack::GSC_HitmanBloodMoney(GSRendererHW& r, int& skip)
{
// The game does a stupid thing where it backs up the last 2 pages of the framebuffer with shuffles, uploads a CT32 texture to it
// then copies the RGB back (keeping the new alpha only). It's pretty gross, I have no idea why they didn't just upload a new alpha.
// This is a real pain to emulate with the current state of things, so let's just clear the dirty area from the upload and pretend it wasn't there.
// Catch the first draw of the copy back.
if (RFBP > 0 && RTPSM == PSMT8H && RFPSM == PSMCT32)
{
GSTextureCache::Target* target = g_texture_cache->FindOverlappingTarget(RFBP, RFBP + 1);
if (target)
target->m_dirty.clear();
}
return false;
}
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const u32 n_vertices = r.m_vertex.next;
@@ -1328,7 +1311,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
CRC_F(GSC_NFSUndercover),
CRC_F(GSC_PolyphonyDigitalGames),
CRC_F(GSC_MetalGearSolid3),
CRC_F(GSC_HitmanBloodMoney),
CRC_F(GSC_Battlefield2),
// Channel Effect

View File

@@ -26,7 +26,6 @@ public:
static bool GSC_Battlefield2(GSRendererHW& r, int& skip);
static bool GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip);
static bool GSC_MetalGearSolid3(GSRendererHW& r, int& skip);
static bool GSC_HitmanBloodMoney(GSRendererHW& r, int& skip);
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);

View File

@@ -2230,6 +2230,7 @@ void GSRendererHW::Draw()
// We mess with this state as an optimization, so take a copy and use that instead.
const GSDrawingContext* context = m_context;
m_cached_ctx.TEX0 = context->TEX0;
m_cached_ctx.TEXA = m_draw_env->TEXA;
m_cached_ctx.CLAMP = context->CLAMP;
m_cached_ctx.TEST = context->TEST;
m_cached_ctx.FRAME = context->FRAME;
@@ -2359,11 +2360,10 @@ void GSRendererHW::Draw()
const u32 fm_mask = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk;
// Note required to compute TryAlphaTest below. So do it now.
const GSDrawingEnvironment& env = *m_draw_env;
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
if (PRIM->TME && tex_psm.pal > 0)
{
m_mem.m_clut.Read32(m_cached_ctx.TEX0, env.TEXA);
m_mem.m_clut.Read32(m_cached_ctx.TEX0, m_cached_ctx.TEXA);
if (m_mem.m_clut.GetGPUTexture())
{
CalcAlphaMinMax(0, 255);
@@ -2672,6 +2672,48 @@ void GSRendererHW::Draw()
GSVector2i(1, 1));
height_invalid = false;
}
const u32 vert_index = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 2 : 1;
u32 const_color = m_vertex.buff[m_index.buff[vert_index]].RGBAQ.U32[0];
u32 fb_mask = m_cached_ctx.FRAME.FBMSK;
// If we could just check the colour, it would be great, but Echo Night decided it's going to set the alpha and green to 128, for some reason, and actually be 32bit, so it ruined my day.
GSTextureCache::Target* rt_tgt = g_texture_cache->GetExactTarget(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, GSTextureCache::RenderTarget, m_cached_ctx.FRAME.Block() + 1);
const bool clear_16bit_likely = !(context->FRAME.PSM & 0x2) && ((rt_tgt && (rt_tgt->m_TEX0.PSM & 2)) || (!rt_tgt && ((static_cast<int>(context->FRAME.FBW) * 64) <= (PCRTCDisplays.GetResolution().x >> 1) || m_r.height() <= (PCRTCDisplays.GetResolution().y >> 1))));
rt_tgt = nullptr;
if (clear_16bit_likely && ((const_color != 0 && (const_color >> 16) == (const_color & 0xFFFF) && ((const_color >> 8) & 0xFF) != (const_color & 0xFF)) ||
(fb_mask != 0 && (fb_mask >> 16) == (fb_mask & 0xFFFF) && ((fb_mask >> 8) & 0xFF) != (fb_mask & 0xFF))))
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
GL_CACHE("Clear 16bit with 32bit %d", s_n);
// May have already been resized through the split draw checks.
if (!(m_cached_ctx.FRAME.PSM & 2))
{
if (next_ctx.FRAME.FBW == (m_cached_ctx.FRAME.FBW * 2))
{
m_cached_ctx.FRAME.FBW *= 2;
m_r.z *= 2;
}
else
{
m_r.w *= 2;
}
}
// Convert colour and masks to 16bit and set a custom TEXA for this draw.
const_color = ((const_color & 0x1F) << 3) | ((const_color & 0x3E0) << 6) | ((const_color & 0x7C00) << 9) | ((const_color & 0x8000) << 16);
m_cached_ctx.FRAME.FBMSK = ((fb_mask & 0x1F) << 3) | ((fb_mask & 0x3E0) << 6) | ((fb_mask & 0x7C00) << 9) | ((fb_mask & 0x8000) << 16);
m_cached_ctx.TEXA.AEM = 0;
m_cached_ctx.TEXA.TA0 = 0;
m_cached_ctx.TEXA.TA1 = 128;
m_cached_ctx.FRAME.PSM = (m_cached_ctx.FRAME.PSM & 2) ? m_cached_ctx.FRAME.PSM : PSMCT16;
m_vertex.buff[m_index.buff[1]].RGBAQ.U32[0] = const_color;
ReplaceVerticesWithSprite(m_r, GSVector2i(m_r.width(), m_r.height()));
}
// Be careful of being 1 pixel from filled.
const bool page_aligned = (m_r.w % pgs.y) == (pgs.y - 1) || (m_r.w % pgs.y) == 0;
@@ -2936,7 +2978,7 @@ void GSRendererHW::Draw()
}
possible_shuffle = !no_rt && (((shuffle_target /*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || IsPossibleChannelShuffle());
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_cached_ctx.TEXA.AEM;
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
@@ -2953,8 +2995,8 @@ void GSRendererHW::Draw()
}
else
{
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(true, TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME, req_color, req_alpha) :
g_texture_cache->LookupSource(true, TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr,
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(true, TEX0, m_cached_ctx.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME, req_color, req_alpha) :
g_texture_cache->LookupSource(true, TEX0, m_cached_ctx.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr,
possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME, req_color, req_alpha);
if (!src) [[unlikely]]
@@ -3045,19 +3087,19 @@ void GSRendererHW::Draw()
if (scale_draw == 1)
{
target_scale = 1.0f;
m_downscale_source = src->m_from_target->GetScale() > 1.0f;
m_downscale_source = src->m_from_target ? src->m_from_target->GetScale() > 1.0f : false;
}
else
m_downscale_source = GSConfig.UserHacks_NativeScaling != GSNativeScaling::Aggressive ? false : src->m_from_target->GetScale() > 1.0f; // Bad for GTA + Full Spectrum Warrior, good for Sacred Blaze + Parappa.
m_downscale_source = (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Aggressive || !src->m_from_target) ? false : src->m_from_target->GetScale() > 1.0f; // Bad for GTA + Full Spectrum Warrior, good for Sacred Blaze + Parappa.
}
else
{
// if it's directly copying keep the scale - Ratchet and clank hits this, stops edge garbage happening.
// Keep it to small targets of 256 or lower.
if (scale_draw == -1 && src && src->m_from_target && src->m_from_target->m_downscaled && ((static_cast<int>(m_cached_ctx.FRAME.FBW * 64) <= (PCRTCDisplays.GetResolution().x >> 1) &&
if (scale_draw == -1 && src && (!src->m_from_target || (src->m_from_target && src->m_from_target->m_downscaled)) && ((static_cast<int>(m_cached_ctx.FRAME.FBW * 64) <= (PCRTCDisplays.GetResolution().x >> 1) &&
(GSVector4i(m_vt.m_min.p).xyxy() == GSVector4i(m_vt.m_min.t).xyxy()).alltrue() && (GSVector4i(m_vt.m_max.p).xyxy() == GSVector4i(m_vt.m_max.t).xyxy()).alltrue()) || possible_shuffle))
{
target_scale = src->m_from_target->GetScale();
target_scale = src->m_from_target ? src->m_from_target->GetScale() : 1.0f;
scale_draw = 1;
scaled_copy = true;
}
@@ -3073,7 +3115,7 @@ void GSRendererHW::Draw()
// This upscaling hack is for games which construct P8 textures by drawing a bunch of small sprites in C32,
// then reinterpreting it as P8. We need to keep the off-screen intermediate textures at native resolution,
// but not propagate that through to the normal render targets. Test Case: Crash Wrath of Cortex.
if (no_ds && src && !m_channel_shuffle && src->m_from_target && (GSConfig.UserHacks_NativePaletteDraw || (src->m_from_target->m_downscaled && scale_draw <= 1)) &&
if (no_ds && src && !m_channel_shuffle && src->m_from_target && (GSConfig.UserHacks_NativePaletteDraw || (src->m_target_direct && src->m_from_target->m_downscaled && scale_draw <= 1)) &&
src->m_scale == 1.0f && (src->m_TEX0.PSM == PSMT8 || src->m_TEX0.TBP0 == m_cached_ctx.FRAME.Block()))
{
GL_CACHE("HW: Using native resolution for target based on texture source");
@@ -3405,7 +3447,7 @@ void GSRendererHW::Draw()
return;
}
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src, possible_shuffle), (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? src->m_from_target->GetScale() : target_scale,
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src, possible_shuffle), (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? ((src && src->m_from_target) ? src->m_from_target->GetScale() : (ds ? ds->m_scale : 1.0f)) : target_scale,
GSTextureCache::RenderTarget, true, fm, false, force_preload, preserve_rt_color || possible_shuffle, lookup_rect, src);
if (!rt) [[unlikely]]
@@ -3603,17 +3645,18 @@ void GSRendererHW::Draw()
if (ds && rt && (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) != (m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0))
{
m_using_temp_z = true;
const int page_offset = (static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32);
const int z_vertical_offset = (page_offset / std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int z_horizontal_offset = (page_offset % std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
const int page_offset = static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32;
const int rt_page_offset = static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32;
const int z_vertical_offset = (page_offset / std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int z_horizontal_offset = (page_offset % std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
if (g_texture_cache->GetTemporaryZ() != nullptr)
{
GSTextureCache::TempZAddress z_address_info = g_texture_cache->GetTemporaryZInfo();
const int old_z_vertical_offset = (page_offset / std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int old_z_horizontal_offset = (page_offset % std::max(ds->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
const int old_z_vertical_offset = (z_address_info.offset / std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int old_z_horizontal_offset = (z_address_info.offset % std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
if (ds->m_TEX0.TBP0 != z_address_info.ZBP || z_address_info.offset != page_offset)
if (ds->m_TEX0.TBP0 != z_address_info.ZBP || z_address_info.offset != page_offset || z_address_info.rt_offset != rt_page_offset)
g_texture_cache->InvalidateTemporaryZ();
else if (!m_r.rintersect(z_address_info.rect_since).rempty() && m_cached_ctx.TEST.ZTST > ZTST_ALWAYS)
{
@@ -3633,10 +3676,11 @@ void GSRendererHW::Draw()
if (g_texture_cache->GetTemporaryZ() == nullptr)
{
ds->Update(); // We need to update any dirty bits of Z before the copy
m_temp_z_full_copy = false;
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
const int rt_page_offset = (static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32);
const int vertical_page_offset = (rt_page_offset / std::max(static_cast<int>(rt->m_TEX0.TBW), 1));
const int vertical_offset = vertical_page_offset * frame_psm.pgs.y;
const int horizontal_offset = (rt_page_offset - (vertical_page_offset * std::max(static_cast<int>(rt->m_TEX0.TBW), 1))) * frame_psm.pgs.x;
@@ -3644,36 +3688,19 @@ void GSRendererHW::Draw()
const u32 horizontal_size = std::max(rt->m_unscaled_size.x, ds->m_unscaled_size.x);
const u32 vertical_size = std::max(rt->m_unscaled_size.y, ds->m_unscaled_size.y);
GSVector4i dRect = GSVector4i(horizontal_offset * ds->m_scale, vertical_offset * ds->m_scale, ds->m_unscaled_size.x * ds->m_scale, ds->m_unscaled_size.y * ds->m_scale);
GSVector4i dRect = GSVector4i(horizontal_offset * ds->m_scale, vertical_offset * ds->m_scale, (horizontal_offset + (ds->m_unscaled_size.x - z_horizontal_offset)) * ds->m_scale, (vertical_offset + (ds->m_unscaled_size.y - z_vertical_offset)) * ds->m_scale);
const int new_height = std::max(static_cast<int>(vertical_size * ds->m_scale), dRect.w);
const int new_width = std::max(static_cast<int>(horizontal_size * ds->m_scale), dRect.z);
const int height_diff = new_height - (ds->m_unscaled_size.y * ds->m_scale);
const int width_diff = new_width - (ds->m_unscaled_size.x * ds->m_scale);
// Size here should match whichever is biggest, since that's probably what's going to happen with it further down.
const int new_height = std::min(2048, std::max(t_size.y, static_cast<int>(vertical_size))) * ds->m_scale;
const int new_width = std::min(2048, std::max(t_size.x, static_cast<int>(horizontal_size))) * ds->m_scale;
if (GSTexture* tex = g_gs_device->CreateDepthStencil(new_width, new_height, GSTexture::Format::DepthStencil, true))
{
GSVector4 sRect = GSVector4(static_cast<float>(z_horizontal_offset) / static_cast<float>(ds->m_unscaled_size.x), static_cast<float>(z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y), 1.0f - (static_cast<float>(horizontal_offset - z_horizontal_offset) / static_cast<float>(ds->m_unscaled_size.x)), 1.0f - (static_cast<float>(vertical_offset - z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y)));
GSVector4 sRect = GSVector4(static_cast<float>(z_horizontal_offset) / static_cast<float>(ds->m_unscaled_size.x), static_cast<float>(z_vertical_offset) / static_cast<float>(ds->m_unscaled_size.y), 1.0f , 1.0f);
const bool restricted_copy = !(((next_ctx.ZBUF.ZBP == m_context->ZBUF.ZBP && next_ctx.FRAME.FBP == m_context->FRAME.FBP)) && !(IsPossibleChannelShuffle() && !IsPageCopy()));
if (!restricted_copy)
{
if (height_diff)
{
const int adjust = std::min(height_diff, (vertical_offset - z_vertical_offset));
sRect.w += static_cast<float>(adjust) / static_cast<float>(ds->m_unscaled_size.y);
dRect.w += adjust;
}
if (width_diff)
{
const int adjust = std::min(width_diff, (horizontal_offset - z_horizontal_offset));
sRect.z += static_cast<float>(adjust) / static_cast<float>(ds->m_unscaled_size.x);
dRect.z += adjust;
}
}
else
if (restricted_copy)
{
// m_r already has horizontal_offset (rt offset) applied)
dRect = GSVector4i(m_r.x * ds->m_scale, m_r.y * ds->m_scale, ((1 + m_r.z) * ds->m_scale), ((1 + m_r.w) * ds->m_scale));
@@ -3686,10 +3713,13 @@ void GSRendererHW::Draw()
GL_CACHE("HW: RT in RT Z copy on draw %d z_vert_offset %d", s_n, page_offset);
g_gs_device->StretchRect(ds->m_texture, sRect, tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
if (m_cached_ctx.TEST.ZTST > ZTST_ALWAYS || !dRect.rintersect(GSVector4i(GSVector4(m_r) * ds->m_scale)).eq(dRect))
{
g_gs_device->StretchRect(ds->m_texture, sRect, tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
g_texture_cache->SetTemporaryZ(tex);
g_texture_cache->SetTemporaryZInfo(ds->m_TEX0.TBP0, page_offset);
g_texture_cache->SetTemporaryZInfo(ds->m_TEX0.TBP0, page_offset, rt_page_offset);
t_size.y = std::max(static_cast<int>(new_height / ds->m_scale), t_size.y);
}
else
@@ -4039,7 +4069,8 @@ void GSRendererHW::Draw()
bool valid_width_change = false;
if (rt && ((!is_possible_mem_clear || blending_cd) || rt->m_TEX0.PSM != FRAME_TEX0.PSM) && !m_in_target_draw)
{
valid_width_change = rt->m_TEX0.TBW != FRAME_TEX0.TBW;
const u32 frame_mask = (m_cached_ctx.FRAME.FBMSK & frame_psm.fmsk);
valid_width_change = rt->m_TEX0.TBW != FRAME_TEX0.TBW && (frame_mask != (frame_psm.fmsk & 0x00FFFFFF) || rt->m_valid_rgb == false);
if (valid_width_change && !m_cached_ctx.ZBUF.ZMSK && (m_cached_ctx.FRAME.FBMSK & 0xFF000000))
{
// Alpha could be a font, and since the width is changing it's no longer valid.
@@ -4178,6 +4209,10 @@ void GSRendererHW::Draw()
const int new_w = std::min(2048, std::max(new_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds_size.x : 0)));
const int new_h = std::min(2048, std::max(new_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds_size.y : 0)));
const bool full_cover_clear = is_possible_mem_clear && GSLocalMemory::IsPageAligned(m_cached_ctx.FRAME.PSM, m_r) && m_r.x == 0 && m_r.y == 0 && !preserve_rt_rgb &&
!IsPageCopy() && m_r.width() == (m_cached_ctx.FRAME.FBW * 64);
if (rt)
{
const u32 old_end_block = rt->m_end_block;
@@ -4224,9 +4259,9 @@ void GSRendererHW::Draw()
g_texture_cache->AddDirtyRectTarget(rt, GSVector4i(rt->m_valid.x, rt->m_valid.w, rt->m_valid.z, new_h), rt->m_TEX0.PSM, rt->m_TEX0.TBW, mask, false);
g_texture_cache->GetTargetSize(rt->m_TEX0.TBP0, rt->m_TEX0.TBW, rt->m_TEX0.PSM, 0, new_h);
}
rt->ResizeValidity(rt->m_valid.rintersect(rt->GetUnscaledRect()));
rt->ResizeDrawn(rt->m_drawn_since_read.rintersect(rt->GetUnscaledRect()));
const bool rt_cover = full_cover_clear && (m_r.height() + frame_psm.pgs.y) >= rt->m_valid.height();
rt->ResizeValidity(rt_cover ? update_rect : rt->m_valid.rintersect(rt->GetUnscaledRect()));
rt->ResizeDrawn(rt_cover ? update_rect : rt->m_drawn_since_read.rintersect(rt->GetUnscaledRect()));
}
const bool rt_update = can_update_size || (m_texture_shuffle && (src && rt && src->m_from_target != rt));
@@ -4234,7 +4269,7 @@ void GSRendererHW::Draw()
// If it's updating from a texture shuffle, limit the size to the source size.
if (rt_update && !can_update_size)
{
if(src->m_from_target)
if (src->m_from_target)
update_rect = update_rect.rintersect(src->m_from_target->m_valid);
update_rect = update_rect.rintersect(GSVector4i::loadh(GSVector2i(new_w, new_h)));
@@ -4304,16 +4339,18 @@ void GSRendererHW::Draw()
DevCon.Warning("HW: Temporary depth buffer creation failed.");
}
}
const bool z_masked = m_cached_ctx.ZBUF.ZMSK;
if (!m_texture_shuffle && !m_channel_shuffle)
{
ds->ResizeValidity(ds->GetUnscaledRect());
ds->ResizeDrawn(ds->GetUnscaledRect());
const bool z_cover = full_cover_clear && (m_r.height() + GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y) >= ds->m_valid.height();
ds->ResizeValidity(z_cover ? m_r : ds->GetUnscaledRect());
ds->ResizeDrawn(z_cover ? m_r : ds->GetUnscaledRect());
}
// Limit to 2x the vertical height of the resolution (for double buffering)
// Dark cloud writes to 424 when the buffer is only 416 high, but masks the Z.
// Updating the valid causes the Z to overlap the framebuffer, which is obviously incorrect.
const bool z_masked = m_cached_ctx.ZBUF.ZMSK;
const bool z_update = can_update_size && !z_masked;
if (rt && m_using_temp_z)
@@ -4321,7 +4358,10 @@ void GSRendererHW::Draw()
const GSLocalMemory::psm_t& z_psm = GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM];
const int vertical_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y;
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(ds->m_TEX0.TBW), 1)) * z_psm.pgs.y;
const GSVector4i ds_rect = m_r - GSVector4i(vertical_offset - z_vertical_offset);
const int z_horizontal_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) % std::max(rt->m_TEX0.TBW, 1U)) * z_psm.pgs.x;
const int horizontal_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) % std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.x;
const GSVector4i ds_rect = m_r - GSVector4i(horizontal_offset - z_horizontal_offset, vertical_offset - z_vertical_offset).xyxy();
ds->UpdateValidity(ds_rect, z_update && (can_update_size || (ds_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
ds->UpdateDrawn(ds_rect, z_update && (can_update_size || (ds_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
}
@@ -4566,14 +4606,14 @@ void GSRendererHW::Draw()
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int z_horizontal_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) % std::max(rt->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
const int z_vertical_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.y;
const int z_horizontal_offset = ((static_cast<int>(m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0) / 32) % std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].pgs.x;
const int vertical_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) / std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.y;
const int horizontal_offset = ((static_cast<int>(m_cached_ctx.FRAME.Block() - rt->m_TEX0.TBP0) / 32) % std::max(static_cast<int>(rt->m_TEX0.TBW), 1)) * frame_psm.pgs.x;
if (was_written)
{
const GSVector4i ds_real_rect = real_rect - GSVector4i(vertical_offset - z_vertical_offset);
const GSVector4i ds_real_rect = real_rect - GSVector4i(horizontal_offset - z_horizontal_offset, vertical_offset - z_vertical_offset).xyxy();
ds->UpdateValidity(ds_real_rect, !z_masked && (can_update_size || (ds_real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
}
@@ -6337,7 +6377,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
// 2/ Only keep aem when it is useful (avoid useless shader permutation)
if (m_conf.ps.shuffle)
{
const GIFRegTEXA& TEXA = m_draw_env->TEXA;
const GIFRegTEXA& TEXA = m_cached_ctx.TEXA;
// Force a 32 bits access (normally shuffle is done on 16 bits)
// m_ps_sel.tex_fmt = 0; // removed as an optimization
@@ -6391,7 +6431,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
}
else if (tex->m_target)
{
const GIFRegTEXA& TEXA = m_draw_env->TEXA;
const GIFRegTEXA& TEXA = m_cached_ctx.TEXA;
// Use an old target. AEM and index aren't resolved it must be done
// on the GPU
@@ -7228,13 +7268,13 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
const GSVector4i shuffle_rect = GSVector4i(m_vt.m_min.p.x, m_vt.m_min.p.y, m_vt.m_max.p.x, m_vt.m_max.p.y);
if (!rt->m_valid.rintersect(shuffle_rect).eq(rt->m_valid) || (m_cached_ctx.FRAME.FBMSK & 0xFFFC0000))
{
rt_new_alpha_max = std::max(static_cast<int>((std::max(m_draw_env->TEXA.TA1, m_draw_env->TEXA.TA0) & 0x80) + 127), rt_new_alpha_max) | fba_value;
rt_new_alpha_min = std::min(static_cast<int>(std::min(m_draw_env->TEXA.TA1, m_draw_env->TEXA.TA0) & 0x80), rt_new_alpha_min);
rt_new_alpha_max = std::max(static_cast<int>((std::max(m_cached_ctx.TEXA.TA1, m_cached_ctx.TEXA.TA0) & 0x80) + 127), rt_new_alpha_max) | fba_value;
rt_new_alpha_min = std::min(static_cast<int>(std::min(m_cached_ctx.TEXA.TA1, m_cached_ctx.TEXA.TA0) & 0x80), rt_new_alpha_min);
}
else
{
rt_new_alpha_max = (std::max(m_draw_env->TEXA.TA1, m_draw_env->TEXA.TA0) & 0x80) + 127 | fba_value;
rt_new_alpha_min = (std::min(m_draw_env->TEXA.TA1, m_draw_env->TEXA.TA0) & 0x80) | fba_value;
rt_new_alpha_max = (std::max(m_cached_ctx.TEXA.TA1, m_cached_ctx.TEXA.TA0) & 0x80) + 127 | fba_value;
rt_new_alpha_min = (std::min(m_cached_ctx.TEXA.TA1, m_cached_ctx.TEXA.TA0) & 0x80) | fba_value;
}
rt->m_alpha_range = true;
}
@@ -8149,7 +8189,7 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
return false;
}
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_draw_env->TEXA.AEM;
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_cached_ctx.TEXA.AEM;
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
@@ -8178,7 +8218,7 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
if (start_bp < dirty_end_bp && end_bp > dirty_start_bp)
{
if (dirty_start_bp > start_bp || dirty_end_bp < end_bp)
if (dirty_start_bp <= start_bp && dirty_end_bp >= end_bp)
{
return true;
}
@@ -8329,8 +8369,8 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
return false;
}
// Z and color must be constant and the same
if (m_vt.m_eq.rgba != 0xFFFF || !m_vt.m_eq.z)
// Z and color must be constant and the same and both are enabled.
if (m_vt.m_eq.rgba != 0xFFFF || !m_vt.m_eq.z || (no_ds != no_rt))
return false;
const u32 write_color = GetConstantDirectWriteMemClearColor();
@@ -8409,7 +8449,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
// bang up next to each other, or a double half clear. The two are really difficult to differentiate.
// Have to check both contexts, because God of War 2 likes to do this in-between setting TRXDIR, which
// causes a flush, and we don't have the next context backed up index set.
bool horizontal = false;
bool horizontal = std::abs(static_cast<int>(m_cached_ctx.FRAME.FBP) - static_cast<int>(m_cached_ctx.ZBUF.ZBP)) == (m_cached_ctx.FRAME.FBW >> 1);
const bool possible_next_clear = !m_env.PRIM.TME && !(m_env.SCANMSK.MSK & 2) && !m_env.CTXT[next_ctx].TEST.ATE && !m_env.CTXT[next_ctx].TEST.DATE &&
(!m_env.CTXT[next_ctx].TEST.ZTE || m_env.CTXT[next_ctx].TEST.ZTST == ZTST_ALWAYS);
@@ -8492,6 +8532,14 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
else
{
const int height = m_r.height();
// We don't want to double half clear already full sized targets, making them double the size, this could be very bad.
// This gets triggered by Monster Lab which clears the Z and FRAME in one go, butted up against each other.
// It's highly unlikely that it will actually require a > 600 high framebuffer, but check with the display height first.
const int display_height = PCRTCDisplays.GetResolution().y;
if ((display_height != 0 && height >= (display_height - 1)) || height > 300)
return false;
m_r.w = ((half - base) / m_cached_ctx.FRAME.FBW) * frame_psm.pgs.y;
m_r.w += m_r.y + height;
}
@@ -8974,6 +9022,9 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
{
if (GSConfig.UserHacks_NativeScaling == GSNativeScaling::Off)
return 0;
const GSVector2i draw_size = GSVector2i(m_vt.m_max.p.x - m_vt.m_min.p.x, m_vt.m_max.p.y - m_vt.m_min.p.y);
GSVector2i tex_size = GSVector2i(m_vt.m_max.t.x - m_vt.m_min.t.x, m_vt.m_max.t.y - m_vt.m_min.t.y);
@@ -8984,10 +9035,13 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
// Try to catch cases of stupid draws like Manhunt and Syphon Filter where they sample a single pixel.
// Also make sure it's grabbing most of the texture.
if (tex_size.x == 0 || tex_size.y == 0 || draw_size.x == 0 || draw_size.y == 0 || !is_target_src)
if (tex_size.x == 0 || tex_size.y == 0 || draw_size.x == 0 || draw_size.y == 0)
return 0;
if (is_target_src && src->m_from_target->m_downscaled && std::abs(draw_size.x - tex_size.x) <= 1 && std::abs(draw_size.y - tex_size.y) <= 1)
const bool no_resize = (std::abs(draw_size.x - tex_size.x) <= 1 && std::abs(draw_size.y - tex_size.y) <= 1);
const bool can_maintain = no_resize || (!is_target_src && m_index.tail == 2);
if (!src || ((!is_target_src || src->m_from_target->m_downscaled) && can_maintain))
return -1;
const GSDrawingContext& next_ctx = m_env.CTXT[m_env.PRIM.CTXT];
@@ -9000,8 +9054,9 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
const bool is_downscale = m_cached_ctx.TEX0.TBW >= m_cached_ctx.FRAME.FBW && draw_size.x <= (tex_size.x * 0.75f) && draw_size.y <= (tex_size.y * 0.75f);
// Check we're getting most of the texture and not just stenciling a part of it.
// Only allow non-bilineared downscales if it's most of the target (misdetections of shadows in Naruto, Transformers etc), otherwise it's fine.
const GSVector2i tex_size_half = GSVector2i((src->GetRegion().HasX() ? src->GetRegionSize().x : src->m_from_target->m_valid.width()) / 2, (src->GetRegion().HasY() ? src->GetRegionSize().y : src->m_from_target->m_valid.height()) / 2);
const bool possible_downscale = m_context->TEX1.MMAG == 1 || src->m_from_target->m_downscaled || tex_size.x >= tex_size_half.x || tex_size.y >= tex_size_half.y;
const GSVector4i src_valid = src->m_from_target ? src->m_from_target->m_valid : src->m_valid_rect;
const GSVector2i tex_size_half = GSVector2i((src->GetRegion().HasX() ? src->GetRegionSize().x : src_valid.width()) / 2, (src->GetRegion().HasY() ? src->GetRegionSize().y : src_valid.height()) / 2);
const bool possible_downscale = m_context->TEX1.MMIN == 1 || !src->m_from_target || src->m_from_target->m_downscaled || tex_size.x >= tex_size_half.x || tex_size.y >= tex_size_half.y;
if (is_downscale && (draw_size.x >= PCRTCDisplays.GetResolution().x || !possible_downscale))
return 0;
@@ -9019,6 +9074,39 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
return is_upscale ? 2 : 1;
}
// Last ditched check if it's doing a lot of small draws exactly the same which could be recursive lighting bloom.
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_index.tail > 2 && !no_gaps_or_single_sprite && m_context->TEX1.MMAG == 1 && !m_context->ALPHA.IsOpaque())
{
GSVertex* v = &m_vertex.buff[0];
float tw = 1 << src->m_TEX0.TW;
float th = 1 << src->m_TEX0.TH;
const int first_u = (PRIM->FST) ? (v[1].U - v[0].U) >> 4 : std::floor(static_cast<int>(tw * v[1].ST.S) - static_cast<int>(tw * v[0].ST.S));
const int first_v = (PRIM->FST) ? (v[1].V - v[0].V) >> 4 : std::floor(static_cast<int>(th * v[1].ST.T) - static_cast<int>(th * v[0].ST.T));
const int first_x = (v[1].XYZ.X - v[0].XYZ.X) >> 4;
const int first_y = (v[1].XYZ.Y - v[0].XYZ.Y) >> 4;
if (first_x > first_u && first_y > first_v && !no_resize && std::abs(draw_size.x - first_x) <= 4 && std::abs(draw_size.y - first_y) <= 4)
{
for (u32 i = 2; i < m_index.tail; i += 2)
{
const int next_u = (PRIM->FST) ? (v[i + 1].U - v[i].U) >> 4 : std::floor(static_cast<int>(tw * v[i + 1].ST.S) - static_cast<int>(tw * v[i].ST.S));
const int next_v = (PRIM->FST) ? (v[i + 1].V - v[i].V) >> 4 : std::floor(static_cast<int>(th * v[i + 1].ST.T) - static_cast<int>(th * v[i].ST.T));
const int next_x = (v[i + 1].XYZ.X - v[i].XYZ.X) >> 4;
const int next_y = (v[i + 1].XYZ.Y - v[i].XYZ.Y) >> 4;
if (std::abs(draw_size.x - next_x) > 4 || std::abs(draw_size.y - next_y) > 4)
break;
if (next_u != first_u || next_v != first_v || next_x != first_x || next_y != first_y)
break;
if (i + 2 >= m_index.tail)
return 2;
}
}
}
return 0;
}

View File

@@ -140,6 +140,7 @@ private:
struct HWCachedCtx
{
GIFRegTEX0 TEX0;
GIFRegTEXA TEXA;
GIFRegCLAMP CLAMP;
GIFRegTEST TEST;
GIFRegFRAME FRAME;

View File

@@ -228,19 +228,22 @@ bool GSTextureCache::CanTranslate(u32 bp, u32 bw, u32 spsm, GSVector4i r, u32 db
{
const GSVector2i src_page_size = GSLocalMemory::m_psm[spsm].pgs;
const GSVector2i dst_page_size = GSLocalMemory::m_psm[dpsm].pgs;
const u32 src_bw = std::max(1U, bw);
const u32 dst_bw = std::max(1U, dbw);
const bool block_layout_match = GSLocalMemory::m_psm[spsm].bpp == GSLocalMemory::m_psm[dpsm].bpp;
const bool bp_page_aligned_bp = ((bp & ~((1 << 5) - 1)) == bp) || bp == dbp || (block_layout_match && bw == dbw);
const bool bp_page_aligned_bp = ((bp & ~((1 << 5) - 1)) == bp) || bp == dbp || (block_layout_match && src_bw == dst_bw);
const GSVector4i page_mask(GSVector4i((src_page_size.x - 1), (src_page_size.y - 1)).xyxy());
const GSVector4i masked_rect(r & ~page_mask);
const int src_pixel_width = static_cast<int>(bw * 64);
const int src_pixel_width = src_bw * 64;
const int dst_pixel_width = dst_bw * 64;
// We can do this if:
// The page width matches.
// The rect width is less than the width of the destination texture and the height is less than or equal to 1 page high.
// The rect width and height is equal to the page size and it covers the width of the incoming bw, so lines are sequential.
const bool page_aligned_rect = masked_rect.xyxy().eq(r.xyxy());
const bool width_match = ((bw * 64) / src_page_size.x) == ((dbw * 64) / dst_page_size.x);
const bool width_match = (src_pixel_width / src_page_size.x) == (dst_pixel_width / dst_page_size.x);
const bool sequential_pages = page_aligned_rect && r.x == 0 && r.z == src_pixel_width;
const bool single_row = (((bw * 64) / src_page_size.x) <= ((dbw * 64) / dst_page_size.x)) && r.width() <= src_pixel_width && r.height() <= src_page_size.y;
const bool single_row = ((src_pixel_width / src_page_size.x) <= (dst_pixel_width / dst_page_size.x)) && r.width() <= src_pixel_width && r.height() <= src_page_size.y;
const bool single_page_aligned = page_aligned_rect && r.z <= src_page_size.x && r.w <= src_page_size.y;
if (block_layout_match)
{
@@ -300,7 +303,7 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
return GSVector4i::zero();
}
const bool block_matched_format = s_psm.bpp == t_psm.bpp && (sbw == tbw || sbw == 1);
const bool block_matched_format = s_psm.bpp == t_psm.bpp && (clamped_sbw == clamped_tbw || clamped_sbw == 1);
// If there is block offset left over, try to adjust to that.
if (block_matched_format)
{
@@ -424,13 +427,15 @@ GSVector4i GSTextureCache::TranslateAlignedRectByPage(u32 tbp, u32 tebp, u32 tbw
}
}
}
else // Widths match
else if (!block_offset) // Widths match
{
const int horizontal_dst_page_offset = page_offset % clamped_tbw;
const int vertical_dst_page_offset = page_offset / clamped_tbw;
GSVector4i offset_rect(horizontal_dst_page_offset * t_psm.pgs.x, vertical_dst_page_offset * t_psm.pgs.y);
new_rect = in_rect + offset_rect.xyxy();
}
else
new_rect = in_rect;
if (new_rect.z > dst_bw)
{
@@ -1320,7 +1325,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
// If the BP is offset in to a page and the format does not match, trying to match up the correct position is very difficult since we don't swizzle.
// Tomb Raider Legends does a block level BP in PSMT8 over a C16 target, which is just a nightmare to get right.
// Baldurs Gate used to have this too, but now we can translate HW moves inside targets when the format matches.
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)) && !CanTranslate(bp, bw, psm, block_boundary_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW))
if (((bp & (BLOCKS_PER_PAGE - 1)) != (t->m_TEX0.TBP0 & (BLOCKS_PER_PAGE - 1))) && (bp & (BLOCKS_PER_PAGE - 1)))
continue;
const bool width_match = (std::max(64U, bw * 64U) >> GSLocalMemory::m_psm[psm].info.pageShiftX()) ==
@@ -1626,14 +1631,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
{
// It is a complex to convert the code in shader. As a reference, let's do it on the CPU,
// it will be slow but can work even with upscaling, also fine tune it so it's not enabled when not needed.
if (psm == PSMT4 || (GSConfig.UserHacks_CPUFBConversion && psm == PSMT8 && (!possible_shuffle || GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != 32)) ||
(psm == PSMT8H && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16))
if (psm == PSMT4 || (psm == PSMT8H && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16))
{
// Forces 4-bit and 8-bit frame buffer conversion to be done on the CPU instead of the GPU, but performance will be slower.
// There is no dedicated shader to handle 4-bit conversion (Beyond Good and Evil and Stuntman).
// Enable readbacks on PSMT4 as we don't have a dedicated shader (Beyond Good and Evil and Stuntman).
// Enable readbacks on PSMT8H 16bit as we don't have a dedicated shader (History Channel - Battle for the Pacific, Sea World - Shamu's Big Adventure).
// Note: Stuntman no longer hits the PSMT4 code path.
// Direct3D10/11 and OpenGL support 8-bit fb conversion but don't render some corner cases properly (Harry Potter games).
// The hack can fix glitches in some games.
// Note2: Harry Potter is now properly handled with shader conversion and no need to enable frame buffer conversion.
if (!t->m_drawn_since_read.rempty())
{
t->UnscaleRTAlpha();
@@ -1789,7 +1792,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
//rect = rect.rintersect(t->m_valid);
if (rect.rintersect(t->m_valid).rempty())
if (rect.rintersect(t->m_valid - GSVector4i(0, 1).xyxy()).rempty())
continue;
if (!t->m_dirty.empty())
@@ -2043,11 +2046,29 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
{
rect.y /= 2;
rect.w /= 2;
if (region.HasY())
{
const u32 min_y = region.GetMinY() / 2;
const u32 max_y = region.GetMaxY() / 2;
region.ClearY();
region.SetY(min_y, max_y);
}
}
else
{
rect.x /= 2;
rect.z /= 2;
if (region.HasX())
{
const u32 min_x = region.GetMinX() / 2;
const u32 max_x = region.GetMaxX() / 2;
region.ClearX();
region.SetX(min_x, max_x);
}
}
if (TEX0.TBP0 == frame.Block())
{
@@ -2361,6 +2382,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// Probably pointing to half way through the target
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
{
// Some games misuse the scissor so it ends up valid 1 pixel over, which causes hell for us. So check if it still overlaps without the extra pixel.
const GSVector4i adjusted_valid = GSVector4i(t->m_valid.x, t->m_valid.y, std::min(t->m_valid.z, static_cast<int>(t->m_TEX0.TBW) * 64), t->m_valid.w - 1);
const u32 adjusted_endblock = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, adjusted_valid);
if (adjusted_endblock <= bp)
{
i++;
continue;
}
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
const bool no_target_or_newer = (!dst || ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
@@ -2368,10 +2398,11 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
const bool ds_offset = !ds || offset != 0;
const bool is_double_buffer = TEX0.TBP0 == ((((t->m_end_block + 1) - t->m_TEX0.TBP0) / 2) + t->m_TEX0.TBP0);
const bool source_match = src && src->m_TEX0.TBP0 == bp && src->m_TEX0.TBW == TEX0.TBW && src->m_from_target && src->m_from_target == t;
const bool was_used_last_draw = t->m_last_draw == (GSState::s_n - 1);
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer)))
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer || was_used_last_draw)))
{
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
@@ -2379,9 +2410,10 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// 896 is just 448 * 2,just gives the buffer chance to be larger than normal, in case they do something like 640x640, or something ridiculous.
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
{
t->m_valid.w /= 2;
t->m_end_block = ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) - 1;
continue;
u32 offset = (((bp - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * s_psm.pgs.y;
dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - offset), GSVector2i(t->m_valid.z, t->m_valid.w - offset), scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src);
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
break;
}
// I know what you're thinking, and I hate the guy who wrote it too (me). Project Snowblind, Tomb Raider etc decide to offset where they're drawing using a channel shuffle, and this gets messy, so best just to kill the old target.
@@ -3383,7 +3415,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
Target* t = *j;
if (dst != t && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst_valid) &&
static_cast<int>(((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) / 32) % std::max(dst->m_TEX0.TBW, 1U)) <= std::max(0, static_cast<int>(dst->m_TEX0.TBW - t->m_TEX0.TBW)))
((std::abs(static_cast<int>(t->m_TEX0.TBP0 - dst->m_TEX0.TBP0)) >> 5) % std::max(static_cast<int>(dst->m_TEX0.TBW), 1)) <= std::max(0, static_cast<int>(dst->m_TEX0.TBW - t->m_TEX0.TBW)))
{
const u32 buffer_width = std::max(1U, dst->m_TEX0.TBW);
@@ -3406,7 +3438,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
}
// If the two targets are misaligned, it's likely a relocation, so we can just kill the old target.
// Kill targets that are overlapping new targets, but ignore the copy if the old target is dirty because we favour GS memory.
if (((((t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) >> 5) % buffer_width) != 0) && !t->m_dirty.empty())
if (((std::abs(static_cast<int>(t->m_TEX0.TBP0 - dst->m_TEX0.TBP0) >> 5) % buffer_width) != 0) && !t->m_dirty.empty())
{
InvalidateSourcesFromTarget(t);
i = list.erase(j);
@@ -3420,6 +3452,23 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
{
GSVector4i new_valid = t->m_valid;
new_valid.w /= 2;
if (preserve_target && t->m_scale == dst->m_scale && dst->m_type == t->m_type && dst->m_dirty.empty() && !t->m_drawn_since_read.rintersect(new_valid).eq(t->m_drawn_since_read))
{
// Clamp the copy inside the source and destination.
const GSVector4i copy_rect = GSVector4i(GSVector4((new_valid + GSVector4i(0, new_valid.w).xyxy()).rintersect(t->m_drawn_since_read).rintersect(GSVector4i(0, 0, dst->m_unscaled_size.x, new_valid.w + dst->m_unscaled_size.y))) * dst->m_scale);
// Copy over the double buffer data, in case we need it.
// Clear the dirty first
t->Update();
dst->m_valid_rgb = t->m_valid_rgb;
dst->m_valid_alpha_low = t->m_valid_alpha_low;
dst->m_valid_alpha_high = t->m_valid_alpha_high;
dst->m_alpha_max = t->m_alpha_max;
dst->m_alpha_min = t->m_alpha_min;
dst->m_rt_alpha_scale = t->m_rt_alpha_scale;
g_gs_device->CopyRect(t->m_texture, dst->m_texture, copy_rect, 0, 0);
}
GL_INS("TC: RT resize buffer for FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, t->m_valid.width(), t->m_valid.height(), new_valid.width(), new_valid.height());
t->ResizeValidity(new_valid);
return hw_clear.value_or(false);
@@ -3829,6 +3878,9 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
{
const GSVector4i right(old_width, 0, preload_width, needed_height);
const GSVector4i bottom(0, old_height, old_width, needed_height);
t->UpdateValidity(right.rintersect(bottom));
AddDirtyRectTarget(t, right, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
AddDirtyRectTarget(t, bottom, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
}
@@ -3837,6 +3889,9 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
const GSVector4i newrect = GSVector4i((old_height < new_height) ? 0 : old_width,
(old_width < preload_width) ? 0 : old_height,
preload_width, needed_height);
t->UpdateValidity(newrect);
AddDirtyRectTarget(t, newrect, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
}
@@ -8036,10 +8091,11 @@ GSTextureCache::TempZAddress GSTextureCache::GetTemporaryZInfo()
return m_temporary_z_info;
}
void GSTextureCache::SetTemporaryZInfo(u32 address, u32 offset)
void GSTextureCache::SetTemporaryZInfo(u32 address, u32 offset, u32 rt_offset)
{
m_temporary_z_info.ZBP = address;
m_temporary_z_info.offset = offset;
m_temporary_z_info.rt_offset = rt_offset;
m_temporary_z_info.rect_since = GSVector4i::zero();
}
void GSTextureCache::SetTemporaryZInfo(TempZAddress address_info)

View File

@@ -45,6 +45,9 @@ public:
bool HasY() const { return static_cast<u32>(bits >> 32) != 0; }
bool HasEither() const { return (bits != 0); }
void ClearX() { bits &= ~0xFFFFFFFFULL; }
void ClearY() { bits &= 0xFFFFFFFFULL; }
void SetX(s32 min, s32 max) { bits |= (static_cast<u64>(static_cast<u16>(min)) | (static_cast<u64>(static_cast<u16>(max) << 16))); }
void SetY(s32 min, s32 max) { bits |= ((static_cast<u64>(static_cast<u16>(min)) << 32) | (static_cast<u64>(static_cast<u16>(max)) << 48)); }
@@ -210,6 +213,7 @@ public:
{
u32 ZBP;
int offset;
int rt_offset;
GSVector4i rect_since;
};
@@ -564,7 +568,7 @@ public:
void SetTemporaryZ(GSTexture* temp_z);
GSTexture* GetTemporaryZ();
TempZAddress GetTemporaryZInfo();
void SetTemporaryZInfo(u32 address, u32 offset);
void SetTemporaryZInfo(u32 address, u32 offset, u32 rt_offset);
void SetTemporaryZInfo(TempZAddress address_info);
/// Invalidates a temporary Z, a partial copy only created from the current DS for the current draw when Z is not offset but RT is.
void InvalidateTemporaryZ();

View File

@@ -1911,7 +1911,7 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* ver
OMSetRenderTargets(nullptr, ds, &GLState::scissor);
{
const GLint clear_color = 0;
constexpr GLint clear_color = 0;
glClearBufferiv(GL_STENCIL, 0, &clear_color);
}
m_convert.ps[SetDATMShader(datm)].Bind();
@@ -1919,10 +1919,8 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* ver
// om
OMSetDepthStencilState(m_date.dss);
if (GLState::blend)
{
glDisable(GL_BLEND);
}
OMSetBlendState(false);
OMSetColorMaskState();
// ia
@@ -1930,18 +1928,12 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* ver
IASetVertexBuffer(vertices, 4);
IASetPrimitiveTopology(GL_TRIANGLE_STRIP);
// Texture
PSSetShaderResource(0, rt);
PSSetSamplerState(m_convert.pt);
DrawPrimitive();
if (GLState::blend)
{
glEnable(GL_BLEND);
}
}
void GSDeviceOGL::IASetVAO(GLuint vao)
@@ -2475,7 +2467,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
primid_texture = InitPrimDateTexture(colclip_rt ? colclip_rt : config.rt, config.drawarea, config.datm);
if (!primid_texture)
{
Console.WriteLn("GL: Failed to allocate DATE image, aborting draw.");
Console.Warning("GL: Failed to allocate DATE image, aborting draw.");
return;
}
break;
@@ -2585,6 +2577,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
{
// Ensure all depth writes are finished before sampling
GL_INS("GL: Texture barrier to flush depth or rt before reading");
g_perfmon.Put(GSPerfMon::Barriers, 1);
glTextureBarrier();
}
// additional non-pipeline config stuff
@@ -2607,7 +2600,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
}
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
if (primid_texture)
{
GL_PUSH("Destination Alpha PrimID Init");
@@ -2627,7 +2620,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
psel.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
SetupPipeline(psel);
PSSetShaderResource(3, primid_texture);
}
if (config.blend.IsEffective(config.colormask))
@@ -2657,6 +2649,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor);
if (primid_texture)
PSSetShaderResource(3, primid_texture);
OMSetColorMaskState(config.colormask);
SetupOM(config.depth);

View File

@@ -5630,7 +5630,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
date_image = SetupPrimitiveTrackingDATE(config);
if (!date_image)
{
Console.WriteLn("VK: Failed to allocate DATE image, aborting draw.");
Console.Warning("VK: Failed to allocate DATE image, aborting draw.");
return;
}

View File

@@ -75,6 +75,7 @@ class Error;
#pragma clang diagnostic ignored "-Wnullability-completeness"
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wunused-function"
#pragma clang diagnostic ignored "-Wunused-private-field"
#endif
#define VMA_STATIC_VULKAN_FUNCTIONS 1

View File

@@ -139,6 +139,7 @@ using ImGuiFullscreen::GetCachedTextureAsync;
using ImGuiFullscreen::GetPlaceholderTexture;
using ImGuiFullscreen::GetQueuedFocusResetType;
using ImGuiFullscreen::HorizontalMenuItem;
using ImGuiFullscreen::HorizontalMenuSvgItem;
using ImGuiFullscreen::IsFocusResetQueued;
using ImGuiFullscreen::IsGamepadInputSource;
using ImGuiFullscreen::LayoutScale;
@@ -271,8 +272,6 @@ namespace FullscreenUI
static std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
s_game_compatibility_textures;
static std::shared_ptr<GSTexture> s_banner_texture;
static std::shared_ptr<GSTexture> s_fallback_disc_texture;
static std::shared_ptr<GSTexture> s_fallback_exe_texture;
static std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
//////////////////////////////////////////////////////////////////////////
@@ -476,9 +475,13 @@ namespace FullscreenUI
static void DrawGameListSettingsWindow();
static void SwitchToGameList();
static void PopulateGameListEntryList();
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type);
static GSTexture* GetTextureForGameListEntryType(GameList::EntryType type, const ImVec2& size, SvgScaling mode = SvgScaling::Stretch);
static GSTexture* GetGameListCover(const GameList::Entry* entry);
static GSTexture* GetCoverForCurrentGame();
static void DrawGameCover(const GameList::Entry* entry, const ImVec2& size);
static void DrawGameCover(const GameList::Entry* entry, ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
// For when we have no GameList entry
static void DrawFallbackCover(const ImVec2& size);
static void DrawFallbackCover(ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
// Lazily populated cover images.
static std::unordered_map<std::string, std::string> s_cover_image_map;
@@ -1107,9 +1110,6 @@ void FullscreenUI::ReturnToMainWindow()
bool FullscreenUI::LoadResources()
{
s_fallback_disc_texture = LoadTexture("fullscreenui/media-cdrom.png");
s_fallback_exe_texture = LoadTexture("fullscreenui/applications-system.png");
return LoadSvgResources();
}
@@ -1128,8 +1128,6 @@ bool FullscreenUI::LoadSvgResources()
void FullscreenUI::DestroyResources()
{
s_fallback_exe_texture.reset();
s_fallback_disc_texture.reset();
s_banner_texture.reset();
for (auto& tex : s_game_compatibility_textures)
tex.reset();
@@ -1457,27 +1455,26 @@ void FullscreenUI::DrawLandingWindow()
{
ResetFocusHere();
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/game-list.png"), FSUI_CSTR("Game List"),
if (HorizontalMenuSvgItem("fullscreenui/game-list.svg", FSUI_CSTR("Game List"),
FSUI_CSTR("Launch a game from images scanned from your game directories.")))
{
SwitchToGameList();
}
if (HorizontalMenuItem(
GetCachedTexture("fullscreenui/media-cdrom.png"), FSUI_CSTR("Start Game"),
if (HorizontalMenuSvgItem("fullscreenui/media-cdrom.svg", FSUI_CSTR("Start Game"),
FSUI_CSTR("Launch a game from a file, disc, or starts the console without any disc inserted.")))
{
s_current_main_window = MainWindowType::StartGame;
QueueResetFocus(FocusResetType::WindowChanged);
}
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/applications-system.png"), FSUI_CSTR("Settings"),
if (HorizontalMenuSvgItem("fullscreenui/applications-system.svg", FSUI_CSTR("Settings"),
FSUI_CSTR("Changes settings for the application.")))
{
SwitchToSettings();
}
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/exit.png"), FSUI_CSTR("Exit"),
if (HorizontalMenuSvgItem("fullscreenui/exit.svg", FSUI_CSTR("Exit"),
FSUI_CSTR("Return to desktop mode, or exit the application.")) ||
(!AreAnyDialogsOpen() && WantsToCloseMenu()))
{
@@ -1536,26 +1533,25 @@ void FullscreenUI::DrawStartGameWindow()
{
ResetFocusHere();
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-file.png"), FSUI_CSTR("Start File"),
if (HorizontalMenuSvgItem("fullscreenui/start-file.svg", FSUI_CSTR("Start File"),
FSUI_CSTR("Launch a game by selecting a file/disc image.")))
{
DoStartFile();
}
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/drive-cdrom.png"), FSUI_CSTR("Start Disc"),
if (HorizontalMenuSvgItem("fullscreenui/drive-cdrom.svg", FSUI_CSTR("Start Disc"),
FSUI_CSTR("Start a game from a disc in your PC's DVD drive.")))
{
DoStartDisc();
}
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/start-bios.png"), FSUI_CSTR("Start BIOS"),
if (HorizontalMenuSvgItem("fullscreenui/start-bios.svg", FSUI_CSTR("Start BIOS"),
FSUI_CSTR("Start the console without any disc inserted.")))
{
DoStartBIOS();
}
// https://www.iconpacks.net/free-icon/arrow-back-3783.html
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/back-icon.png"), FSUI_CSTR("Back"),
if (HorizontalMenuSvgItem("fullscreenui/back-icon.svg", FSUI_CSTR("Back"),
FSUI_CSTR("Return to the previous menu.")) ||
(!AreAnyDialogsOpen() && WantsToCloseMenu()))
{
@@ -1606,8 +1602,7 @@ void FullscreenUI::DrawExitWindow()
{
ResetFocusHere();
// https://www.iconpacks.net/free-icon/arrow-back-3783.html
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/back-icon.png"), FSUI_CSTR("Back"),
if (HorizontalMenuSvgItem("fullscreenui/back-icon.svg", FSUI_CSTR("Back"),
FSUI_CSTR("Return to the previous menu.")) ||
WantsToCloseMenu())
{
@@ -1615,7 +1610,7 @@ void FullscreenUI::DrawExitWindow()
QueueResetFocus(FocusResetType::WindowChanged);
}
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/exit.png"), FSUI_CSTR("Exit PCSX2"),
if (HorizontalMenuSvgItem("fullscreenui/exit.svg", FSUI_CSTR("Exit PCSX2"),
FSUI_CSTR("Completely exits the application, returning you to your desktop.")))
{
DoRequestExit();
@@ -1623,8 +1618,8 @@ void FullscreenUI::DrawExitWindow()
if (!Host::InNoGUIMode())
{
if (HorizontalMenuItem(GetCachedTexture("fullscreenui/desktop-mode.png"), FSUI_CSTR("Desktop Mode"),
FSUI_CSTR("Exits Big Picture mode, returning to the desktop interface.")))
if (HorizontalMenuSvgItem("fullscreenui/desktop-mode.svg", FSUI_CSTR("Desktop Mode"),
FSUI_CSTR("Exits Big Picture mode, returning to the desktop interface.")))
{
DoDesktopMode();
}
@@ -3384,7 +3379,7 @@ void FullscreenUI::DrawSummarySettingsPage()
for (const std::string& name : Pad::GetInputProfileNames())
{
options.emplace_back(fmt::format(FSUI_FSTR(name)), (value.has_value() && !value->empty() && value == name) ? true : false);
options.emplace_back(name, (value.has_value() && !value->empty() && value == name) ? true : false);
names.push_back(std::move(name));
}
@@ -5463,14 +5458,18 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
}
DrawShadowedText(dl, g_medium_font, subtitle_pos, text_color, s_current_game_subtitle.c_str());
GSTexture* const cover = GetCoverForCurrentGame();
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_max(image_min.x + LayoutScale(image_width), image_min.y + LayoutScale(image_height) + rp_height);
const ImRect image_rect(CenterImage(
ImRect(image_min, image_max), ImVec2(static_cast<float>(cover->GetWidth()), static_cast<float>(cover->GetHeight()))));
dl->AddImage(reinterpret_cast<ImTextureID>(cover->GetNativeHandle()), image_rect.Min, image_rect.Max);
{
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str());
if (entry)
DrawGameCover(entry, dl, image_min, image_max);
else
DrawFallbackCover(dl, image_min, image_max);
}
}
// current time / play time
@@ -6286,7 +6285,7 @@ void FullscreenUI::PopulateGameListEntryList()
case 4: // CRC
{
if (lhs->crc != rhs->crc)
return reverse ? (lhs->crc >= rhs->crc) : (lhs->crc < rhs->crc);
return reverse ? (lhs->crc > rhs->crc) : (lhs->crc < rhs->crc);
}
break;
@@ -6458,8 +6457,6 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
if (!visible)
continue;
GSTexture* cover_texture = GetGameListCover(entry);
summary.clear();
if (entry->serial.empty())
fmt::format_to(std::back_inserter(summary), "{} - ", GameList::RegionToString(entry->region, true));
@@ -6469,11 +6466,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
const std::string_view filename(Path::GetFileName(entry->path));
summary.append(filename);
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()),
image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
DrawGameCover(entry, ImGui::GetWindowDrawList(), bb.Min, bb.Min + image_size);
const float midpoint = bb.Min.y + g_large_font->FontSize + LayoutScale(4.0f);
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
@@ -6514,29 +6507,46 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
if (BeginFullscreenColumnWindow(-530.0f, 0.0f, "game_list_info", UIPrimaryDarkColor))
{
const GSTexture* cover_texture =
selected_entry ? GetGameListCover(selected_entry) : GetTextureForGameListEntryType(GameList::EntryType::Count);
if (cover_texture)
{
const ImRect image_rect(CenterImage(LayoutScale(ImVec2(275.0f, 400.0f)),
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
const float img_padding_y = LayoutScale(20.0f);
// Spacing between each text item
const float text_spacing_y = LayoutScale(8.0f);
// Space between title/serial and details, is in addition to text_spacing_y
const float title_padding_below_y = LayoutScale(12.0f);
ImGui::SetCursorPos(LayoutScale(ImVec2(128.0f, 20.0f)) + image_rect.Min);
ImGui::Image(reinterpret_cast<ImTextureID>(selected_entry ? GetGameListCover(selected_entry)->GetNativeHandle() :
GetTextureForGameListEntryType(GameList::EntryType::Count)->GetNativeHandle()),
image_rect.GetSize());
}
// Estimate how much space is needed for text
// Do this even when nothing is selected, to ensure cover/icon is in a consistant size/position
const float title_detail_height =
LayoutScale(LAYOUT_LARGE_FONT_SIZE) + text_spacing_y + // Title
LayoutScale(LAYOUT_MEDIUM_FONT_SIZE) + text_spacing_y + // Serial
title_padding_below_y +
7.0f * (LayoutScale(LAYOUT_MEDIUM_FONT_SIZE) + text_spacing_y) + // File, CRC, Region, Compat, Time/Last Played, Size
LayoutScale(12.0f); // Extra padding
const float work_width = ImGui::GetCurrentWindow()->WorkRect.GetWidth();
constexpr float field_margin_y = 10.0f;
constexpr float start_x = 50.0f;
float text_y = 440.0f;
// Limit cover height to avoid pushing text off the screen
const ImGuiWindow* window = ImGui::GetCurrentWindow();
// Based on ImGui code for WorkRect, with scrolling logic removed
const float window_height = std::trunc(window->InnerRect.GetHeight() - 2.0f * std::max(window->WindowPadding.y, window->WindowBorderSize));
const float free_height = window_height - title_detail_height;
const float img_height = std::min(free_height - 2.0f * img_padding_y, LayoutScale(400.0f));
const ImVec2 image_size = ImVec2(LayoutScale(275.0f), img_height);
ImGui::SetCursorPos(ImVec2(LayoutScale(128.0f), img_padding_y));
if (selected_entry)
DrawGameCover(selected_entry, image_size);
else
DrawFallbackCover(image_size);
const float work_width = window->WorkRect.GetWidth();
const float start_x = LayoutScale(50.0f);
const float text_y = img_height + 2.0f * img_padding_y;
float text_width;
PushPrimaryColor();
ImGui::SetCursorPos(LayoutScale(start_x, text_y));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, field_margin_y));
ImGui::PushTextWrapPos(LayoutScale(480.0f));
ImGui::SetCursorPos(ImVec2(start_x, text_y));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, text_spacing_y));
ImGui::PushTextWrapPos(LayoutScale(490.0f));
ImGui::BeginGroup();
if (selected_entry)
@@ -6545,6 +6555,8 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
ImGui::PushFont(g_large_font);
const std::string_view title(std::string_view(selected_entry->GetTitle(true)).substr(0, 37));
text_width = ImGui::CalcTextSize(title.data(), title.data() + title.length(), false, work_width).x;
if (title.length() != selected_entry->GetTitle(true).length())
text_width += ImGui::CalcTextSize("...", nullptr, false, -1.0f).x;
ImGui::SetCursorPosX((work_width - text_width) / 2.0f);
ImGui::TextWrapped(
"%.*s%s", static_cast<int>(title.size()), title.data(), (title.length() == selected_entry->GetTitle(true).length()) ? "" : "...");
@@ -6556,7 +6568,7 @@ void FullscreenUI::DrawGameList(const ImVec2& heading_size)
text_width = ImGui::CalcTextSize(selected_entry->serial.c_str(), nullptr, false, work_width).x;
ImGui::SetCursorPosX((work_width - text_width) / 2.0f);
ImGui::TextWrapped("%s", selected_entry->serial.c_str());
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 15.0f);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + title_padding_below_y);
// file tile
ImGui::TextWrapped("%s", SmallString::from_format(FSUI_FSTR("File: {}"), Path::GetFileName(selected_entry->path)).c_str());
@@ -6693,12 +6705,7 @@ void FullscreenUI::DrawGameGrid(const ImVec2& heading_size)
bb.Min += style.FramePadding;
bb.Max -= style.FramePadding;
const GSTexture* const cover_texture = GetGameListCover(entry);
const ImRect image_rect(CenterImage(ImRect(bb.Min, bb.Min + image_size),
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()),
image_rect.Min, image_rect.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
DrawGameCover(entry, ImGui::GetWindowDrawList(), bb.Min, bb.Min + image_size);
const ImRect title_bb(ImVec2(bb.Min.x, bb.Min.y + image_height + title_spacing), bb.Max);
const std::string_view title(std::string_view(entry->GetTitle(true)).substr(0, 31));
@@ -6987,33 +6994,108 @@ GSTexture* FullscreenUI::GetGameListCover(const GameList::Entry* entry)
cover_it = s_cover_image_map.emplace(entry->path, std::move(cover_path)).first;
}
GSTexture* tex = (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
return tex ? tex : GetTextureForGameListEntryType(entry->type);
return (!cover_it->second.empty()) ? GetCachedTextureAsync(cover_it->second.c_str()) : nullptr;
}
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type)
GSTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType type, const ImVec2& size, SvgScaling mode)
{
switch (type)
{
case GameList::EntryType::ELF:
return s_fallback_exe_texture.get();
return GetCachedSvgTexture("fullscreenui/applications-system.svg", size, mode);
case GameList::EntryType::PS1Disc:
case GameList::EntryType::PS2Disc:
default:
return s_fallback_disc_texture.get();
return GetCachedSvgTexture("fullscreenui/media-cdrom.svg", size, mode);
}
}
GSTexture* FullscreenUI::GetCoverForCurrentGame()
void FullscreenUI::DrawGameCover(const GameList::Entry* entry, const ImVec2& size)
{
auto lock = GameList::GetLock();
// Used in DrawGameList (selected preview)
const GSTexture* cover_texture = GetGameListCover(entry);
const GameList::Entry* entry = GameList::GetEntryForPath(s_current_disc_path.c_str());
if (!entry)
return s_fallback_disc_texture.get();
pxAssert(ImGui::GetCurrentContext()->Style.ImageBorderSize == 0);
const ImVec2 origin = ImGui::GetCursorPos();
return GetGameListCover(entry);
if (cover_texture)
{
const ImRect image_rect(CenterImage(size,
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
ImGui::SetCursorPos(origin + image_rect.Min);
ImGui::Image(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()), image_rect.GetSize());
}
else
{
const float min_size = std::min(size.x, size.y);
const ImVec2 image_square(min_size, min_size);
GSTexture* const icon_texture = GetTextureForGameListEntryType(entry->type, image_square);
const ImRect image_rect(CenterImage(size, image_square));
ImGui::SetCursorPos(origin + image_rect.Min);
DrawSvgTexture(icon_texture, image_square);
}
// Pretend the image we drew was the the size passed to us
ImGui::SetCursorPos(origin);
ImGui::Dummy(size);
}
void FullscreenUI::DrawGameCover(const GameList::Entry* entry, ImDrawList* draw_list, const ImVec2& min, const ImVec2& max)
{
// Used in DrawPauseMenu, DrawGameList (list item), DrawGameGrid
const GSTexture* cover_texture = GetGameListCover(entry);
if (cover_texture)
{
const ImRect image_rect(CenterImage(ImRect(min, max),
ImVec2(static_cast<float>(cover_texture->GetWidth()), static_cast<float>(cover_texture->GetHeight()))));
draw_list->AddImage(reinterpret_cast<ImTextureID>(cover_texture->GetNativeHandle()),
image_rect.Min, image_rect.Max);
}
else
{
const float min_size = std::min(max.x - min.x, max.y - min.y);
const ImVec2 image_square(min_size, min_size);
const ImRect image_rect(CenterImage(ImRect(min, max), image_square));
DrawListSvgTexture(draw_list, GetTextureForGameListEntryType(entry->type, image_square, SvgScaling::Fit),
image_rect.Min, image_rect.Max);
}
}
void FullscreenUI::DrawFallbackCover(const ImVec2& size)
{
pxAssert(ImGui::GetCurrentContext()->Style.ImageBorderSize == 0);
const ImVec2 origin = ImGui::GetCursorPos();
const float min_size = std::min(size.x, size.y);
const ImVec2 image_square(min_size, min_size);
GSTexture* const icon_texture = GetTextureForGameListEntryType(GameList::EntryType::PS2Disc, image_square);
const ImRect image_rect(CenterImage(size, image_square));
ImGui::SetCursorPos(origin + image_rect.Min);
DrawSvgTexture(icon_texture, image_square);
// Pretend the image we drew was the the size passed to us
ImGui::SetCursorPos(origin);
ImGui::Dummy(size);
}
void FullscreenUI::DrawFallbackCover(ImDrawList* draw_list, const ImVec2& min, const ImVec2& max)
{
const float min_size = std::min(max.x - min.x, max.y - min.y);
const ImVec2 image_square(min_size, min_size);
const ImRect image_rect(CenterImage(ImRect(min, max), image_square));
DrawListSvgTexture(draw_list, GetTextureForGameListEntryType(GameList::EntryType::PS2Disc, image_square, SvgScaling::Fit),
image_rect.Min, image_rect.Max);
}
//////////////////////////////////////////////////////////////////////////

View File

@@ -1992,7 +1992,7 @@ void ImGuiFullscreen::EndHorizontalMenu()
EndFullscreenWindow();
}
bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, const char* description)
bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const ImVec2& icon_uv0, const ImVec2& icon_uv1, const char* title, const char* description)
{
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
@@ -2031,7 +2031,7 @@ bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, con
const ImVec2 icon_pos = bb.Min + ImVec2((avail_width - icon_size) * 0.5f, 0.0f);
ImDrawList* dl = ImGui::GetWindowDrawList();
dl->AddImage(reinterpret_cast<ImTextureID>(icon->GetNativeHandle()), icon_pos, icon_pos + ImVec2(icon_size, icon_size));
dl->AddImage(reinterpret_cast<ImTextureID>(icon->GetNativeHandle()), icon_pos, icon_pos + ImVec2(icon_size, icon_size), icon_uv0, icon_uv1);
ImFont* title_font = g_large_font;
const ImVec2 title_size = title_font->CalcTextSizeA(title_font->FontSize, avail_width, 0.0f, title);
@@ -2056,6 +2056,22 @@ bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, con
return pressed;
}
bool ImGuiFullscreen::HorizontalMenuItem(GSTexture* icon, const char* title, const char* description)
{
return HorizontalMenuItem(icon, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), title, description);
}
bool ImGuiFullscreen::HorizontalMenuSvgItem(const char* svg_path, const char* title, const char* description, SvgScaling mode)
{
const ImVec2 icon_size = LayoutScale(150.0f, 150.0f);
GSTexture* padded_texture = GetCachedSvgTexture(svg_path, icon_size, mode);
const ImVec2 padded_size(padded_texture->GetWidth(), padded_texture->GetHeight());
const ImVec2 uv1 = icon_size / padded_size;
return HorizontalMenuItem(padded_texture, ImVec2(0.0f, 0.0f), uv1, title, description);
}
void ImGuiFullscreen::PopulateFileSelectorItems()
{
s_file_selector_items.clear();

View File

@@ -235,7 +235,9 @@ namespace ImGuiFullscreen
bool BeginHorizontalMenu(const char* name, const ImVec2& position, const ImVec2& size, u32 num_items);
void EndHorizontalMenu();
bool HorizontalMenuItem(GSTexture* icon, const ImVec2& icon_uv0, const ImVec2& icon_uv1, const char* title, const char* description);
bool HorizontalMenuItem(GSTexture* icon, const char* title, const char* description);
bool HorizontalMenuSvgItem(const char* svg_path, const char* title, const char* description, SvgScaling mode = SvgScaling::Stretch);
using FileSelectorCallback = std::function<void(const std::string& path)>;
using FileSelectorFilters = std::vector<std::string>;

View File

@@ -416,10 +416,10 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
APPEND("B={} PL={} ", static_cast<unsigned>(GSConfig.AccurateBlendingUnit), static_cast<unsigned>(GSConfig.TexturePreloading));
if (GSConfig.GPUPaletteConversion)
APPEND("PT ");
APPEND("PLTX ");
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
APPEND("DL={} ", static_cast<unsigned>(GSConfig.HWDownloadMode));
APPEND("HWDM={} ", static_cast<unsigned>(GSConfig.HWDownloadMode));
if (GSConfig.HWMipmap)
APPEND("MM ");
@@ -466,7 +466,7 @@ __ri void ImGuiManager::DrawSettingsOverlay(float scale, float margin, float spa
if (GSConfig.UserHacks_CPUFBConversion)
APPEND("FBC ");
if (GSConfig.UserHacks_ReadTCOnClose)
APPEND("FTC ");
APPEND("RTOC ");
if (GSConfig.UserHacks_DisableDepthSupport)
APPEND("DDC ");
if (GSConfig.UserHacks_DisablePartialInvalidation)

View File

@@ -947,12 +947,12 @@ namespace R3000A
return 1;
// maximum allowed size for our buffer before we truncate
const unsigned int max_len = 4096;
constexpr unsigned int max_len = 4096;
char tmp[max_len], tmp2[max_len];
char* ptmp = tmp;
unsigned int printed_bytes = 0;
int remaining_buf = max_len - 1;
int n = 1, i = 0, j = 0;
unsigned int n = 1, i = 0, j = 0;
while (fmt[i])
{

View File

@@ -189,16 +189,6 @@ u8 PadNegcon::ModeSwitch(u8 commandByte)
{
case 3:
this->analogLight = commandByte;
if (this->analogLight)
{
this->currentMode = Pad::Mode::ANALOG;
}
else
{
this->currentMode = Pad::Mode::DIGITAL;
}
break;
case 4:
this->analogLocked = (commandByte == 0x03);
@@ -219,7 +209,7 @@ u8 PadNegcon::StatusInfo(u8 commandByte)
case 4:
return 0x02;
case 5:
return this->analogLight;
return 0x01;
case 6:
return 0x02;
case 7:
@@ -500,7 +490,7 @@ u8 PadNegcon::SendCommandByte(u8 commandByte)
Console.Warning("%s(%02X) Config-only command was sent to a pad outside of config mode!", __FUNCTION__, commandByte);
}
ret = this->isInConfig ? static_cast<u8>(Pad::Mode::CONFIG) : static_cast<u8>(this->currentMode);
ret = this->isInConfig ? static_cast<u8>(Pad::Mode::CONFIG) : static_cast<u8>(Pad::Mode::NEGCON);
break;
case 2:
ret = 0x5a;

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 = 66;
static constexpr u32 SHADER_CACHE_VERSION = 67;