Compare commits

...
48 Commits
Author SHA1 Message Date
TheLastRarandlightningterror 38ee1e1e22 GS/DX: Specify shader model directly instead of via feature level 2026-05-11 22:29:45 +02:00
PCSX2 BotandTy 2b02a5dc78 [ci skip] PAD: Update to latest controller database. 2026-05-11 13:30:19 -04:00
descawedandlightningterror dead00eb62 GameDB: Enable Instant DMA Hack for SLPM-74405 to fix hang 2026-05-10 19:07:21 +02:00
TJnotJTandlightningterror 4fa2b8e45d GS/HW: Fixes/improvements to early scissor/bbox test. 2026-05-09 13:48:42 +02:00
refractionpcsx2andlightningterror fc90973509 GS/HW/COLCLIP: Check tex mapping is enabled when checking for recursion 2026-05-09 13:47:58 +02:00
Immersion95andlightningterror ff9176f83e GameDB: fixes wrong title for SLPM-65496 - Hyper Street Fighter II - The Anniversary Edition 2026-05-09 13:46:48 +02:00
refractionpcsx2andlightningterror 63d3dd9978 GS/TC: Update dirty target linked to source when dirty overlaps the read 2026-05-09 13:46:25 +02:00
JordanTheToasterandlightningterror b704c6caaa GameDB: Pump It Up Exceed Fixes 2026-05-08 18:56:51 +02:00
lightningterror a64465681c GS/HW: Disable texture shuffle if there's no tex. 2026-05-08 18:29:05 +02:00
refractionpcsx2andlightningterror 539357436c Build: Clean up a few warnings 2026-05-08 18:17:31 +02:00
refractionpcsx2andlightningterror 22cb6b0c9c GS/HW: Remove zero clear from GSUploadQueue, use transfer_type instead 2026-05-08 18:15:18 +02:00
PCSX2 Botandlightningterror 81df4e5def [ci skip] Qt: Update Base Translation. 2026-05-07 02:15:53 +02:00
ZiemasandTy a3f3e8733d GS: fix build with libstdc++ 16 2026-05-06 18:05:51 -04:00
SternXDandlightningterror 6fc84f76bb FullscreenUI: Fix RA login state, and add dismiss dialog 2026-05-06 20:55:44 +02:00
TJnotJTandlightningterror 0949f193b8 GS/SW: Adjust bounds in assertion to present false positive.
Account for the fact that bottom/right coordinates are exclusive.
2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 5f3dc1a76e GS/SW: Early exit optimization for multithreaded horizontal lines. 2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 9afea55bb6 GS/SW: Adjust bbox to account for AA1 and points/line rounding. 2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 433614ae85 GS: Adjust primitive bbox in vertex kick to account for AA1. 2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 00c85c4a8f GS/TC: Use temporary source on invalid TEX0. 2026-05-06 19:18:04 +02:00
PCSX2 Botandlightningterror f9885315bd [ci skip] Qt: Update Base Translation. 2026-05-06 11:59:56 +02:00
TheLastRarandlightningterror 26563dbae2 GS/HW: Clamp large UVs in shader anisotropic filtering 2026-05-06 11:59:32 +02:00
Ty 53e3838c1d Qt: Add MainWindowMaximized to ini for human readable maximized override 2026-05-05 16:01:35 -04:00
Ty 4509d11daf Qt: Add MainWindowFullscreen to ini for human readable fullscreen override 2026-05-05 16:01:35 -04:00
JordanTheToasterandlightningterror 37d5bf33e8 Deps: Update SDL3 to v3.4.8 2026-05-05 20:01:47 +02:00
PCSX2 Botandlightningterror 76842401ee [ci skip] PAD: Update to latest controller database. 2026-05-04 23:33:20 +02:00
PCSX2 BotandTy bf9d38e86a [ci skip] Qt: Update Base Translation. 2026-05-03 21:08:21 -04:00
Vishrut SachanandTy 7de2aad05a Qt: Fix Big Picture Mode mnemonic shortcut lost after state change 2026-05-03 19:41:23 -04:00
Florin9doiandTy 7d11e1f963 USB: Support for Olympus storage devices
Also the commands were incorrectly handled as file R/W
operations when the host did not provide unique tags,
causing f.buf to be overwritten with file content
2026-05-03 19:38:07 -04:00
TellowKrinkleandlightningterror 452e239eb4 GS:MTL: Fix AA1 depth clears 2026-05-03 23:51:30 +02:00
TJnotJTandlightningterror 8b82fbb449 GS/HW: Add constant buffer dumping. 2026-05-03 18:05:35 +02:00
lightningterror de84c41757 GS: Check if current tex is valid before resizing. 2026-05-03 15:25:58 +02:00
PCSX2 Botandlightningterror 3000e113e2 [ci skip] Qt: Update Base Translation. 2026-05-02 05:39:03 +02:00
refractionpcsx2andlightningterror c5d6b28be4 GS/HW: Correct buffered rect size of corrected EE->GS transfers 2026-05-02 05:38:48 +02:00
chaoticgdandlightningterror c0dfb8cfa0 Debugger: Call QWidget::update instead of QWidget::repaint 2026-05-01 20:47:40 +02:00
lightningterror f9feca4f4d GS/DX11/GL: Further cleanup to the copy behavior and some features checks. 2026-05-01 20:35:30 +02:00
lightningterror 4fa9c4e382 GS/HW: Check if alpha second pass copies are feedback loops. 2026-05-01 20:35:30 +02:00
lightningterror 93f1fcbe88 GS/HW: Sync rt copy behaviour across renderers. 2026-05-01 20:35:30 +02:00
TJnotJTandlightningterror 3d71e310c0 GSRunner: Add -debugdevice command line parameter. 2026-04-30 02:21:35 +02:00
TJnotJTandlightningterror 02d0f613ba GS/HW: Fix brackets in texture shuffle detection. 2026-04-30 02:03:44 +02:00
PCSX2 Botandlightningterror 3091407e9e [ci skip] Qt: Update Base Translation. 2026-04-29 22:53:37 +02:00
ZiemasandTy 70f6169eff StackWalker: Fix bugs.
Fixes incorrect stack tracing in cases where we are stoppen on certain instructions.
2026-04-28 19:58:38 -04:00
ZiemasandTy a1a3cd9d1b DebugData: Remove stack getters
They're not used and the naming is confused, we can readd these if we
actually want to use them.
2026-04-28 19:57:48 -04:00
ZiemasandTy 86c5d2ae2a DebugInterface: Allow stack tracing any IOP thread 2026-04-28 19:57:48 -04:00
ZiemasandTy d759055231 DebugInterface: Enable stack tracing any EE thread 2026-04-28 19:57:48 -04:00
ZiemasandTy c6474852d5 DebugData: Add function for getting register state 2026-04-28 19:57:48 -04:00
ZiemasandTy a2ac576711 DebugData: Add struct for the saved thread context 2026-04-28 19:57:48 -04:00
ZiemasandTy 1b6924a033 DebugData: Rename EE sleep state to match IOP 2026-04-28 19:57:48 -04:00
ZiemasandTy 8e5e053b81 DebugData: Fix EE thread struct 2026-04-28 19:57:48 -04:00
62 changed files with 1257 additions and 827 deletions
@@ -27,7 +27,7 @@ LIBJPEGTURBO=3.1.4.1
LIBPNG=1.6.56
LIBWEBP=1.6.0
NVENC=13.0.19.0
SDL=SDL3-3.4.4
SDL=SDL3-3.4.8
LZ4=1.10.0
VULKAN=1.4.328.1
ZSTD=1.5.7
@@ -60,7 +60,7 @@ b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
f7d8bf1601b7804f583a254ab343a6549ca6cf27d255c302c47af2d9d36a6f18 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864 $SDL.tar.gz
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
9ce32d4a2763a2ac5f258726ba2f49e9011327c1ee8c30862a32d0f30889fbe8 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.4.4.tar.gz",
"sha256": "ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864"
"url": "https://libsdl.org/release/SDL3-3.4.8.tar.gz",
"sha256": "e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba"
}
],
"cleanup": [
@@ -47,7 +47,7 @@ QT=6.11.0
QTAPNG=1.3.0
FREETYPE=2.14.3
SDL=SDL3-3.4.4
SDL=SDL3-3.4.8
HARFBUZZ=14.0.0
ZSTD=1.5.7
LZ4=1.10.0
@@ -93,7 +93,7 @@ cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everyw
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864 $SDL.tar.gz
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
f29db9470e0ca5cef484e04e27baeec233aa428e8fdabe9e51b0f706c0809d24 harfbuzz-$HARFBUZZ.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
@@ -24,7 +24,7 @@ QT=6.11.0
QTAPNG=1.3.0
FREETYPE=2.14.3
SDL=SDL3-3.4.4
SDL=SDL3-3.4.8
HARFBUZZ=14.0.0
ZSTD=1.5.7
LZ4=1.10.0
@@ -69,7 +69,7 @@ cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everyw
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864 $SDL.tar.gz
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
f29db9470e0ca5cef484e04e27baeec233aa428e8fdabe9e51b0f706c0809d24 harfbuzz-$HARFBUZZ.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
@@ -60,7 +60,7 @@ set QTAPNG=1.3.0
set FREETYPE=2.14.3
set HARFBUZZ=14.0.0
set SDL=SDL3-3.4.4
set SDL=SDL3-3.4.8
set LIBJPEGTURBO=3.1.4.1
set LIBPNG=1656
set LIBPNGLONG=1.6.56
@@ -94,7 +94,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 9ce32d4a2763a2ac5f258726ba2f49e9011327c1ee8c30862a32d0f30889fbe8 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 6bd4fbb665f77899a488b381c5b6e9681fc57c60b669738f985fea714f3456c5 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
@@ -77,7 +77,7 @@ set LIBX264=b35605ace3ddf7c1a5d67a2eb553f034aef41d55
set FREETYPE=2.14.3
set HARFBUZZ=14.0.0
set SDL=SDL3-3.4.4
set SDL=SDL3-3.4.8
set LIBJPEGTURBO=3.1.4.1
set LIBPNG=1656
set LIBPNGLONG=1.6.56
@@ -121,7 +121,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 9ce32d4a2763a2ac5f258726ba2f49e9011327c1ee8c30862a32d0f30889fbe8 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 6bd4fbb665f77899a488b381c5b6e9681fc57c60b669738f985fea714f3456c5 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
+8 -1
View File
@@ -31969,6 +31969,9 @@ SLKA-25176:
name: "펌프 잇 업 익시드"
name-en: "Pump It Up - Exceed"
region: "NTSC-K"
gsHWFixes:
cpuSpriteRenderBW: 4 # Fixes graphical corruption in musical notes.
cpuSpriteRenderLevel: 2 # Needed for above.
SLKA-25180:
name: "스플린터 셀 - 판도라 투머로우"
name-en: "Tom Clancy's Splinter Cell - Pandora Tomorrow"
@@ -44039,7 +44042,7 @@ SLPM-65495:
SLPM-65496:
name: "ハイパーストリートファイターⅡ アニバーサリーエディション"
name-sort: "はいぱーすとりーとふぁいたー2 あにばーさりーえでぃしょん"
name-en: "Hyper Street Fighter"
name-en: "Hyper Street Fighter II - The Anniversary Edition"
region: "NTSC-J"
SLPM-65497:
name: "ワールドサッカーウイニングイレブン7 インターナショナル プレミアムパッケージ supported by アディダス"
@@ -54506,6 +54509,7 @@ SLPM-74405:
region: "NTSC-J"
gameFixes:
- EETimingHack # Stops crash after intro movie.
- InstantDMAHack # Stops hang when entering Sword Select screen.
SLPM-74406:
name: "WRC WORLD RALLY CHAMPIONSHIP [PlayStation2 the Best]"
name-sort: "WRC ~わーるど らりー ちゃんぴおんしっぷ~ [PlayStation2 the Best]"
@@ -70115,6 +70119,9 @@ SLUS-21131:
name: "Pump It Up - Exceed"
region: "NTSC-U"
compat: 5
gsHWFixes:
cpuSpriteRenderBW: 4 # Fixes graphical corruption in musical notes.
cpuSpriteRenderLevel: 2 # Needed for above.
SLUS-21132:
name: "Stella Deus - The Gate of Eternity"
region: "NTSC-U"
+5
View File
@@ -683,6 +683,7 @@
03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000bd12000010d1000000000000,Sanwa 4Button,a:b0,b:b1,x:b2,y:b3,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Windows,
03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows,
03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,
@@ -1537,6 +1538,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000853200000503000000010000,Nacon MGX Pro,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
0300000085320000170d000011010000,Nacon Revolution 5 Pro,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
0300000085320000190d000011010000,Nacon Revolution 5 Pro,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
0300000085320000180d000011010000,Nacon Revolution 5 Pro,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
0300000085320000160d000011010000,Nacon Revolution 5 Pro,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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,
@@ -1794,6 +1797,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,
030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000f51000001370000000010000,Turtle Beach ReactR,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
03000000f51000001370000000000000,Turtle Beach ReactR,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
03000000100800000100000010010000,Twin PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux,
03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+4
View File
@@ -266,6 +266,10 @@ float4 sample_c_af(float2 uv, float uv_w)
// HW sampler will reject bad UVs, match that here.
uv = any(isnan(uv) | isinf(uv)) ? float2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
float2 sz;
+4
View File
@@ -215,6 +215,10 @@ vec4 sample_c_af(vec2 uv, float uv_w)
// HW sampler will reject bad UVs, match that here.
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
vec2 sz = textureSize(TextureSampler, 0);
+4
View File
@@ -555,6 +555,10 @@ vec4 sample_c_af(vec2 uv, float uv_w)
// HW sampler will reject bad UVs, match that here.
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
vec2 sz = textureSize(Texture, 0);
+6
View File
@@ -799,6 +799,12 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
s_perf_enable = true;
continue;
}
else if (CHECK_ARG("-debugdevice"))
{
Console.WriteLn("Enable debug device");
s_settings_interface.SetBoolValue("EmuCore/GS", "UseDebugDevice", true);
continue;
}
else if (CHECK_ARG("--"))
{
no_more_args = true;
+4 -5
View File
@@ -450,7 +450,7 @@ void DebuggerWindow::onStepInto()
cpu->resumeCpu();
});
repaint();
update();
}
void DebuggerWindow::onStepOver()
@@ -500,7 +500,7 @@ void DebuggerWindow::onStepOver()
cpu->resumeCpu();
});
this->repaint();
update();
}
void DebuggerWindow::onStepOut()
@@ -525,8 +525,7 @@ void DebuggerWindow::onStepOut()
cpu->getPC(),
cpu->getRegister(0, 31),
cpu->getRegister(0, 29),
thread->EntryPoint(),
thread->StackTop());
thread->EntryPoint());
break;
}
}
@@ -541,7 +540,7 @@ void DebuggerWindow::onStepOut()
cpu->resumeCpu();
});
this->repaint();
update();
}
void DebuggerWindow::changeEvent(QEvent* event)
+9 -9
View File
@@ -86,7 +86,7 @@ bool DisassemblyView::fromJson(const JsonValueWrapper& json)
if (show_instruction_bytes != json.value().MemberEnd() && show_instruction_bytes->value.IsBool())
m_showInstructionBytes = show_instruction_bytes->value.GetBool();
repaint();
update();
return true;
}
@@ -222,7 +222,7 @@ void DisassemblyView::contextRunToCursor()
void DisassemblyView::contextJumpToCursor()
{
cpu().setPc(m_selectedAddressStart);
this->repaint();
update();
}
void DisassemblyView::contextToggleBreakpoint()
@@ -378,7 +378,7 @@ void DisassemblyView::contextRestoreFunction()
void DisassemblyView::contextShowInstructionBytes()
{
m_showInstructionBytes = !m_showInstructionBytes;
this->repaint();
update();
}
QString DisassemblyView::GetLineDisasm(u32 address)
@@ -610,7 +610,7 @@ void DisassemblyView::mousePressEvent(QMouseEvent* event)
m_selectedAddressEnd = selectedAddress;
}
}
this->repaint();
update();
}
}
@@ -637,7 +637,7 @@ void DisassemblyView::wheelEvent(QWheelEvent* event)
{
m_visibleStart -= 4;
}
this->repaint();
update();
}
void DisassemblyView::keyPressEvent(QKeyEvent* event)
@@ -709,7 +709,7 @@ void DisassemblyView::keyPressEvent(QKeyEvent* event)
break;
}
this->repaint();
update();
}
void DisassemblyView::openContextMenu(QPoint pos)
@@ -1014,9 +1014,9 @@ void DisassemblyView::gotoAddress(u32 address, bool should_set_focus)
m_selectedAddressStart = destAddress;
m_selectedAddressEnd = destAddress;
this->repaint();
update();
if (should_set_focus)
this->setFocus();
setFocus();
}
void DisassemblyView::toggleBreakpoint(u32 address)
@@ -1034,7 +1034,7 @@ void DisassemblyView::toggleBreakpoint(u32 address)
QtHost::RunOnUIThread([view, cpu]() {
BreakpointModel::getInstance(*cpu)->refreshData();
if (view)
view->repaint();
view->update();
});
});
}
@@ -27,7 +27,6 @@ MemorySearchView::MemorySearchView(const DebuggerViewParameters& parameters)
: DebuggerView(parameters, MONOSPACE_FONT)
{
m_ui.setupUi(this);
this->repaint();
m_ui.listSearchResults->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.btnSearch, &QPushButton::clicked, this, &MemorySearchView::onSearchButtonClicked);
+7 -7
View File
@@ -741,7 +741,7 @@ bool MemoryView::fromJson(const JsonValueWrapper& json)
if (little_endian != json.value().MemberEnd() && little_endian->value.IsBool())
m_table.SetLittleEndian(little_endian->value.GetBool());
repaint();
update();
return true;
}
@@ -764,7 +764,7 @@ void MemoryView::mousePressEvent(QMouseEvent* event)
return;
m_table.SelectAt(event->pos());
repaint();
update();
}
void MemoryView::openContextMenu(QPoint pos)
@@ -859,7 +859,7 @@ void MemoryView::openContextMenu(QPoint pos)
menu->popup(this->mapToGlobal(pos));
this->repaint();
update();
return;
}
@@ -934,7 +934,7 @@ void MemoryView::wheelEvent(QWheelEvent* event)
{
m_table.UpdateStartAddress(m_table.startAddress - 0x10);
}
this->repaint();
update();
}
void MemoryView::keyPressEvent(QKeyEvent* event)
@@ -954,7 +954,7 @@ void MemoryView::keyPressEvent(QKeyEvent* event)
break;
}
}
this->repaint();
update();
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
}
@@ -962,8 +962,8 @@ void MemoryView::gotoAddress(u32 address)
{
m_table.UpdateStartAddress(address & ~0xF);
m_table.selectedAddress = address;
this->repaint();
this->setFocus();
update();
setFocus();
}
#include "moc_MemoryView.cpp"
+7 -6
View File
@@ -36,7 +36,7 @@ RegisterView::RegisterView(const DebuggerViewParameters& parameters)
ui.registerTabs->addTab(cpu().getRegisterCategoryName(i));
}
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { this->repaint(); });
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { update(); });
receiveEvent<DebuggerEvents::Refresh>([this](const DebuggerEvents::Refresh& event) -> bool {
update();
@@ -69,7 +69,7 @@ bool RegisterView::fromJson(const JsonValueWrapper& json)
if (show_fpr_float != json.value().MemberEnd() && show_fpr_float->value.IsBool())
m_showFPRFloat = show_fpr_float->value.GetBool();
repaint();
update();
return true;
}
@@ -209,7 +209,8 @@ void RegisterView::mousePressEvent(QMouseEvent* event)
}
}
}
this->repaint();
update();
}
void RegisterView::wheelEvent(QWheelEvent* event)
@@ -223,7 +224,7 @@ void RegisterView::wheelEvent(QWheelEvent* event)
m_rowStart -= 1;
}
this->repaint();
update();
}
void RegisterView::mouseDoubleClickEvent(QMouseEvent* event)
@@ -259,7 +260,7 @@ void RegisterView::customMenuRequested(QPoint pos)
action->setChecked(m_showFPRFloat);
connect(action, &QAction::triggered, this, [this]() {
m_showFPRFloat = !m_showFPRFloat;
repaint();
update();
});
menu->addSeparator();
@@ -272,7 +273,7 @@ void RegisterView::customMenuRequested(QPoint pos)
action->setChecked(m_showVU0FFloat);
connect(action, &QAction::triggered, this, [this]() {
m_showVU0FFloat = !m_showVU0FFloat;
repaint();
update();
});
menu->addSeparator();
+1 -2
View File
@@ -111,8 +111,7 @@ void StackModel::refreshData()
{
if (thread->Status() == ThreadStatus::THS_RUN)
{
m_stackFrames = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29),
thread->EntryPoint(), thread->StackTop());
m_stackFrames = m_cpu.StackTrace(*thread);
break;
}
}
-2
View File
@@ -71,8 +71,6 @@ private:
//: Refers to a Thread Wait State in the Debugger.
{WaitState::NONE, tr("NONE")},
//: Refers to a Thread Wait State in the Debugger.
{WaitState::WAKEUP_REQ, tr("WAKEUP REQUEST")},
//: Refers to a Thread Wait State in the Debugger.
{WaitState::SEMA, tr("SEMAPHORE")},
//: Refers to a Thread Wait State in the Debugger.
{WaitState::SLEEP, tr("SLEEP")},
+39 -1
View File
@@ -873,6 +873,26 @@ void MainWindow::saveStateToConfig()
changed = true;
}
if (Host::ContainsBaseSettingValue("UI", "MainWindowMaximized"))
{
const bool maximized = Host::GetBaseBoolSettingValue("UI", "MainWindowMaximized");
if (maximized != isMaximized())
{
Host::SetBaseBoolSettingValue("UI", "MainWindowMaximized", isMaximized());
changed = true;
}
}
if (Host::ContainsBaseSettingValue("UI", "MainWindowFullscreen"))
{
const bool fullscreen = Host::GetBaseBoolSettingValue("UI", "MainWindowFullscreen");
if (fullscreen != isFullScreen())
{
Host::SetBaseBoolSettingValue("UI", "MainWindowFullscreen", isFullScreen());
changed = true;
}
}
if (changed)
Host::CommitBaseSettingChanges();
}
@@ -901,6 +921,24 @@ void MainWindow::restoreStateFromConfig()
m_ui.actionViewStatusBar->setChecked(!m_ui.statusBar->isHidden());
}
}
{
if (Host::ContainsBaseSettingValue("UI", "MainWindowMaximized"))
{
const bool maximized = Host::GetBaseBoolSettingValue("UI", "MainWindowMaximized");
if (maximized)
showMaximized();
}
}
{
if (Host::ContainsBaseSettingValue("UI", "MainWindowFullscreen"))
{
const bool fullscreen = Host::GetBaseBoolSettingValue("UI", "MainWindowFullscreen");
if (fullscreen)
showFullScreen();
}
}
}
void MainWindow::updateEmulationActions(bool starting, bool running, bool stopping)
@@ -1714,7 +1752,7 @@ void MainWindow::onStartFullscreenUITriggered()
void MainWindow::onFullscreenUIStateChange(bool running)
{
m_ui.actionStartFullscreenUI->setText(running ? tr("Stop Big Picture Mode") : tr("Start Big Picture Mode"));
m_ui.actionStartFullscreenUI->setText(running ? tr("Stop Big Picture Mode") : tr("Start Big Picture &Mode"));
m_ui.actionToolbarStartFullscreenUI->setText(running ? tr("Exit Big Picture", "In Toolbar") : tr("Big Picture", "In Toolbar"));
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -876,7 +876,7 @@ static __fi void rcntEndGate(bool isVblank, u64 sCycle)
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
counters[i].count = 0;
counters[i].target &= 0xffff;
counters[i].startCycle = sCycle & ~(counters[i].rate - 1);
counters[i].startCycle = sCycle & ~(static_cast<u64>(counters[i].rate - 1));
break;
}
}
+4 -3
View File
@@ -49,7 +49,8 @@ std::vector<std::unique_ptr<BiosThread>> getIOPThreads()
return {};
}
data.stackTop = iopMemRead32(item + 0x3c);
data.stacMem = iopMemRead32(item + 0x3c);
data.stackSize = iopMemRead32(item + 0x40);
data.status = iopMemRead8(item + 0xc);
data.tid = iopMemRead16(item + 0xa);
data.entrypoint = iopMemRead32(item + 0x38);
@@ -57,9 +58,9 @@ std::vector<std::unique_ptr<BiosThread>> getIOPThreads()
data.waitId = iopMemRead32(item + 0x20);
data.initPriority = iopMemRead16(item + 0xe);
data.SavedSP = iopMemRead32(item + 0x10);
data.regCtx = iopMemRead32(item + 0x10);
data.PC = iopMemRead32(data.SavedSP + 0x8c);
data.PC = iopMemRead32(data.regCtx + 0x8c);
threads.emplace_back(std::make_unique<IOPThread>(data));
+29 -16
View File
@@ -18,26 +18,37 @@ enum class ThreadStatus
THS_DORMANT = 0x10,
};
struct EEInternalCtx
{
u32 sa;
u32 fcsr;
u32 float_thing;
u32 unk;
// gpr excluding $zero
// k0/k1 contains hi, hi1, lo, lo1
u128 gpr[31];
float fpr[32];
};
struct EEInternalThread
{ // internal struct
u32 prev;
u32 next;
int status;
u32 entry;
u32 stack;
u32 resumeAddr; // address to return to when switching
u32 regCtx; // points to the saved regs on stack
u32 gpReg;
short currentPriority;
short initPriority;
short currentPriority;
int waitType;
int semaId;
int wakeupCount;
int attr;
int option;
u32 entry_init;
u32 entry;
int argc;
u32 argstring;
u32 stack_bottom; //FIXME
u32 stackMem;
int stackSize;
u32 root;
u32 heap_base;
@@ -48,8 +59,9 @@ struct IOPInternalThread
{
u32 tid;
u32 PC;
u32 stackTop;
u32 SavedSP;
u32 stacMem;
u32 stackSize;
u32 regCtx;
u32 status;
u32 entrypoint;
u32 waitstate;
@@ -71,14 +83,13 @@ enum class IOPWaitStatus
enum class EEWaitStatus
{
WAIT_NONE = 0,
WAIT_WAKEUP_REQ = 1,
WAIT_SLEEP = 1,
WAIT_SEMA = 2,
};
enum class WaitState
{
NONE,
WAKEUP_REQ,
SEMA,
SLEEP,
DELAY,
@@ -98,8 +109,10 @@ public:
[[nodiscard]] virtual WaitState Wait() const = 0;
[[nodiscard]] virtual u32 WaitId() const = 0;
[[nodiscard]] virtual u32 EntryPoint() const = 0;
[[nodiscard]] virtual u32 StackTop() const = 0;
[[nodiscard]] virtual u32 Priority() const = 0;
// Only call RegCtx on threads that aren't running
[[nodiscard]] virtual u32 RegCtx() const = 0;
};
class EEThread : public BiosThread
@@ -113,7 +126,7 @@ public:
~EEThread() override = default;
[[nodiscard]] u32 TID() const override { return tid; };
[[nodiscard]] u32 PC() const override { return data.entry; };
[[nodiscard]] u32 PC() const override { return data.resumeAddr; };
[[nodiscard]] ThreadStatus Status() const override { return static_cast<ThreadStatus>(data.status); };
[[nodiscard]] WaitState Wait() const override
{
@@ -122,17 +135,17 @@ public:
{
case EEWaitStatus::WAIT_NONE:
return WaitState::NONE;
case EEWaitStatus::WAIT_WAKEUP_REQ:
return WaitState::WAKEUP_REQ;
case EEWaitStatus::WAIT_SLEEP:
return WaitState::SLEEP;
case EEWaitStatus::WAIT_SEMA:
return WaitState::SEMA;
}
return WaitState::NONE;
};
[[nodiscard]] u32 WaitId() const override { return data.semaId; };
[[nodiscard]] u32 EntryPoint() const override { return data.entry_init; };
[[nodiscard]] u32 StackTop() const override { return data.stack; };
[[nodiscard]] u32 EntryPoint() const override { return data.entry; };
[[nodiscard]] u32 Priority() const override { return data.currentPriority; };
[[nodiscard]] u32 RegCtx() const override { return data.regCtx; };
private:
u32 tid;
@@ -175,8 +188,8 @@ public:
};
[[nodiscard]] u32 WaitId() const override { return data.waitId; };
[[nodiscard]] u32 EntryPoint() const override { return data.entrypoint; };
[[nodiscard]] u32 StackTop() const override { return data.stackTop; };
[[nodiscard]] u32 Priority() const override { return data.initPriority; };
[[nodiscard]] u32 RegCtx() const override { return data.regCtx; };
private:
IOPInternalThread data;
+33
View File
@@ -708,6 +708,23 @@ std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() co
return getEEThreads();
}
std::vector<MipsStackWalk::StackFrame> R5900DebugInterface::StackTrace(const BiosThread& thread)
{
if (thread.Status() == ThreadStatus::THS_RUN)
{
return MipsStackWalk::Walk(this, getPC(), getRegister(0, 31), getRegister(0, 29),
thread.EntryPoint());
}
EEInternalCtx* ctx = static_cast<EEInternalCtx*>(PSM(thread.RegCtx()));
u32 pc = thread.PC();
// $zero is not in the array so subtract 1
u32 ra = ctx->gpr[31 - 1]._u32[0];
u32 sp = ctx->gpr[29 - 1]._u32[0];
return MipsStackWalk::Walk(this, pc, ra, sp, thread.EntryPoint());
}
std::vector<IopMod> R5900DebugInterface::GetModuleList() const
{
return {};
@@ -1049,6 +1066,22 @@ std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() co
return getIOPThreads();
}
std::vector<MipsStackWalk::StackFrame> R3000DebugInterface::StackTrace(const BiosThread& thread)
{
if (thread.Status() == ThreadStatus::THS_RUN)
{
return MipsStackWalk::Walk(this, getPC(), getRegister(0, 31), getRegister(0, 29),
thread.EntryPoint());
}
u32 p = thread.RegCtx();
u32 pc = Read32(p + 0x8c);
u32 ra = Read32(p + 0x7c);
u32 sp = Read32(p + 0x74);
return MipsStackWalk::Walk(this, pc, ra, sp, thread.EntryPoint());
}
std::vector<IopMod> R3000DebugInterface::GetModuleList() const
{
return getIOPModules();
+4
View File
@@ -7,6 +7,7 @@
#include "ExpressionParser.h"
#include "SymbolGuardian.h"
#include "SymbolImporter.h"
#include "MipsStackWalk.h"
#include "common/MemoryInterface.h"
@@ -72,6 +73,7 @@ public:
virtual SymbolGuardian& GetSymbolGuardian() const = 0;
virtual SymbolImporter* GetSymbolImporter() const = 0;
virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
virtual std::vector<MipsStackWalk::StackFrame> StackTrace(const BiosThread& thread) = 0;
virtual std::vector<IopMod> GetModuleList() const = 0;
bool isAlive();
@@ -135,6 +137,7 @@ public:
SymbolGuardian& GetSymbolGuardian() const override;
SymbolImporter* GetSymbolImporter() const override;
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::vector<MipsStackWalk::StackFrame> StackTrace(const BiosThread& thread) override;
std::vector<IopMod> GetModuleList() const override;
std::string disasm(u32 address, bool simplify) override;
@@ -180,6 +183,7 @@ public:
SymbolGuardian& GetSymbolGuardian() const override;
SymbolImporter* GetSymbolImporter() const override;
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::vector<MipsStackWalk::StackFrame> StackTrace(const BiosThread& thread) override;
std::vector<IopMod> GetModuleList() const override;
std::string disasm(u32 address, bool simplify) override;
+5 -2
View File
@@ -103,7 +103,10 @@ namespace MipsStackWalk
// TODO: Check if found entry is in the same symbol? Might be wrong sometimes...
int ra_offset = -1;
const u32 start = frame.pc;
// The instruction pointed to by pc hasn't been executed yet,
// so we don't want to consider it here
const u32 start = frame.pc - 4;
u32 stop = entry;
if (entry == INVALIDTARGET)
@@ -183,7 +186,7 @@ namespace MipsStackWalk
return ScanForEntry(cpu, frame, newPossibleEntry, ra);
}
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop)
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry)
{
std::vector<StackFrame> frames;
StackFrame current;
+1 -1
View File
@@ -20,5 +20,5 @@ namespace MipsStackWalk {
int stackSize;
};
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop);
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry);
};
+4 -4
View File
@@ -91,11 +91,11 @@ void GSDrawingContext::UpdateScissor()
scissor.in = rscissor + GSVector4i::cxpr(0, 0, 1, 1);
// Fixed-point scissor min/max, used for rejecting primitives which are entirely outside.
scissor.cull = rscissor.sll32<4>();
// Add half a pixel around the edges for upscaling and lines/points.
scissor.cull = rscissor.sll32<4>() + GSVector4i(-8, -8, 8, 8);
// Offset applied to vertices for culling, zw is for native resolution culling
// We want to round subpixels down, because at least one pixel gets filled per scanline.
scissor.xyof = GSVector4i::loadl(&XYOFFSET.U64).xyxy().sub32(GSVector4i::cxpr(0, 0, 15, 15));
// Offset applied to vertices for culling.
scissor.xyof = GSVector4i::loadl(&XYOFFSET.U64).xyxy();
}
GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear, bool mipmap) const
+58 -74
View File
@@ -1279,12 +1279,12 @@ void GSState::DumpTransferList(const std::string& filename)
(*file) << std::endl;
// clear, EE->GS, or GS->GS
(*file) << LIST_ITEM << "type: " << (transfer.zero_clear ? "clear" : ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) ? "EE_to_GS" : "GS_to_GS")) << std::endl;
(*file) << LIST_ITEM << "type: " << ((transfer.transfer_type == EEGS_TransferType::Clear) ? "clear" : ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) ? "EE_to_GS" : "GS_to_GS")) << std::endl;
// Dump BITBLTBUF
(*file) << INDENT << "BITBLTBUF: " << OPEN_MAP;
const bool gs_to_gs = (transfer.transfer_type == EEGS_TransferType::GS_to_GS) && !transfer.zero_clear;
const bool gs_to_gs = (transfer.transfer_type == EEGS_TransferType::GS_to_GS) && transfer.transfer_type != EEGS_TransferType::Clear;
if (gs_to_gs)
{
@@ -1330,11 +1330,11 @@ void GSState::DumpTransferImages()
const GSUploadQueue& transfer = m_draw_transfers[i];
std::string filename;
if ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) || transfer.zero_clear)
if ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) || transfer.transfer_type == EEGS_TransferType::Clear)
{
// clear or EE->GS: only the destination info is relevant.
filename = GetDrawDumpPath("%05lld_transfer%02d_%s_%04x_%d_%s_%d_%d_%d_%d.png",
s_n, transfer_n++, (transfer.zero_clear ? "clear" : "EE_to_GS"), transfer.blit.DBP, transfer.blit.DBW,
s_n, transfer_n++, ((transfer.transfer_type == EEGS_TransferType::Clear) ? "clear" : "EE_to_GS"), transfer.blit.DBP, transfer.blit.DBW,
GSUtil::GetPSMName(transfer.blit.DPSM), transfer.rect.x, transfer.rect.y, transfer.rect.z, transfer.rect.w);
}
else
@@ -2403,7 +2403,7 @@ void GSState::FlushWrite()
if (m_draw_transfers.size() > 0 && m_tr.m_blit.DBP == m_draw_transfers.back().blit.DBP)
{
m_draw_transfers.back().rect = r;
m_draw_transfers.back().rect = m_draw_transfers.back().rect.runion(r);
}
}
}
@@ -2666,7 +2666,6 @@ void GSState::FlushPrim()
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[i];
GSVector4i v = vert_ptr[1];
v = v.xxxx().u16to32().sub32(m_xyof);
v = v.blend32<12>(v.sra32<4>());
m_vertex->xy[i & 3] = v;
m_vertex->xy_tail = unused;
}
@@ -2912,12 +2911,11 @@ void GSState::Write(const u8* mem, int len)
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
transfer.draw = s_n;
transfer.zero_clear = false;
m_draw_transfers.push_back(transfer);
}
else
{
const GSUploadQueue new_transfer = {blit, r, s_n, false, EEGS_TransferType::EE_to_GS};
const GSUploadQueue new_transfer = {blit, s_n, r, EEGS_TransferType::EE_to_GS};
m_draw_transfers.push_back(new_transfer);
}
@@ -3112,12 +3110,11 @@ void GSState::Move()
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
transfer.draw = s_n;
transfer.zero_clear = false;
m_draw_transfers.push_back(transfer);
}
else
{
const GSUploadQueue new_transfer = {m_env.BITBLTBUF, r, s_n, false, EEGS_TransferType::GS_to_GS};
const GSUploadQueue new_transfer = {m_env.BITBLTBUF, s_n, r, EEGS_TransferType::GS_to_GS};
m_draw_transfers.push_back(new_transfer);
}
@@ -3790,10 +3787,8 @@ void GSState::UpdateContext()
void GSState::UpdateScissor()
{
m_scissor_cull_min = m_context->scissor.cull.xyxy();
m_scissor_cull_max = m_context->scissor.cull.zwzw();
m_xyof = m_context->scissor.xyof;
m_scissor_invalid = !m_context->scissor.in.gt32(m_context->scissor.in.zwzw()).allfalse();
m_scissor_invalid = m_context->scissor.in.rempty();
}
void GSState::UpdateVertexKick()
@@ -5841,6 +5836,7 @@ template <u32 prim, bool auto_flush>
__forceinline void GSState::VertexKick(u32 skip)
{
constexpr u32 n = NumIndicesForPrim(prim);
constexpr int primclass = GSUtil::GetPrimClass(prim);
static_assert(n > 0);
pxAssert(m_vertex->tail < m_vertex->maxcount + 3);
@@ -5884,11 +5880,7 @@ __forceinline void GSState::VertexKick(u32 skip)
// We maintain the X/Y coordinates for the last 4 vertices, as well as the head for triangle fans, so we can compute
// the min/max, and cull degenerate triangles, which saves draws in some cases. Why 4? Mod 4 is cheaper than Mod 3.
// These vertices are a full vector containing <X_Fixed_Point, Y_Fixed_Point, X_Integer, Y_Integer>. We use the
// integer coordinates for culling at native resolution, and the fixed point for all others. The XY offset has to be
// applied, then we split it into the fixed/integer portions.
const GSVector4i xy_ofs = new_v1.xxxx().u16to32().sub32(m_xyof);
const GSVector4i xy = xy_ofs.blend32<12>(xy_ofs.sra32<4>());
const GSVector4i xy = new_v1.xxxx().u16to32().sub32(m_xyof);
m_vertex->xy[xy_tail & 3] = xy;
// Backup head for triangle fans so we can read it later, otherwise it'll get lost after the 4th vertex.
@@ -5918,73 +5910,66 @@ __forceinline void GSState::VertexKick(u32 skip)
// Skip draws when scissor is out of range (i.e. bottom-right is less than top-left), since everything will get clipped.
skip |= static_cast<u32>(m_scissor_invalid);
GSVector4i pmin, pmax;
GSVector4i bbox;
if (skip == 0)
{
const GSVector4i v0 = m_vertex->xy[(xy_tail - 1) & 3];
const GSVector4i v1 = m_vertex->xy[(xy_tail - 2) & 3];
const GSVector4i v2 = (prim == GS_TRIANGLEFAN) ? m_vertex->xyhead : m_vertex->xy[(xy_tail - 3) & 3];
switch (prim)
if constexpr (n == 1)
{
case GS_POINTLIST:
pmin = v0;
pmax = v0;
break;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_SPRITE:
pmin = v0.min_i32(v1);
pmax = v0.max_i32(v1);
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
pmin = v0.min_i32(v1.min_i32(v2));
pmax = v0.max_i32(v1.max_i32(v2));
break;
default:
break;
bbox = v0;
}
else if constexpr (n == 2)
{
bbox = v0.runion(v1);
}
else if constexpr (n == 3)
{
bbox = v0.runion(v1).runion(v2);
}
GSVector4i test = pmax.lt32(m_scissor_cull_min) | pmin.gt32(m_scissor_cull_max);
switch (prim)
if constexpr (primclass == GS_TRIANGLE_CLASS || primclass == GS_SPRITE_CLASS)
{
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
case GS_SPRITE:
if (m_nativeres)
{
// Discard degenerate triangles which don't cover at least one pixel. Since the vertices are in native
// resolution space, we can use the integer locations. When upscaling, we can't, because a primitive which
// does not span a single pixel at 1x may span multiple pixels at higher resolutions.
const GSVector4i degen_test = pmin.eq32(pmax);
test |= m_nativeres ? degen_test.zwzw() : degen_test;
// For triangles and sprites at native res take the interior pixel centers.
const GSVector4i interior = (bbox + GSVector4i(0xF, 0xF, -1, -1)) & GSVector4i(~0xF);
bbox = interior + GSVector4i(0, 0, 1, 1); // +1 to bottom/right so empty test works correctly.
}
else
{
// For upscaling, remove bottom/right subtexels.
bbox -= ((bbox & GSVector4i(0xF)) == GSVector4i(0)) & GSVector4i(0, 0, 1, 1);
}
// For AA1 triangles and lines, expand the bounds by 1 pixel on all sides.
// Note: redundant check for the AA1 flag to avoid calling a function if not needed.
if (PRIM->AA1 && IsCoverageAlphaSupported())
{
bbox += GSVector4i(-0x10, -0x10, 0x10, 0x10);
}
break;
default:
break;
}
switch (prim)
// Do scissor test.
const GSVector4i bbox_ex = bbox + GSVector4i(0, 0, 1, 1); // Exclusive coords for the scissor test.
const GSVector4i& scissor = m_context->scissor.cull;
u32 test = static_cast<u32>(!bbox_ex.rintersects(scissor));
// Test for empty bbox.
if constexpr (primclass == GS_TRIANGLE_CLASS || primclass == GS_SPRITE_CLASS)
{
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
test = (test | v0.eq64(v1)) | (v1.eq64(v2) | v0.eq64(v2));
break;
default:
break;
test |= static_cast<u32>(bbox.rempty());
}
#ifndef ARCH_ARM64
// We only care about the xy passing the skip test. zw is the offset coordinates for native culling.
skip |= test.mask() & 0xff;
#else
// mask() is slow on ARM, so just pull the bits out instead, thankfully we only care about the first 4 bytes.
skip |= (static_cast<u64>(test.extract64<0>()) & UINT64_C(0x8080808080808080)) != 0;
#endif
// Test for degenerate triangle.
if constexpr (primclass == GS_TRIANGLE_CLASS)
{
test |= static_cast<u32>(v0.eq(v1)) | static_cast<u32>(v1.eq(v2)) | static_cast<u32>(v0.eq(v2));
}
skip |= test;
}
if (skip != 0)
@@ -6094,13 +6079,12 @@ __forceinline void GSState::VertexKick(u32 skip)
ASSUME(0);
}
// Update rectangle for the current draw. We can use the re-integer coordinates from min/max here.
const GSVector4i draw_min = pmin.zwzw();
const GSVector4i draw_max = pmax;
if (m_index->tail != n)
temp_draw_rect = temp_draw_rect.min_i32(draw_min).blend32<12>(temp_draw_rect.max_i32(draw_max));
// Update rectangle for the current draw. Needs exclusive endpoints.
const GSVector4i draw_rect = bbox.sra32<4>() + GSVector4i(0, 0, 1, 1);
if (m_vertex->tail != n)
temp_draw_rect = temp_draw_rect.runion(draw_rect);
else
temp_draw_rect = draw_min.blend32<12>(draw_max);
temp_draw_rect = draw_rect;
temp_draw_rect = temp_draw_rect.rintersect(m_context->scissor.in);
constexpr u32 max_vertices = MaxVerticesForPrim(prim);
+3 -5
View File
@@ -131,8 +131,6 @@ protected:
GSVertex m_v = {};
float m_q = 1.0f;
GSVector4i m_scissor_cull_min = {};
GSVector4i m_scissor_cull_max = {};
GSVector4i m_xyof = {};
int m_used_buffers_idx = 0;
int m_current_buffer_idx = 0;
@@ -260,15 +258,15 @@ public:
{
EE_to_GS,
GS_to_GS,
GS_to_EE
GS_to_EE,
Clear
};
struct GSUploadQueue
{
GIFRegBITBLTBUF blit;
GSVector4i rect;
u64 draw;
bool zero_clear;
GSVector4i rect;
EEGS_TransferType transfer_type;
};
+24
View File
@@ -339,3 +339,27 @@ const char* GSUtil::GetPSMName(int psm)
}
return "BAD_PSM";
}
bool GSUtil::IsValidPSM(int psm)
{
switch (psm)
{
case PSMCT32:
case PSMCT24:
case PSMCT16:
case PSMCT16S:
case PSMT8:
case PSMT4:
case PSMT8H:
case PSMT4HL:
case PSMT4HH:
case PSMZ32:
case PSMZ24:
case PSMZ16:
case PSMZ16S:
case PSGPU24:
return true;
default:
return false;
}
}
+3
View File
@@ -6,6 +6,7 @@
#include "GS.h"
#include "GSRegs.h"
#include "GSPerfMon.h"
#include <climits>
class GSUtil
{
@@ -28,6 +29,8 @@ public:
static const char* GetACName(u32 ac);
static const char* GetPerfMonCounterName(GSPerfMon::counter_t counter, bool hw = true);
static bool IsValidPSM(int psm);
static const u32* HasSharedBitsPtr(u32 dpsm);
static bool HasSharedBits(u32 spsm, const u32* ptr);
static bool HasSharedBits(u32 spsm, u32 dpsm);
+58 -3
View File
@@ -1559,8 +1559,51 @@ static void DumpBlendMultipass(DrawConfigWriter& out, const GSHWDrawConfig::Blen
DumpBlendState(out.WithIndent(), bmp.blend);
}
template<typename T, typename U = int>
static void DumpVector4(DrawConfigWriter& out, const char* name, const T& val)
{
out.WriteLn("{}: [{}, {}, {}, {}]", name, val.x, val.y, val.z, val.w);
};
template<typename T>
static void DumpVector2(DrawConfigWriter& out, const char* name, const T& val)
{
out.WriteLn("{}: [{}, {}]", name, val.x, val.y);
};
static void DumpPSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::PSConstantBuffer& cb)
{
DumpVector4(out, "FogColor_AREF", cb.FogColor_AREF);
DumpVector4(out, "WH", cb.WH);
DumpVector4(out, "TA_MaxDepth_Af", cb.TA_MaxDepth_Af);
DumpVector4(out, "FbMask", cb.FbMask);
DumpVector4(out, "HalfTexel", cb.HalfTexel);
DumpVector4(out, "MinMax", cb.MinMax);
DumpVector4(out, "LODParams", cb.LODParams);
DumpVector4(out, "STRange", cb.STRange);
DumpVector4(out, "ChannelShuffle", cb.ChannelShuffle);
DumpVector2(out, "ChannelShuffleOffset", cb.ChannelShuffleOffset);
DumpVector2(out, "TCOffsetHack", cb.TCOffsetHack);
DumpVector2(out, "STScale", cb.STScale);
DumpVector4(out, "DitherMatrix_0", cb.DitherMatrix[0]);
DumpVector4(out, "DitherMatrix_1", cb.DitherMatrix[1]);
DumpVector4(out, "DitherMatrix_2", cb.DitherMatrix[2]);
DumpVector4(out, "DitherMatrix_3", cb.DitherMatrix[3]);
DumpVector4(out, "ScaleFactor", cb.ScaleFactor);
}
static void DumpVSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::VSConstantBuffer& cb)
{
DumpVector2(out, "vertex_scale", cb.vertex_scale);
DumpVector2(out, "vertex_offset", cb.vertex_offset);
DumpVector2(out, "texture_scale", cb.texture_scale);
DumpVector2(out, "texture_offset", cb.texture_offset);
DumpVector2(out, "point_size", cb.point_size);
DumpVector2(out, "max_depth", cb.max_depth);
}
static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp)
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp, bool cbvs, bool cbps)
{
out.WriteLn("topology: {} ({})", GetTopologyName(conf.topology), static_cast<u32>(conf.topology));
out.WriteLn("require_one_barrier: {}", conf.require_one_barrier);
@@ -1612,15 +1655,27 @@ static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
out.WriteLn("blend_multi_pass:");
DumpBlendMultipass(out.WithIndent(), conf.blend_multi_pass);
}
if (cbvs)
{
out.WriteLn("cb_vs:");
DumpVSConstantBuffer(out.WithIndent(), conf.cb_vs);
}
if (cbps)
{
out.WriteLn("cb_ps:");
DumpPSConstantBuffer(out.WithIndent(), conf.cb_ps);
}
}
void GSHWDrawConfig::DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp)
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp, bool cbvs, bool cbps)
{
if (FileSystem::ManagedCFilePtr file = FileSystem::OpenManagedCFile(path.c_str(), "w"))
{
DrawConfigWriter writer;
::DumpConfig(writer, conf, ps, vs, bs, dss, ss, asp, bmp);
::DumpConfig(writer, conf, ps, vs, bs, dss, ss, asp, bmp, cbvs, cbps);
fwrite(writer.buffer.data(), 1, writer.buffer.size(), file.get());
}
}
+2 -1
View File
@@ -879,7 +879,8 @@ struct alignas(16) GSHWDrawConfig
// Dumping
static void DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true);
bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true,
bool cbvs = true, bool cbps = true);
};
static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand)
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "GSDirtyRect.h"
#include <climits>
#include <vector>
GSDirtyRect::GSDirtyRect() :
+1
View File
@@ -4,6 +4,7 @@
#pragma once
#include "common/AlignedMalloc.h"
#include <climits>
template <class T>
struct Element
+1 -1
View File
@@ -718,7 +718,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
const GSTexture* current = g_gs_device->GetCurrent();
const GSVector2i internal_res = GetInternalResolution();
if (current->GetWidth() > internal_res.x || current->GetHeight() > internal_res.y)
if (current && (current->GetWidth() > internal_res.x || current->GetHeight() > internal_res.y))
g_gs_device->Resize(internal_res.x, internal_res.y);
}
+29 -5
View File
@@ -466,28 +466,52 @@ GSRendererType D3D::GetPreferredRenderer()
}
}
wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D_FEATURE_LEVEL feature_level, bool debug,
const char* D3D::ShaderModelToCacheString(D3D::ShaderModel shader_model)
{
switch (shader_model)
{
case ShaderModel::SM40:
return "sm40";
case ShaderModel::SM41:
return "sm41";
case ShaderModel::SM50:
return "sm50";
case ShaderModel::SM51:
return "sm51";
default:
return "unk";
}
}
wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D::ShaderModel shader_model, bool debug,
const std::string_view code, const D3D_SHADER_MACRO* macros /* = nullptr */,
const char* entry_point /* = "main" */)
{
const char* target;
switch (feature_level)
switch (shader_model)
{
case D3D_FEATURE_LEVEL_10_0:
case ShaderModel::SM40:
{
static constexpr std::array<const char*, 4> targets = {{"vs_4_0", "ps_4_0", "cs_4_0"}};
target = targets[static_cast<int>(type)];
}
break;
case D3D_FEATURE_LEVEL_11_0:
case ShaderModel::SM41:
{
static constexpr std::array<const char*, 4> targets = {{"vs_4_1", "ps_4_1", "cs_4_1"}};
target = targets[static_cast<int>(type)];
}
break;
case ShaderModel::SM50:
{
static constexpr std::array<const char*, 4> targets = {{"vs_5_0", "ps_5_0", "cs_5_0"}};
target = targets[static_cast<int>(type)];
}
break;
case D3D_FEATURE_LEVEL_11_1:
case ShaderModel::SM51:
default:
{
static constexpr std::array<const char*, 4> targets = {{"vs_5_1", "ps_5_1", "cs_5_1"}};
+11 -1
View File
@@ -63,6 +63,16 @@ namespace D3D
Compute
};
wil::com_ptr_nothrow<ID3DBlob> CompileShader(ShaderType type, D3D_FEATURE_LEVEL feature_level, bool debug,
enum class ShaderModel
{
SM40 = 0x40, // DX11 FL 10_0
SM41 = 0x41, // DX11 FL 10_1
SM50 = 0x50, // DX11 FL 11_0
SM51 = 0x51, // DX12
};
const char* ShaderModelToCacheString(ShaderModel shader_model);
wil::com_ptr_nothrow<ID3DBlob> CompileShader(ShaderType type, ShaderModel shader_model, bool debug,
const std::string_view code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
}; // namespace D3D
+20 -14
View File
@@ -56,12 +56,27 @@ bool D3D11ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
bool D3D11ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
{
m_feature_level = feature_level;
switch (feature_level)
{
case D3D_FEATURE_LEVEL_10_0:
m_shader_model = D3D::ShaderModel::SM40;
break;
case D3D_FEATURE_LEVEL_10_1:
m_shader_model = D3D::ShaderModel::SM41;
break;
case D3D_FEATURE_LEVEL_11_0:
m_shader_model = D3D::ShaderModel::SM50;
break;
default:
pxAssert(false);
return false;
}
m_debug = debug;
if (!GSConfig.DisableShaderCache)
{
const std::string base_filename = GetCacheBaseFileName(feature_level, debug);
const std::string base_filename = GetCacheBaseFileName(m_shader_model, debug);
const std::string index_filename = base_filename + ".idx";
const std::string blob_filename = base_filename + ".bin";
@@ -198,19 +213,10 @@ bool D3D11ShaderCache::ReadExisting(const std::string& index_filename, const std
return true;
}
std::string D3D11ShaderCache::GetCacheBaseFileName(D3D_FEATURE_LEVEL feature_level, bool debug)
std::string D3D11ShaderCache::GetCacheBaseFileName(D3D::ShaderModel shader_model, bool debug)
{
std::string base_filename = "d3d_shaders_";
switch (feature_level)
{
case D3D_FEATURE_LEVEL_11_0:
base_filename += "sm50";
break;
default:
base_filename += "unk";
break;
}
base_filename += D3D::ShaderModelToCacheString(shader_model);
if (debug)
base_filename += "_debug";
@@ -400,7 +406,7 @@ wil::com_ptr_nothrow<ID3DBlob> D3D11ShaderCache::CompileAndAddShaderBlob(const C
const std::string_view shader_code, const D3D_SHADER_MACRO* macros, const char* entry_point)
{
wil::com_ptr_nothrow<ID3DBlob> blob =
D3D::CompileShader(key.shader_type, m_feature_level, m_debug, shader_code, macros, entry_point);
D3D::CompileShader(key.shader_type, m_shader_model, m_debug, shader_code, macros, entry_point);
if (!blob)
return {};
+2 -3
View File
@@ -16,7 +16,6 @@ public:
D3D11ShaderCache();
~D3D11ShaderCache();
D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
bool UsingDebugShaders() const { return m_debug; }
bool Open(D3D_FEATURE_LEVEL feature_level, bool debug);
@@ -74,7 +73,7 @@ private:
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
static std::string GetCacheBaseFileName(D3D_FEATURE_LEVEL feature_level, bool debug);
static std::string GetCacheBaseFileName(D3D::ShaderModel shader_mode, bool debug);
static CacheIndexKey GetCacheKey(D3D::ShaderType type, const std::string_view shader_code,
const D3D_SHADER_MACRO* macros, const char* entry_point);
@@ -89,6 +88,6 @@ private:
CacheIndex m_index;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
D3D::ShaderModel m_shader_model = D3D::ShaderModel::SM50;
bool m_debug = false;
};
+62 -49
View File
@@ -57,7 +57,7 @@ GSDevice11::GSDevice11()
m_features.primitive_id = true;
m_features.texture_barrier = false;
m_features.multidraw_fb_copy = GSConfig.OverrideTextureBarriers != 0;
m_features.multidraw_fb_copy = false;
m_features.provoking_vertex_last = false;
m_features.point_expand = false;
m_features.line_expand = false;
@@ -68,7 +68,8 @@ GSDevice11::GSDevice11()
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = false;
m_features.depth_feedback = true;
m_features.depth_feedback = false;
m_features.aa1 = false;
}
GSDevice11::~GSDevice11() = default;
@@ -636,16 +637,19 @@ void GSDevice11::Destroy()
void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
{
// Check these first as others depend on them.
m_features.multidraw_fb_copy = GSConfig.OverrideTextureBarriers != 0;
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && m_feature_level >= D3D_FEATURE_LEVEL_11_0);
// Check all three formats, since the feature means any can be used.
m_features.dxt_textures = SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC1_UNORM) &&
SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC2_UNORM) &&
SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC3_UNORM);
m_features.bptc_textures = SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC7_UNORM);
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.cas_sharpening = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.test_and_sample_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.depth_feedback = m_features.feedback_loops();
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
m_max_texture_size = (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
@@ -2944,19 +2948,30 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
draw_ds = m_state.current_ds;
}
if (draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy) || (config.tex && config.tex == config.rt)))
const bool tex_is_fb = config.tex && config.tex == draw_rt;
const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT() || tex_is_fb;
const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT() || tex_is_fb;
if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
config.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
// Requires a copy of the RT.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);
if (!draw_rt_clone)
Console.Warning("D3D11: Failed to allocate temp texture for RT copy.");
}
if (draw_ds && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
m_features.depth_feedback && config.ps.IsFeedbackLoopDepth())
const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth();
const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth();
if (draw_ds && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
draw_ds_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_ds->GetFormat(), true);
if (!draw_ds_clone)
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
}
@@ -2969,7 +2984,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && need_barrier)
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
if (config.blend_multi_pass.enable)
@@ -2995,9 +3010,10 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
SetupPS(config.alpha_second_pass.ps, nullptr, config.sampler);
}
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.multidraw_fb_copy;
SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
false, config.alpha_second_pass.require_full_barrier);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
}
if (colclip_rt)
@@ -3021,58 +3037,55 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
{
if (draw_rt_clone || draw_ds_clone)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
#endif
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(2, draw_rt_clone);
if (config.tex && config.tex == draw_rt)
PSSetShaderResource(0, draw_rt_clone);
}
if (draw_ds_clone)
{
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
PSSetShaderResource(4, draw_ds_clone);
}
};
const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight());
if (full_barrier)
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(2, draw_rt_clone);
if (config.tex && config.tex == draw_rt)
PSSetShaderResource(0, draw_rt_clone);
}
if (draw_ds_clone)
{
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
PSSetShaderResource(4, draw_ds_clone);
}
};
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight());
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
if (full_barrier)
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
Draw(config, p, count);
p += count;
}
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
return;
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
Draw(config, p, count);
p += count;
}
// Optimization: For alpha second pass we can reuse the copy snapshot from the first pass.
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
return;
}
if (one_barrier)
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
Draw(config);
}
+13 -21
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-3.0+
#include "GS/Renderers/DX12/D3D12ShaderCache.h"
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GS.h"
#include "Config.h"
@@ -54,16 +53,18 @@ bool D3D12ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
source_length != key.source_length);
}
bool D3D12ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
bool D3D12ShaderCache::Open(D3D::ShaderModel shader_model, bool debug)
{
m_feature_level = feature_level;
// Only support SM5.1 for now, which is the minimum for D3D12.
pxAssert(shader_model >= D3D::ShaderModel::SM51);
m_shader_model = shader_model;
m_debug = debug;
bool result = true;
if (!GSConfig.DisableShaderCache)
{
const std::string base_shader_filename = GetCacheBaseFileName("shaders", feature_level, debug);
const std::string base_shader_filename = GetCacheBaseFileName("shaders", m_shader_model, debug);
const std::string shader_index_filename = base_shader_filename + ".idx";
const std::string shader_blob_filename = base_shader_filename + ".bin";
@@ -75,7 +76,7 @@ bool D3D12ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
if (result)
{
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", feature_level, debug);
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", m_shader_model, debug);
const std::string pipelines_index_filename = base_pipelines_filename + ".idx";
const std::string pipelines_blob_filename = base_pipelines_filename + ".bin";
@@ -133,7 +134,7 @@ void D3D12ShaderCache::InvalidatePipelineCache()
if (GSConfig.DisableShaderCache)
return;
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", m_feature_level, m_debug);
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", m_shader_model, m_debug);
const std::string pipelines_index_filename = base_pipelines_filename + ".idx";
const std::string pipelines_blob_filename = base_pipelines_filename + ".bin";
CreateNew(pipelines_index_filename, pipelines_blob_filename, m_pipeline_index_file, m_pipeline_blob_file);
@@ -253,21 +254,12 @@ bool D3D12ShaderCache::ReadExisting(const std::string& index_filename, const std
return true;
}
std::string D3D12ShaderCache::GetCacheBaseFileName(const std::string_view type, D3D_FEATURE_LEVEL feature_level, bool debug)
std::string D3D12ShaderCache::GetCacheBaseFileName(const std::string_view type, D3D::ShaderModel shader_model, bool debug)
{
std::string base_filename = "d3d12_";
base_filename += type;
base_filename += "_";
switch (feature_level)
{
case D3D_FEATURE_LEVEL_11_0:
base_filename += "sm50";
break;
default:
base_filename += "unk";
break;
}
base_filename += D3D::ShaderModelToCacheString(shader_model);
if (debug)
base_filename += "_debug";
@@ -497,15 +489,15 @@ D3D12ShaderCache::ComPtr<ID3DBlob> D3D12ShaderCache::CompileAndAddShaderBlob(
{
case EntryType::VertexShader:
blob =
D3D::CompileShader(D3D::ShaderType::Vertex, m_feature_level, m_debug, shader_code, macros, entry_point);
D3D::CompileShader(D3D::ShaderType::Vertex, m_shader_model, m_debug, shader_code, macros, entry_point);
break;
case EntryType::PixelShader:
blob =
D3D::CompileShader(D3D::ShaderType::Pixel, m_feature_level, m_debug, shader_code, macros, entry_point);
D3D::CompileShader(D3D::ShaderType::Pixel, m_shader_model, m_debug, shader_code, macros, entry_point);
break;
case EntryType::ComputeShader:
blob = D3D::CompileShader(
D3D::ShaderType::Compute, m_feature_level, m_debug, shader_code, macros, entry_point);
blob =
D3D::CompileShader(D3D::ShaderType::Compute, m_shader_model, m_debug, shader_code, macros, entry_point);
break;
default:
break;
+4 -4
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "GS/Renderers/DX11/D3D.h"
#include "common/Pcsx2Defs.h"
#include "common/HashCombine.h"
@@ -32,10 +33,9 @@ public:
D3D12ShaderCache();
~D3D12ShaderCache();
__fi D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
__fi bool UsingDebugShaders() const { return m_debug; }
bool Open(D3D_FEATURE_LEVEL feature_level, bool debug);
bool Open(D3D::ShaderModel shader_model, bool debug);
void Close();
__fi ComPtr<ID3DBlob> GetVertexShader(
@@ -95,7 +95,7 @@ private:
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
static std::string GetCacheBaseFileName(const std::string_view type, D3D_FEATURE_LEVEL feature_level, bool debug);
static std::string GetCacheBaseFileName(const std::string_view type, D3D::ShaderModel shader_model, bool debug);
static CacheIndexKey GetShaderCacheKey(
EntryType type, const std::string_view shader_code, const D3D_SHADER_MACRO* macros, const char* entry_point);
static CacheIndexKey GetPipelineCacheKey(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc);
@@ -123,6 +123,6 @@ private:
std::FILE* m_pipeline_blob_file = nullptr;
CacheIndex m_pipeline_index;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
D3D::ShaderModel m_shader_model = D3D::ShaderModel::SM51;
bool m_debug = false;
};
+3 -2
View File
@@ -892,7 +892,7 @@ bool GSDevice12::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_tfx_source = std::move(*shader);
}
if (!m_shader_cache.Open(m_feature_level, GSConfig.UseDebugDevice))
if (!m_shader_cache.Open(D3D::ShaderModel::SM51, GSConfig.UseDebugDevice))
Console.Warning("D3D12: Shader cache failed to open.");
if (!CreateRootSignatures())
@@ -4371,7 +4371,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(m_ds_as_rt);
const bool feedback_rt = draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier) || (config.tex && config.tex == config.rt));
const bool feedback_rt = draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier)) && (config.ps.IsFeedbackLoopRT() ||
config.alpha_second_pass.ps.IsFeedbackLoopRT())) || (config.tex && config.tex == config.rt));
const bool feedback_depth = draw_ds_as_rt != nullptr;
if (feedback_rt && !m_features.texture_barrier)
+21 -18
View File
@@ -790,11 +790,14 @@ void GSRendererHW::DetectTextureShuffle()
void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
const auto HasLowerOnes = [&](u32 x) { return x != 0 && (x & (x + 1)) == 0; };
if (!m_process_texture || !rt)
{
GL_INS("HW: Texture shuffle detection (2): Not detected.");
m_texture_shuffle.Disable();
return;
}
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
if (frame_psm.bpp != 16)
{
GL_INS("HW: Texture shuffle detection (2): Failed (not 16 bit RT).");
@@ -804,7 +807,7 @@ void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GS
if (m_texture_shuffle)
{
if (tex->m_32_bits_fmt)
if (tex && tex->m_32_bits_fmt)
{
GL_INS("HW: Texture shuffle detection (2): Passed.");
}
@@ -812,9 +815,9 @@ void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GS
{
// Detects when the source texture is really a 16 bit texture instead of 32 bit being reinterpreted as 16 bit.
// Make sure it's opaque and not bilinear to reduce false positives.
const auto HasLowerOnes = [&](u32 x) { return x != 0 && (x & (x + 1)) == 0; };
if (m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() &&
rt->m_32_bits_fmt == true && IsOpaque() && !m_vt.IsRealLinear() &&
rt && rt->m_32_bits_fmt == true && IsOpaque() && !m_vt.IsRealLinear() &&
HasLowerOnes(m_cached_ctx.FRAME.FBMSK))
{
GL_INS("HW: Texture shuffle detection (2): Passed (real 16 bit source).");
@@ -832,10 +835,10 @@ void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GS
// Last ditch check for reinterpreting a 32 bit source and RT as 16 bits.
// These "shuffles" appear to not do anything useful for games, but using the texture shuffle
// path helps to maintain correct sizes in the texture cache. Occurs in NFS Most Wanted.
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
if (PRIM->TME &&
(m_vt.m_primclass == GS_SPRITE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS && TrianglesAreQuads(true)) &&
(tex_psm.bpp == 16) && (frame_psm.bpp == 16) && rt->m_32_bits_fmt && tex->m_32_bits_fmt)
((m_vt.m_primclass == GS_SPRITE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS) && TrianglesAreQuads(true)) &&
(tex_psm.bpp == 16) && (frame_psm.bpp == 16) && rt && rt->m_32_bits_fmt && tex && tex->m_32_bits_fmt)
{
GL_INS("HW: Texture shuffle detection (2): Passed (HACK: reinterpreting both source/RT as 16 bit).");
m_texture_shuffle.type = TextureShuffleType::HackShuffle;
@@ -2918,7 +2921,7 @@ void GSRendererHW::Draw()
}
// Test if we can optimize Alpha Test as a NOP
m_cached_ctx.TEST.ATE = m_cached_ctx.TEST.ATE && !GSRenderer::TryAlphaTest(fm, zm);
m_cached_ctx.TEST.ATE = !!m_cached_ctx.TEST.ATE && !GSRenderer::TryAlphaTest(fm, zm);
// Need to fix the alpha test, since the alpha will be fixed to 1.0 if ABE is disabled and AA1 is enabled
// So if it doesn't meet the condition, always fail, if it does, always pass (turn off the test).
@@ -2992,7 +2995,7 @@ void GSRendererHW::Draw()
// I hate that I have to do this, but some games (like Pac-Man World Rally) troll us by causing a flush with degenerate triangles, so we don't have all available information about the next draw.
// So we have to check when the next draw happens if our frame has changed or if it's become recursive.
const bool has_colclip_texture = g_gs_device->GetColorClipTexture() != nullptr;
if (!no_rt && has_colclip_texture && (m_conf.colclip_frame.FBP != m_cached_ctx.FRAME.FBP || m_conf.colclip_frame.Block() == m_cached_ctx.TEX0.TBP0))
if (!no_rt && has_colclip_texture && (m_conf.colclip_frame.FBP != m_cached_ctx.FRAME.FBP || (PRIM->TME && m_conf.colclip_frame.Block() == m_cached_ctx.TEX0.TBP0)))
{
GIFRegTEX0 FRAME;
FRAME.TBP0 = m_conf.colclip_frame.Block();
@@ -4388,15 +4391,15 @@ void GSRendererHW::Draw()
}
}
// Second pass texture shuffle detection using RT and source.
DetectTextureShuffleSecondPass(rt, src);
if (m_process_texture)
{
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
if (rt)
{
// Second pass texture shuffle detection using RT and source.
DetectTextureShuffleSecondPass(rt, src);
if (m_texture_shuffle)
{
if (IsSplitTextureShuffle(rt->m_TEX0, rt->m_valid))
@@ -6261,7 +6264,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GS
// If date is enabled you need to test the green channel instead of the alpha channel.
// Only enable this code in DATE mode to reduce the number of shaders.
m_conf.ps.write_rg = (process_rg & SHUFFLE_WRITE) && m_cached_ctx.TEST.DATE;
m_conf.ps.write_rg = !!(process_rg & SHUFFLE_WRITE) && !!m_cached_ctx.TEST.DATE;
m_conf.ps.real16src = m_texture_shuffle.real_16_bit_source;
@@ -8804,7 +8807,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
EmulateDATESelectMethod(date_options, rt, blend_alpha_min, blend_alpha_max);
// Before emulateblending, dither will be used
m_conf.ps.dither = GSConfig.Dithering > 0 && m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_16 && env.DTHE.DTHE;
m_conf.ps.dither = GSConfig.Dithering > 0 && m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_16 && !!env.DTHE.DTHE;
if (m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_24)
{
@@ -9761,12 +9764,12 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.FBMSK);
GSUploadQueue clear_queue;
clear_queue.transfer_type = EEGS_TransferType::Clear;
clear_queue.draw = s_n;
clear_queue.rect = m_r;
clear_queue.blit.DBP = m_cached_ctx.FRAME.Block();
clear_queue.blit.DBW = m_cached_ctx.FRAME.FBW;
clear_queue.blit.DPSM = m_cached_ctx.FRAME.PSM;
clear_queue.zero_clear = true;
m_draw_transfers.push_back(clear_queue);
}
else
@@ -9791,12 +9794,12 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
ds_end_bp, m_cached_ctx.ZBUF.PSM, m_cached_ctx.FRAME.FBW);
GSUploadQueue clear_queue;
clear_queue.transfer_type = EEGS_TransferType::Clear;
clear_queue.draw = s_n;
clear_queue.rect = m_r;
clear_queue.blit.DBP = m_cached_ctx.ZBUF.Block();
clear_queue.blit.DBW = m_cached_ctx.FRAME.FBW;
clear_queue.blit.DPSM = m_cached_ctx.ZBUF.PSM;
clear_queue.zero_clear = true;
m_draw_transfers.push_back(clear_queue);
}
}
@@ -551,13 +551,13 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc, b
if (add_ee_transfer)
{
GSRendererHW::GSUploadQueue uq;
uq.transfer_type = GSRendererHW::GetInstance()->EEGS_TransferType::EE_to_GS;
uq.blit.U64 = 0;
uq.blit.DBP = hw.m_cached_ctx.FRAME.Block();
uq.blit.DBW = hw.m_cached_ctx.FRAME.FBW;
uq.blit.DPSM = hw.m_cached_ctx.FRAME.PSM;
uq.draw = GSState::s_n;
uq.rect = bbox;
uq.zero_clear = false;
hw.m_draw_transfers.push_back(uq);
}
+40 -18
View File
@@ -1340,6 +1340,14 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
return nullptr;
}
// Invalid values show up in dumps but it's unclear if it's a core issue or a bug in the games.
// Return a temporary source to prevent the lookup from corrupting the TC.
if (TEX0.TBW > 32 && !GSUtil::IsValidPSM(static_cast<int>(TEX0.PSM)))
{
GL_CACHE("TC: Invalid TEX0 (TBW=%d, PSM=%x), using temporary source.", TEX0.TBW, TEX0.PSM);
return CreateSource(TEX0, TEXA, CLAMP, nullptr, 0, 0, nullptr, nullptr, nullptr, SourceRegion(), true);
}
const GSVector2i compare_lod(lod ? *lod : GSVector2i(0, 0));
Source* src = nullptr;
@@ -2253,20 +2261,34 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
}
}
if (src->m_from_target && src->m_target_direct && src->m_region.HasEither())
if (src->m_from_target && src->m_target_direct)
{
if (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0)
GSVector4i req_rect = r;
// The read area might be offset but the start of the texture is at the beginning of the space.
req_rect.x = region.HasX() ? region.GetMinX() : 0;
req_rect.y = region.HasY() ? region.GetMinY() : 0;
const bool dirty_overlap = !src->m_from_target->m_dirty.GetTotalRect(src->m_from_target->m_TEX0, src->m_from_target->m_unscaled_size).rintersect(req_rect).rempty();
if (dirty_overlap)
src->m_from_target->Update();
if (src->m_region.HasEither())
{
src->m_region.bits = 0;
src->m_region.SetX(0, region.HasX() ? region.GetMaxX() : (1 << TEX0.TW));
src->m_region.SetY(0, region.HasY() ? region.GetMaxY() : (1 << TEX0.TH));
}
else if (src->m_TEX0.TBP0 > src->m_from_target->m_TEX0.TBP0)
{
GSVector4i dst_offset = TranslateAlignedRectByPage(src->m_from_target, src->m_TEX0.TBP0, src->m_TEX0.PSM, src->m_TEX0.TBW, GSVector4i(0, 0, 1, 1), false);
src->m_region.bits = 0;
src->m_region.SetX(dst_offset.x, dst_offset.x + (region.HasX() ? std::min(region.GetMaxX(), (1 << TEX0.TW)) : (1 << TEX0.TW)));
src->m_region.SetY(dst_offset.y, dst_offset.y + (region.HasY() ? std::min(region.GetMaxY(), (1 << TEX0.TH)) : (1 << TEX0.TH)));
if (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0)
{
src->m_region.bits = 0;
src->m_region.SetX(0, region.HasX() ? region.GetMaxX() : (1 << TEX0.TW));
src->m_region.SetY(0, region.HasY() ? region.GetMaxY() : (1 << TEX0.TH));
}
else if (src->m_TEX0.TBP0 > src->m_from_target->m_TEX0.TBP0)
{
GSVector4i dst_offset = TranslateAlignedRectByPage(src->m_from_target, src->m_TEX0.TBP0, src->m_TEX0.PSM, src->m_TEX0.TBW, GSVector4i(0, 0, 1, 1), false);
src->m_region.bits = 0;
src->m_region.SetX(dst_offset.x, dst_offset.x + (region.HasX() ? std::min(region.GetMaxX(), (1 << TEX0.TW)) : (1 << TEX0.TW)));
src->m_region.SetY(dst_offset.y, dst_offset.y + (region.HasY() ? std::min(region.GetMaxY(), (1 << TEX0.TH)) : (1 << TEX0.TH)));
}
}
}
@@ -2341,7 +2363,7 @@ void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache:
const GSVector4 target_drect = GSVector4(target_drect_unscaled) * target->m_scale;
const bool valid_color = t->m_valid_rgb;
const bool valid_alpha = (t->m_valid_alpha_high | t->m_valid_alpha_low) && (GSUtil::GetChannelMask(t->m_TEX0.PSM) & 0x8);
const bool valid_alpha = (t->m_valid_alpha_high || t->m_valid_alpha_low) && (GSUtil::GetChannelMask(t->m_TEX0.PSM) & 0x8);
target->m_valid_alpha_high |= t->m_valid_alpha_high;
target->m_valid_alpha_low |= t->m_valid_alpha_low;
@@ -3474,14 +3496,15 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
else
eerect = eerect.runion(targetr);
const bool transfer_is_clear = iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear;
// Later writes might be partial over a previously cleared area. We want to upload in these cases.
hw_clear = hw_clear.has_value() ? (hw_clear.value() && iter->zero_clear) : iter->zero_clear;
hw_clear = hw_clear.has_value() ? (hw_clear.value() && transfer_is_clear) : transfer_is_clear;
// When the write covers the entire target, don't bother checking any earlier writes.
if (iter->blit.DBP <= TEX0.TBP0 && transfer_end >= rect_end)
{
// If it was a clear draw then we can use that as our target size.
if (iter->zero_clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DPSM == TEX0.PSM)
if (iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DPSM == TEX0.PSM)
dst->UpdateValidity(iter->rect);
// Some games clear RT and Z at the same time, only erase if it's specifically this target.
@@ -3520,7 +3543,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
if (iter->draw < (GSState::s_n - 1))
break;
if (iter->zero_clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DBW == TEX0.TBW)
if (iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DBW == TEX0.TBW)
{
hw_clear = true;
transfers.erase(std::next(iter).base());
@@ -3855,7 +3878,6 @@ GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(GIFRegTEX0 TEX0, con
const u32 bp = TEX0.TBP0;
Target* dst = nullptr;
Target* dst_match = nullptr;
auto* list = &m_dst[RenderTarget];
// Let's try to find a perfect frame that contains valid data
@@ -4011,7 +4033,7 @@ GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(GIFRegTEX0 TEX0, con
else
eerect = eerect.runion(targetr);
if (iter->zero_clear && iter->draw == last_draw)
if (iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear && iter->draw == last_draw)
{
can_create = false;
break;
+1 -1
View File
@@ -263,7 +263,7 @@ public:
bool OverlapsValid(u32 bp, u32 bw, u32 psm, const GSVector4i& rect) const;
__fi bool HasValidAlpha() const { return (m_valid_alpha_low | m_valid_alpha_high); }
__fi bool HasValidAlpha() const { return (m_valid_alpha_low || m_valid_alpha_high); }
bool HasValidBitsForFormat(u32 psm, bool req_color, bool req_alpha, bool width_match);
void ResizeDrawn(const GSVector4i& rect);
+5 -10
View File
@@ -492,7 +492,10 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
if (rt1)
{
pxAssert(md);
desc.colorAttachments[1].texture = GetRT1DepthTexture(md);
MTLRenderPassColorAttachmentDescriptor* color1 = desc.colorAttachments[1];
color1.texture = GetRT1DepthTexture(md);
if (m_features.framebuffer_fetch)
color1.clearColor = MTLClearColorMake(depth_load == MTLLoadActionClear ? depth_clear : -1, 0, 0, 0);
}
EndRenderPass();
@@ -1031,15 +1034,7 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
{
MTLRenderPassColorAttachmentDescriptor* color1 = [[desc colorAttachments] objectAtIndexedSubscript:1];
[color1 setStoreAction:MTLStoreActionDontCare];
if (m_features.framebuffer_fetch)
{
[color1 setLoadAction:MTLLoadActionClear];
[color1 setClearColor:MTLClearColorMake(-1, 0, 0, 0)];
}
else
{
[color1 setLoadAction:MTLLoadActionLoad];
}
[color1 setLoadAction:m_features.framebuffer_fetch ? MTLLoadActionClear : MTLLoadActionLoad];
}
m_render_pass_desc[i] = desc;
}
+4
View File
@@ -464,6 +464,10 @@ struct PSMain
// HW sampler will reject bad UVs, match that here.
uv = any(isnan(uv) | isinf(uv)) ? float2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
float2 sz = float2(get_tex_dims());
+18 -7
View File
@@ -2931,20 +2931,30 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
glTextureBarrier();
}
if (draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy) || (config.tex && config.tex == config.rt)) &&
!m_features.texture_barrier)
const bool tex_is_fb = config.tex && config.tex == draw_rt;
const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT() || tex_is_fb;
const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT() || tex_is_fb;
if (draw_rt && !m_features.texture_barrier && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
config.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
// Requires a copy of the RT.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);
if (!draw_rt_clone)
Console.Warning("GL: Failed to allocate temp texture for RT copy.");
}
if (draw_ds && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
!m_features.texture_barrier && m_features.depth_feedback && config.ps.IsFeedbackLoopDepth())
const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth();
const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth();
if (draw_ds && !m_features.texture_barrier && m_features.depth_feedback &&
(config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) && (ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
draw_ds_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_ds->GetFormat(), true);
if (!draw_ds_clone)
Console.Warning("GL: Failed to allocate temp texture for DS copy.");
}
@@ -2960,7 +2970,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
glClearBufferiv(GL_STENCIL, 0, &clear_color);
}
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
if (config.blend_multi_pass.enable)
@@ -3006,9 +3016,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
{
OMSetBlendState();
}
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.feedback_loops();
SetupOM(config.alpha_second_pass.depth);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
m_features.texture_barrier ? config.alpha_second_pass.require_one_barrier : false, config.alpha_second_pass.require_full_barrier);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
}
if (colclip_rt)
+5 -1
View File
@@ -527,6 +527,10 @@ void GSRasterizer::DrawEdgeLine(const GSVertexSW& v0, const GSVertexSW& v1, cons
const int rxi1 = static_cast<int>(rx1);
const int ryi1 = static_cast<int>(ry1);
// Early exit for horizontal lines.
if (delta_y == 0.0f && !IsOneOfMyScanlines(ryi0) && !aa)
return;
const GSVertexSW dedge = dv / GSVector4(std::abs(step_x ? delta_x : delta_y));
GSVertexSW edge(v0);
@@ -1546,7 +1550,7 @@ void GSRasterizerList::Queue(const GSRingHeap::SharedPtr<GSRasterizerData>& data
m_ds.SetupDraw(*data.get());
}
pxAssert(r.top >= 0 && r.top < 2048 && r.bottom >= 0 && r.bottom < 2048);
pxAssert(r.top >= 0 && r.top <= 2048 && r.bottom >= 0 && r.bottom <= 2048);
int top = r.top >> m_thread_height;
int bottom = std::min<int>((r.bottom + (1 << m_thread_height) - 1) >> m_thread_height, top + (int)m_workers.size());
+19 -1
View File
@@ -468,7 +468,25 @@ void GSRendererSW::Draw()
std::memcpy(sd->index, m_index->buff, sizeof(u16) * m_index->tail);
GSVector4i scissor = context->scissor.in;
GSVector4i bbox = GSVector4i(m_vt.m_min.p.floor().upld(m_vt.m_max.p.floor())) + GSVector4i(0, 0, 1, 1); // right/bottom should be exclusive so +1
GSVector4i bbox;
if (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_POINT_CLASS)
{
// min: round, max: round
bbox = GSVector4i((m_vt.m_min.p + GSVector4(0.5f)).floor().upld((m_vt.m_max.p + GSVector4(0.5f)).floor()));
}
else
{
// min: ceil, max: floor
bbox = GSVector4i(m_vt.m_min.p.ceil().upld(m_vt.m_max.p.floor()));
}
if (PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS))
{
bbox += GSVector4i(-1, -1, 1, 1); // Expand bbox by 1 on all sides when using antialiasing.
}
bbox += GSVector4i(0, 0, 1, 1); // right/bottom should be exclusive so +1
GSVector4i r = bbox.rintersect(scissor);
+2 -1
View File
@@ -5992,7 +5992,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
}
if (draw_rt && (config.require_one_barrier || (config.tex && config.tex == config.rt)) && !m_features.texture_barrier)
if (draw_rt && ((config.require_one_barrier && (config.ps.IsFeedbackLoopRT() || config.alpha_second_pass.ps.IsFeedbackLoopRT())) ||
(config.tex && config.tex == config.rt)) && !m_features.texture_barrier)
{
// Requires a copy of the RT.
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true));
+77 -30
View File
@@ -649,7 +649,7 @@ void FullscreenUI::Render()
if (s_about_window_open)
DrawAboutWindow();
if (s_achievements_login_open)
if (s_achievements_login_open || ImGui::IsPopupOpen("RetroAchievements"))
DrawAchievementsLoginWindow();
if (s_input_binding_type != InputBindingInfo::Type::Unknown)
@@ -3499,14 +3499,18 @@ bool FullscreenUI::OpenAchievementsWindow()
void FullscreenUI::DrawAchievementsLoginWindow()
{
ImGui::SetNextWindowSize(LayoutScale(400.0f, 330.0f));
if (s_achievements_login_open && !ImGui::IsPopupOpen("RetroAchievements"))
ImGui::OpenPopup("RetroAchievements");
const float dialog_width = std::clamp(LayoutScale(420.0f), 300.0f, ImGui::GetIO().DisplaySize.x);
ImGui::SetNextWindowSizeConstraints(ImVec2(dialog_width, 0.0f), ImVec2(dialog_width, ImGui::GetIO().DisplaySize.y));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(ImGuiFullscreen::LAYOUT_WINDOW_ROUNDING));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(24.0f, 24.0f));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.13f, 0.13f, 0.13f, 0.95f));
if (ImGui::BeginPopupModal("RetroAchievements", &s_achievements_login_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar))
if (ImGui::BeginPopupModal("RetroAchievements", &s_achievements_login_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize))
{
const float content_width = ImGui::GetContentRegionAvail().x;
@@ -3553,7 +3557,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (s_achievements_login_logging_in)
if (s_achievements_login_logging_in || s_achievements_login_show_dismiss)
ImGui::BeginDisabled();
ImGui::SetNextItemWidth(content_width);
@@ -3567,7 +3571,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PopStyleColor(5);
ImGui::PopStyleVar(3);
if (s_achievements_login_logging_in)
if (s_achievements_login_logging_in || s_achievements_login_show_dismiss)
ImGui::EndDisabled();
ImGui::Spacing();
@@ -3582,20 +3586,67 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PopStyleColor();
ImGui::Spacing();
}
else if (s_achievements_login_show_dismiss)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.35f, 0.9f, 0.35f, 1.0f));
const TinyString username = Host::GetBaseTinyStringSettingValue("Achievements", "Username", "");
const SmallString success_text = SmallString::from_format(
FSUI_FSTR("Successfully logged in as {}."), username.empty() ? "Unknown" : username.view());
const float status_width = ImGui::CalcTextSize(success_text.c_str()).x;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (content_width - status_width) * 0.5f);
ImGui::TextUnformatted(success_text.c_str());
ImGui::PopStyleColor();
ImGui::Spacing();
}
const float button_height = LayoutScale(36.0f);
const float button_width = LayoutScale(100.0f);
const float button_height = std::max(LayoutScale(36.0f), 28.0f);
const float button_spacing = LayoutScale(12.0f);
const float total_width = (button_width * 2) + button_spacing;
const float start_x = (content_width - total_width) * 0.5f;
const float button_width = (content_width - button_spacing) * 0.5f;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + start_x);
auto CloseLoginPopup = []() {
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_logging_in = false;
s_achievements_login_show_dismiss = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
};
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(ImGuiFullscreen::LAYOUT_FRAME_ROUNDING));
const bool can_login = !s_achievements_login_logging_in &&
strlen(s_achievements_login_username) > 0 &&
strlen(s_achievements_login_password) > 0;
const bool can_login = !s_achievements_login_show_dismiss && !s_achievements_login_logging_in &&
strlen(s_achievements_login_username) > 0 &&
strlen(s_achievements_login_password) > 0;
if (s_achievements_login_show_dismiss)
{
// keep dialog open and let user explicitly dismiss.
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.9f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
if (ImGui::Button(FSUI_CSTR("Dismiss"), ImVec2(button_width, button_height)) && !s_achievements_login_logging_in)
CloseLoginPopup();
ImGui::PopStyleColor(3);
ImGui::SameLine(0, button_spacing);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::BeginDisabled();
ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height));
ImGui::EndDisabled();
ImGui::PopStyleColor(3);
ImGui::PopStyleVar();
ImGui::EndPopup();
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
return;
}
if (can_login)
{
@@ -3613,6 +3664,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
if (ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height)) && can_login)
{
s_achievements_login_logging_in = true;
s_achievements_login_show_dismiss = false;
Host::RunOnCPUThread([username = std::string(s_achievements_login_username),
password = std::string(s_achievements_login_password)]() {
@@ -3624,14 +3676,10 @@ void FullscreenUI::DrawAchievementsLoginWindow()
if (!result)
{
ShowToast(std::string(), fmt::format(FSUI_FSTR("Login failed.\nError: {}\n\nPlease check your username and password, and try again."),
error.GetDescription()));
error.GetDescription()));
return;
}
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
if (s_achievements_login_reason == Achievements::LoginRequestReason::UserInitiated)
@@ -3683,11 +3731,12 @@ void FullscreenUI::DrawAchievementsLoginWindow()
});
}
}
s_achievements_login_show_dismiss = true;
});
}
ImGui::PopStyleColor(3);
ImGui::SameLine(0, button_spacing);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
@@ -3702,12 +3751,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
Achievements::DisableHardcoreMode();
}
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_logging_in = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
CloseLoginPopup();
}
ImGui::PopStyleColor(3);
@@ -3718,9 +3762,6 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
if (!ImGui::IsPopupOpen("RetroAchievements"))
ImGui::OpenPopup("RetroAchievements");
}
bool FullscreenUI::IsAchievementsWindowOpen()
@@ -3960,11 +4001,14 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
"sounds/achievements/lbsubmit.wav", FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Select Leaderboard Submit Sound"));
MenuHeading(FSUI_CSTR("Account"));
if (bsi->ContainsValue("Achievements", "Token"))
SettingsInterface* secrets_si = Host::Internal::GetSecretsSettingsLayer();
const TinyString username = bsi->GetTinyStringValue("Achievements", "Username", "");
const bool has_token = (secrets_si && secrets_si->ContainsValue("Achievements", "Token"));
if (has_token)
{
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyle().Colors[ImGuiCol_Text]);
ActiveButton(SmallString::from_format(
fmt::runtime(FSUI_ICONSTR(ICON_FA_USER, "Username: {}")), bsi->GetTinyStringValue("Achievements", "Username")),
fmt::runtime(FSUI_ICONSTR(ICON_FA_USER, "Username: {}")), username.empty() ? "Unknown" : username.view()),
false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
ActiveButton(SmallString::from_format(fmt::runtime(FSUI_ICONSTR(ICON_FA_CLOCK, "Login token generated on {}")),
TimeToPrintableString(static_cast<time_t>(
@@ -3984,6 +4028,7 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements.")))
{
s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
s_achievements_login_show_dismiss = false;
s_achievements_login_open = true;
}
}
@@ -4172,6 +4217,7 @@ TRANSLATE_NOOP("FullscreenUI", "Please enter your user name and password for ret
TRANSLATE_NOOP("FullscreenUI", "Username");
TRANSLATE_NOOP("FullscreenUI", "Password");
TRANSLATE_NOOP("FullscreenUI", "Logging in...");
TRANSLATE_NOOP("FullscreenUI", "Dismiss");
TRANSLATE_NOOP("FullscreenUI", "Login");
TRANSLATE_NOOP("FullscreenUI", "Cancel");
TRANSLATE_NOOP("FullscreenUI", "When enabled and logged in, PCSX2 will scan for achievements on startup.");
@@ -4210,6 +4256,7 @@ TRANSLATE_NOOP("FullscreenUI", "Last Played: {}");
TRANSLATE_NOOP("FullscreenUI", "Size: {:.2f} MB");
TRANSLATE_NOOP("FullscreenUI", "Are you sure you want to reset the play time for '{}' ({})?\n\nYour current play time is {}.\n\nThis action cannot be undone.");
TRANSLATE_NOOP("FullscreenUI", "Version: {}");
TRANSLATE_NOOP("FullscreenUI", "Successfully logged in as {}.");
TRANSLATE_NOOP("FullscreenUI", "Login failed.\nError: {}\n\nPlease check your username and password, and try again.");
TRANSLATE_NOOP("FullscreenUI", "Failed to preview sound:\n{}");
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Backup Slot {}");
+1
View File
@@ -234,6 +234,7 @@ namespace FullscreenUI
// achievements login dialog state
inline bool s_achievements_login_open = false;
inline bool s_achievements_login_logging_in = false;
inline bool s_achievements_login_show_dismiss = false;
inline char s_achievements_login_username[256] = {};
inline char s_achievements_login_password[256] = {};
inline Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
+1 -1
View File
@@ -709,7 +709,7 @@ __fi void psxRcntWmode32(int index, u32 value)
// Current counter *always* resets on mode write.
counter.count = 0;
counter.startCycle = psxRegs.cycle & ~(counter.rate - 1);
counter.startCycle = psxRegs.cycle & ~(static_cast<u64>(counter.rate - 1));
counter.target &= 0xffffffff;
_rcntSet(index);
}
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 91; // Last changed in PR 14370
static constexpr u32 SHADER_CACHE_VERSION = 92; // Last changed in PR 14399
+91 -13
View File
@@ -29,6 +29,20 @@ namespace usb_msd
"00000000000PCSX2",
};
static const USBDescStrings olympus_camera_desc_strings = {
"",
"OLYMPUS",
"USB Mass Storage",
"00000000000PCSX2",
};
static const USBDescStrings olympus_mo_desc_strings = {
"",
"OLYMPUS",
"MOS3412",
"00000000000PCSX2",
};
static const USBDescStrings sony_msac_desc_strings = {
"",
"Sony",
@@ -38,14 +52,14 @@ namespace usb_msd
static const uint8_t zip100_dev_descriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 0.10
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 8
0x9B, 0x05, // idVendor 0x059B
0x34, 0x00, // idProduct 0x0034
0x00, 0x01, // bcdDevice 0.00
0x00, 0x01, // bcdDevice 1.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
@@ -94,6 +108,23 @@ namespace usb_msd
0x20, // bInterval 32 (unit depends on device speed)
};
static const uint8_t olympus_dev_descriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 8
0xb4, 0x07, // idVendor 0x07b4
0x02, 0x01, // idProduct 0x0102
0x00, 0x01, // bcdDevice 1.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t sony_msac_dev_descriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
@@ -535,7 +566,7 @@ namespace usb_msd
len = std::min<size_t>(len, s->f.data_len);
//TODO No async reader/writer so do it right here
if (s->f.tag == s->f.file_op_tag)
if (s->f.file_op_tag)
{
switch (s->f.mode)
{
@@ -586,6 +617,7 @@ namespace usb_msd
s->f.result = COMMAND_PASSED;
s->f.off = 0;
s->f.file_op_tag = false;
if (cbw->cmd[0] != REQUEST_SENSE)
set_sense(s, SENSE_CODE(NO_SENSE));
@@ -698,7 +730,7 @@ namespace usb_msd
xfer_len = bswap32(*(uint32_t*)&cbw->cmd[6]);
s->f.data_len = xfer_len * LBA_BLOCK_SIZE;
s->f.file_op_tag = s->f.tag;
s->f.file_op_tag = true;
if (xfer_len == 0) // nothing to do
@@ -733,7 +765,7 @@ namespace usb_msd
xfer_len = bswap32(*(uint32_t*)&cbw->cmd[6]);
s->f.data_len = xfer_len * LBA_BLOCK_SIZE;
s->f.file_op_tag = s->f.tag;
s->f.file_op_tag = true;
if (xfer_len == 0) //nothing to do
break;
@@ -1125,6 +1157,26 @@ namespace usb_msd
s->dev.klass.handle_control = usb_msd_handle_control;
s->dev.klass.handle_data = usb_msd_handle_data;
break;
case OLYMPUS_CAMERA:
path = USB::GetConfigString(si, port, TypeName(), "ImagePathOlympusCamera");
s->desc.str = olympus_camera_desc_strings;
if (usb_desc_parse_dev(olympus_dev_descriptor, sizeof(olympus_dev_descriptor), s->desc, s->desc_dev) < 0)
goto fail;
if (usb_desc_parse_config(zip100_config_descriptor, sizeof(zip100_config_descriptor), s->desc_dev) < 0)
goto fail;
s->dev.klass.handle_control = usb_msd_handle_control;
s->dev.klass.handle_data = usb_msd_handle_data;
break;
case OLYMPUS_MO:
path = USB::GetConfigString(si, port, TypeName(), "ImagePathOlympusMD");
s->desc.str = olympus_mo_desc_strings;
if (usb_desc_parse_dev(olympus_dev_descriptor, sizeof(olympus_dev_descriptor), s->desc, s->desc_dev) < 0)
goto fail;
if (usb_desc_parse_config(zip100_config_descriptor, sizeof(zip100_config_descriptor), s->desc_dev) < 0)
goto fail;
s->dev.klass.handle_control = usb_msd_handle_control;
s->dev.klass.handle_data = usb_msd_handle_data;
break;
case SONY_MSAC_US1:
path = USB::GetConfigString(si, port, TypeName(), "ImagePathMsac");
s->desc.str = sony_msac_desc_strings;
@@ -1230,6 +1282,8 @@ namespace usb_msd
{
static const char* subtypes[] = {
TRANSLATE_NOOP("USB", "Iomega Zip-100 (Generic)"),
TRANSLATE_NOOP("USB", "Olympus Camera"),
TRANSLATE_NOOP("USB", "Olympus Turbo MO"),
TRANSLATE_NOOP("USB", "Sony MSAC-US1 (PictureParadise)")
};
return subtypes;
@@ -1241,18 +1295,42 @@ namespace usb_msd
{
case IOMEGA_ZIP_100:
{
static constexpr const SettingInfo settings[] = {
{SettingInfo::Type::Path, "ImagePathMsd", TRANSLATE_NOOP("USB", "Image Path"),
TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")},
};
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathMsd",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
case OLYMPUS_CAMERA:
{
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathOlympusCamera",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
case OLYMPUS_MO:
{
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathOlympusMD",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
case SONY_MSAC_US1:
{
static constexpr const SettingInfo settings[] = {
{SettingInfo::Type::Path, "ImagePathMsac", TRANSLATE_NOOP("USB", "Image Path"),
TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")},
};
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathMsac",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
default:
+2
View File
@@ -10,6 +10,8 @@ namespace usb_msd
enum MSDType
{
IOMEGA_ZIP_100,
OLYMPUS_CAMERA,
OLYMPUS_MO,
SONY_MSAC_US1,
};