Compare commits

...
70 Commits
Author SHA1 Message Date
RedDevilusandrefractionpcsx2 c6ce380042 GameDB: More GS HW Fixes
Some extra comments for Monster Lab and SSX Tricky.

New entries:

- Alter Echo
- Beyond Good and Evil
- Dragon Ball Z - Budokai Tenkaichi 2
- Fatal Frame 1 / 3
- Ghost in the Shell - Stand Alone Complex
- Global Defence Force
- Xenosaga 1 / 2 / 3
2022-03-17 08:57:58 +00:00
lightningterror 939d98d660 Qt: Allow skipdraw up to 10k max. 2022-03-17 02:44:14 +01:00
lightningterror c9a3420f85 GS: Fix Wunused-variable warnings. 2022-03-17 00:50:28 +01:00
refractionpcsx2 9a1a399bac GameDB: Add a bunch of hw fixes and upscaling fixes 2022-03-16 23:40:53 +00:00
RedDevilusandrefractionpcsx2 c8aac1dca9 GameDB: Ace Combat + Soul Calibur + Metal Gear Solid Series
Missing mipmap from the Europese Ace Combat Zero/4/5, Align Sprite for them and Round Sprite. Align Sprite for SoulCalibur series.
Metal Gear 2 substance gets round sprite for font and Metal Gear 3 Subsistence gets Special (Texture) for blurriness.
2022-03-16 22:38:39 +00:00
lightningterror 14c17916f5 GS-hw: Optimize blending equations based on alpha value.
It will allow us to use free sw blending without texture barriers.

Will be especially helpful for opengl/vulkan.
2022-03-16 15:17:00 +01:00
RedDevilusandGitHub 7b0576d7cc GameDB: Dog Island and COD Final Fronts (#5671) 2022-03-16 07:24:05 +00:00
Blackbird88andGitHub ecb118d5af GameDB: Add gsHWFixes for GTA LCS/VCS (#5662)
Co authored by @Blackbird88
2022-03-15 20:22:19 +00:00
RedDevilusandrefractionpcsx2 efbf187b35 GameDB: Upscaling GS Batch 2
Adds second batch of upscaling fixes.
2022-03-15 20:21:15 +00:00
Tzerinasandrefractionpcsx2 2ea3feba90 GameDB: Add Upscaling Fixes to Onimusha 3
Adds Half-Pixel Offset: Special (Texture) and Round Sprite (Full) to Onimusha 3: Demon Siege
2022-03-15 20:19:06 +00:00
Tzerinasandrefractionpcsx2 f265257efb GameDB: Add Software FMV to Grandia 2 and Xtreme
Adds the SoftwareRendererFMV hack to Grandia 2 and Xtreme. Also adjusted titles.
2022-03-15 20:18:43 +00:00
lightningterror 13cc0caed7 GS Debugger: Fix hw hacks behavior not properly disabling. 2022-03-15 15:46:24 +01:00
refractionpcsx2 2068240e1a GS: Adjust Auto Flush to catch edge cases
Affected Beyond Good and Evil water
2022-03-15 10:15:21 +00:00
Tzerinasandrefractionpcsx2 7f5901f022 GameDB: GS HW renderer fixes for Tales of the Abyss
Description of Changes
Add Half-Pixel Offset: Special (Texture) to Tales of the Abyss.
2022-03-15 09:38:24 +00:00
a022de6225 GameDB: Add gsHWFixes for Shutokou Battle 01. (#5659)
Co-authored-by: Tokman5 <isozin_1210@yahoo.co.jp>
2022-03-15 08:12:52 +00:00
RedDevilusandrefractionpcsx2 f5570b7f40 GameDB: Rogue Galaxy
Adds half roundsprite to make upscaled HW more like software mode such as minimap while fighting.
2022-03-15 08:11:17 +00:00
RedDevilusandrefractionpcsx2 7e1c48694a GameDB: Upscaling GS Batch 1
Fixes the lint and adds a bunch of upscaling fixes.
2022-03-15 00:54:38 +00:00
lightningterror a546f61ea8 Qt: Fix Round sprite hack not applying. 2022-03-14 23:43:25 +01:00
PCSX2 Botandlightningterror 7ecac10eee PAD: Update to latest controller database. 2022-03-14 22:20:49 +01:00
Mrlinkwiiandrefractionpcsx2 ee2e10e86b GameDB: correct and update serials 2022-03-14 19:15:50 +00:00
RedDevilusandrefractionpcsx2 7752f44a90 GameDB: Documentation
Add values for GameDB, GUI options and the default to easier make GameDB changes
2022-03-14 19:14:40 +00:00
refractionpcsx2 e01fc3eb47 GS: Loosen requirement for half bottom 2022-03-14 19:14:26 +00:00
SaltyBetandrefractionpcsx2 910c8190e0 GameDB: Added missing variants of NAMCO games 2022-03-14 15:47:02 +00:00
SaltyBetandrefractionpcsx2 a62ccc0d69 GameDB: Auto GS HW renderer fixes for some NAMCO games
- Tekken 4
- Tekken 5
- Tekken Tag Tournament
2022-03-14 15:47:02 +00:00
Dreadmothandrefractionpcsx2 4f47041108 GameDB: Metal Arms - Glitch in the System (PAL)
Force disable mVU Flag Hack to prevent SPS
2022-03-14 15:33:39 +00:00
Connor McLaughlinandrefractionpcsx2 16cfde0538 GS: Use single array for dump packets 2022-03-14 15:26:19 +00:00
Connor McLaughlinandrefractionpcsx2 6a15d46461 Qt: Improve boot filename autodetection 2022-03-14 15:26:19 +00:00
Connor McLaughlinandrefractionpcsx2 4331ae1925 VMManager: Support playing back GS dumps 2022-03-14 15:26:19 +00:00
Connor McLaughlinandrefractionpcsx2 4d85d916b7 GS: Move dump file loading to core 2022-03-14 15:26:19 +00:00
Connor McLaughlinandrefractionpcsx2 4f4b14dd4d GameList: Add GetEntryByCRC() 2022-03-14 15:26:19 +00:00
Connor McLaughlinandrefractionpcsx2 444e650711 StringUtil: Add StartsWithNoCase/EndsWithNoCase 2022-03-14 15:26:19 +00:00
refractionpcsx2 c65ccaa153 GS-Config: Clear missing manual GS fixes 2022-03-14 11:08:39 +00:00
Connor McLaughlinandrefractionpcsx2 6b54db9b75 GS/VertexTrace: Fix min/max when last provoking vertex unsupported
This was causing incorrect min/max alpha values due to the index swap,
which caused the sides of text boxes in KH to disappear (because the
alpha test optimized to always failing).

Closes #5639.
2022-03-14 08:47:32 +00:00
Mrlinkwiiandrefractionpcsx2 3bab470db6 Docs : Add GS hardware fixes and MTVUSpeedHack 2022-03-13 18:33:37 +00:00
refractionpcsx2 adf01cb56a GS-Build: Fix Dump Verticles for linux 2022-03-13 05:01:51 +00:00
refractionpcsx2 bbe3af7f61 GS-Debug: Dump verticles with draws
- Move context dumping to the top so we see it as the game set, not after we've messed with it.
Thanks to tadanokojin for these changes
2022-03-13 03:46:16 +00:00
refractionpcsx2 3e7d32c807 GameDB: Give slightly clearer message for GS Fix override 2022-03-13 03:18:44 +00:00
refractionpcsx2 76e25cb738 GameDB: Add mipmapping/trilinear for Hot Shots/Everybody's Golf games 2022-03-13 02:34:29 +00:00
refractionpcsx2 8f0dc34847 GS: Improvements to MTBA address calculation 2022-03-13 01:55:08 +00:00
RedDevilusandrefractionpcsx2 fc6ec2cc02 GameDB: Transformers Armada: Prelude to Energon
Adds missing VIFFIFO + force disabling MTVU for not getting black screen
2022-03-13 01:53:48 +00:00
refractionpcsx2 34b7b07ff8 GameDB: Added a whole host of auto GS HW renderer fixes 2022-03-13 01:53:15 +00:00
Connor McLaughlinandrefractionpcsx2 6c33b73cdd GS: Make TC offset changable without recreating 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 1e86ba4120 GS: Append game serial/name to dump filename 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 89c79aa6c3 GSDump: Add embedded screenshot 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 4ed748fa30 GSDump: Add extensible header and serial
Serial is used to apply hw fixes.
2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 5569e94f41 GS: Make setting change detection more fine grained
Avoids the slower full restart when it's not needed.
2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 892eec79ed Workflows: Fix lint-gamedb for gsHWFixes 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 b248b4a8af GameDB: Add HW fixes for GTASA/GOW/GOW2 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 39fe467b64 GS: Purge CRCs of unused titles 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 1c301ec889 GS: Move point list palette to gamedb 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 de5690ddcb GS: Move texture-inside-rt flag to gamedb 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 f376c8f7ae GS: Remove second source of truth for HWMipmap 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 2e199d47a8 GS: Move automatic mipmapping override to gamedb 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 96269db93e GameDatabase: Add ability to override GS fixes 2022-03-12 20:48:51 +00:00
Connor McLaughlinandrefractionpcsx2 d35db63d73 GS: Reference GSConfig instead of using theApp
Removes multiple sources of truth, enables overrides.
2022-03-12 20:48:51 +00:00
refractionpcsx2 9d003486c2 GS-hw: Attempt to improve half screen detection 2022-03-12 12:28:18 +00:00
refractionpcsx2 0c4b85980c GS: Support local to local transfers that overwrite themselves 2022-03-12 12:27:43 +00:00
Connor McLaughlinandrefractionpcsx2 5961db6b9b GS/Vulkan: Elide render pass restarts on depth buffer toggle 2022-03-12 12:22:51 +00:00
Connor McLaughlinandrefractionpcsx2 d0039c2920 Gif_Unit: Vectorize analyzeTag() 2022-03-12 12:10:32 +00:00
TellowKrinkleandrefractionpcsx2 5bdec2f532 x86emitter: Fix x64 8-bit rmw codegen 2022-03-11 12:59:57 +00:00
refractionpcsx2 fd758bb307 GameDB: Added MTVU disable to InstantVU off games 2022-03-11 10:25:15 +00:00
refractionpcsx2 a11d09ebdf Git: Update GameDB Validation script 2022-03-11 10:25:15 +00:00
refractionpcsx2 d294064da6 GameDB: Disable MTVU on T-Bit games 2022-03-11 10:25:15 +00:00
refractionpcsx2 fd4a5acc40 MTVU: Try to make T-Bit more reliable.
Add MTVUSpeedHack option to GameDB so it can be forcefully disabled
2022-03-11 10:25:15 +00:00
refractionpcsx2 05a7a61257 GS/Autoflush: Handle different page widths/arrangements 2022-03-11 10:24:26 +00:00
refractionpcsx2 90a4a11d49 GS: Add Auto Flush for Z buffer draws
Adjust Burnout CRC hack
2022-03-11 10:24:26 +00:00
lightningterror d9f914eb7c GS-hw: Move the Ad to As equation swap when alpha is masked to Basic level and higher on gl/vk.
Safer this way, otherwise need to take in to account when accumulation, non recursive, and blend mix is enabled, or manually enable them on Minimum level.

Everything that we need is enabled on Basic level.

Change is done for clamp 1 only.
2022-03-09 23:24:44 +01:00
TheLastRarandrefractionpcsx2 134242973b Config: Set a sane default value for HddSizeSectors 2022-03-09 10:07:54 +00:00
TheLastRarandrefractionpcsx2 92900d8dc8 Config: Fix manual subnet mask save/load 2022-03-09 10:07:54 +00:00
Ziemasandrefractionpcsx2 7a970e1d00 Filesystem: Properly convert stat return to bool.
Two of the overloads where wrong.
2022-03-09 09:33:51 +00:00
70 changed files with 4767 additions and 1255 deletions
@@ -12,6 +12,7 @@ allowed_game_options = [
"roundModes",
"clampModes",
"gameFixes",
"gsHWFixes",
"speedHacks",
"memcardFilters",
"patches",
@@ -19,24 +20,54 @@ allowed_game_options = [
allowed_round_modes = ["eeRoundMode", "vuRoundMode"]
allowed_clamp_modes = ["eeClampMode", "vuClampMode"]
allowed_game_fixes = [
"VuAddSubHack",
"FpuCompareHack",
"FpuMulHack",
"FpuNegDivHack",
"XGKickHack",
"EETimingHack",
"GoemonTlbHack",
"SoftwareRendererFMVHack",
"SkipMPEGHack",
"OPHFlagHack",
"EETimingHack",
"DMABusyHack",
"GIFFIFOHack",
"VIFFIFOHack",
"VIF1StallHack",
"GIFFIFOHack",
"GoemonTlbHack",
"VUSyncHack",
"VuAddSubHack",
"IbitHack",
"VUSyncHack",
"VUOverflowHack",
"XGKickHack",
]
allowed_speed_hacks = ["mvuFlagSpeedHack", "InstantVU1SpeedHack"]
allowed_gs_hw_fixes = [
"autoFlush",
"conservativeFramebuffer",
"cpuFramebufferConversion",
"disableDepthSupport",
"wrapGSMem",
"preloadFrameData",
"fastTextureInvalidation",
"textureInsideRT",
"alignSprite",
"mergeSprite",
"wildArmsHack",
"pointListPalette",
"mipmap",
"trilinearFiltering",
"skipDrawStart",
"skipDrawEnd",
"halfBottomOverride",
"halfPixelOffset",
"roundSprite",
"texturePreloading",
]
gs_hw_fix_ranges = {
"mipmap": (0, 2),
"trilinearFiltering": (0, 2),
"skipDrawStart": (0, 100000),
"skipDrawEnd": (0, 100000),
"halfPixelOffset": (0, 3),
"roundSprite": (0, 2),
}
allowed_speed_hacks = ["mvuFlagSpeedHack", "InstantVU1SpeedHack", "MTVUSpeedHack"]
# Patches are allowed to have a 'default' key or a crc-32 key, followed by
allowed_patch_options = ["author", "content"]
@@ -94,6 +125,29 @@ def validate_game_fixes(serial, key, value):
validate_valid_options(serial, key, gamefix, allowed_game_fixes)
def validate_gs_hw_fix_value(serial, key, value):
low, high = 0, 1
if key in gs_hw_fix_ranges:
low, high = gs_hw_fix_ranges[key]
validate_int_option(serial, key, value, low, high)
def validate_gs_hw_fixes(serial, key, value):
if not isinstance(value, dict):
issue_list.append("[{}]: 'gsHWFixes' must be a valid object".format(serial))
return
for fix, fix_value in value.items():
validate_valid_options(serial, key, fix, allowed_gs_hw_fixes)
validate_gs_hw_fix_value(serial, fix, fix_value)
# skipdraw range must have end >= start
skip_draw_start = value["skipDrawStart"] if "skipDrawStart" in value else 0
skip_draw_end = value["skipDrawEnd"] if "skipDrawEnd" in value else 0
if isinstance(skip_draw_start, int) and isinstance(skip_draw_end, int) and skip_draw_end < skip_draw_start:
issue_list.append("[{}]: skipDrawStart({}) must be greater or equal to skipDrawEnd({})".format(
serial, skip_draw_start, skip_draw_end))
def validate_speed_hacks(serial, key, value):
if not isinstance(value, dict):
issue_list.append("[{}]: 'speedHacks' must be a valid object".format(serial))
@@ -145,6 +199,7 @@ option_validation_handlers = {
)
),
"gameFixes": (lambda serial, key, value: validate_game_fixes(serial, key, value)),
"gsHWFixes": (lambda serial, key, value: validate_gs_hw_fixes(serial, key, value)),
"speedHacks": (lambda serial, key, value: validate_speed_hacks(serial, key, value)),
"memcardFilters": (
lambda serial, key, value: validate_list_of_strings(serial, key, value)
Binary file not shown.
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -402,7 +402,7 @@
03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,
030000007e0500001920000000000000,Nintendo Switch N64 Controller,+rightx:b8,+righty:b2,-rightx:b3,-righty:b7,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows,
030000007e0500001720000000000000,Nintendo Switch Online Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,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:b2,y:b3,platform:Windows,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,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:b2,y:b3,misc1:b13,platform:Windows,
030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,
03000000550900001472000000000000,NVIDIA Controller,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,
03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows,
@@ -1112,6 +1112,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000000b000008040000,Microsoft Xbox One Elite 2,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,
050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e040000ea02000008040000,Microsoft Xbox One S,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,
060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
030000005e0400008902000021010000,Microsoft Xbox pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
+2 -2
View File
@@ -1269,7 +1269,7 @@ bool FileSystem::FindFiles(const char* Path, const char* Pattern, u32 Flags, Fin
bool FileSystem::StatFile(const char* path, struct stat* st)
{
return stat(path, st);
return stat(path, st) == 0;
}
bool FileSystem::StatFile(std::FILE* fp, struct stat* st)
@@ -1278,7 +1278,7 @@ bool FileSystem::StatFile(std::FILE* fp, struct stat* st)
if (fd < 0)
return false;
return fstat(fd, st);
return fstat(fd, st) == 0;
}
bool FileSystem::StatFile(const char* path, FILESYSTEM_STAT_DATA* sd)
+11
View File
@@ -153,6 +153,17 @@ namespace StringUtil
return (str.length() >= suffix_length && str.compare(str.length() - suffix_length, suffix_length, suffix) == 0);
}
/// StartsWith/EndsWith variants which aren't case sensitive.
static inline bool StartsWithNoCase(const std::string_view& str, const char* prefix)
{
return (Strncasecmp(str.data(), prefix, std::strlen(prefix)) == 0);
}
static inline bool EndsWithNoCase(const std::string_view& str, const char* suffix)
{
const std::size_t suffix_length = std::strlen(suffix);
return (str.length() >= suffix_length && Strncasecmp(str.data() + (str.length() - suffix_length), suffix, suffix_length) == 0);
}
/// Strip whitespace from the start/end of the string.
std::string_view StripWhitespace(const std::string_view& str);
+1 -1
View File
@@ -45,7 +45,7 @@ namespace x86Emitter
{
if (sibdest.Is8BitOp())
{
xOpWrite(sibdest.GetPrefix16(), 0x80, InstType, sibdest);
xOpWrite(sibdest.GetPrefix16(), 0x80, InstType, sibdest, 1);
xWrite<s8>(imm);
}
+4
View File
@@ -28,6 +28,7 @@
#include "pcsx2/Frontend/InputManager.h"
#include "pcsx2/Frontend/ImGuiManager.h"
#include "pcsx2/GS.h"
#include "pcsx2/GSDumpReplayer.h"
#include "pcsx2/HostDisplay.h"
#include "pcsx2/PAD/Host/PAD.h"
#include "pcsx2/PerformanceMetrics.h"
@@ -677,6 +678,9 @@ bool Host::BeginPresentFrame(bool frame_skip)
void Host::EndPresentFrame()
{
if (GSDumpReplayer::IsReplayingDump())
GSDumpReplayer::RenderUI();
ImGuiManager::RenderOSD();
s_host_display->EndPresent();
ImGuiManager::NewFrame();
+13 -9
View File
@@ -41,7 +41,9 @@ static void PrintCommandLineHelp(const char* progname)
std::fprintf(stderr, "\n");
std::fprintf(stderr, " -help: Displays this information and exits.\n");
std::fprintf(stderr, " -version: Displays version information and exits.\n");
std::fprintf(stderr, " -batch: Enables batch mode (exits after powering off).\n");
std::fprintf(stderr, " -batch: Enables batch mode (exits after shutting down).\n");
std::fprintf(stderr, " -elf <file>: Overrides the boot ELF with the specified filename.\n");
std::fprintf(stderr, " -disc <path>: Uses the specified host DVD drive as a source.\n");
std::fprintf(stderr, " -fastboot: Force fast boot for provided filename.\n");
std::fprintf(stderr, " -slowboot: Force slow boot for provided filename.\n");
std::fprintf(stderr, " -resume: Load resume save state. If a boot filename is provided,\n"
@@ -64,10 +66,8 @@ static void PrintCommandLineHelp(const char* progname)
static std::shared_ptr<VMBootParameters>& AutoBoot(std::shared_ptr<VMBootParameters>& autoboot)
{
if (!autoboot)
{
autoboot = std::make_shared<VMBootParameters>();
autoboot->source_type = CDVD_SourceType::NoDisc;
}
return autoboot;
}
@@ -134,6 +134,12 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
AutoBoot(autoboot)->elf_override = argv[++i];
continue;
}
else if (CHECK_ARG_PARAM("-disc"))
{
AutoBoot(autoboot)->source_type = CDVD_SourceType::Disc;
AutoBoot(autoboot)->filename = argv[++i];
continue;
}
else if (CHECK_ARG("-fullscreen"))
{
Console.WriteLn("Going fullscreen after booting.");
@@ -172,12 +178,10 @@ static bool ParseCommandLineOptions(int argc, char* argv[], std::shared_ptr<VMBo
#undef CHECK_ARG_PARAM
}
if (!AutoBoot(autoboot)->source.empty())
AutoBoot(autoboot)->source += ' ';
else
AutoBoot(autoboot)->source_type = CDVD_SourceType::Iso;
if (!AutoBoot(autoboot)->filename.empty())
AutoBoot(autoboot)->filename += ' ';
AutoBoot(autoboot)->source += argv[i];
AutoBoot(autoboot)->filename += argv[i];
}
return true;
+55 -17
View File
@@ -44,9 +44,15 @@
#include "svnrev.h"
static constexpr char DISC_IMAGE_FILTER[] =
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u);;Single-Track Raw Images (*.bin "
"*.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;CSO Images (*.cso);;"
"ELF Executables (*.elf);;IRX Executables (*.irx);;Playlists (*.m3u)");
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u *.gs *.gs.xz);;"
"Single-Track Raw Images (*.bin *.iso);;"
"Cue Sheets (*.cue);;"
"MAME CHD Images (*.chd);;"
"CSO Images (*.cso);;"
"ELF Executables (*.elf);;"
"IRX Executables (*.irx);;"
"Playlists (*.m3u);;"
"GS Dumps (*.gs *.gs.xz)");
const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion";
@@ -602,7 +608,10 @@ void MainWindow::clearProgressBar()
m_ui.statusBar->removeWidget(m_status_progress_widget);
}
bool MainWindow::isShowingGameList() const { return m_ui.mainContainer->currentIndex() == 0; }
bool MainWindow::isShowingGameList() const
{
return m_ui.mainContainer->currentIndex() == 0;
}
void MainWindow::switchToGameListView()
{
@@ -635,11 +644,20 @@ void MainWindow::switchToEmulationView()
m_display_widget->setFocus();
}
void MainWindow::refreshGameList(bool invalidate_cache) { m_game_list_widget->refresh(invalidate_cache); }
void MainWindow::refreshGameList(bool invalidate_cache)
{
m_game_list_widget->refresh(invalidate_cache);
}
void MainWindow::invalidateSaveStateCache() { m_save_states_invalidated = true; }
void MainWindow::invalidateSaveStateCache()
{
m_save_states_invalidated = true;
}
void MainWindow::reportError(const QString& title, const QString& message) { QMessageBox::critical(this, title, message); }
void MainWindow::reportError(const QString& title, const QString& message)
{
QMessageBox::critical(this, title, message);
}
bool MainWindow::confirmShutdown()
{
@@ -660,7 +678,10 @@ void MainWindow::requestExit()
close();
}
void Host::InvalidateSaveStateCache() { QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection); }
void Host::InvalidateSaveStateCache()
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection);
}
void MainWindow::onGameListRefreshProgress(const QString& status, int current, int total)
{
@@ -668,7 +689,10 @@ void MainWindow::onGameListRefreshProgress(const QString& status, int current, i
setProgressBar(current, total);
}
void MainWindow::onGameListRefreshComplete() { clearProgressBar(); }
void MainWindow::onGameListRefreshComplete()
{
clearProgressBar();
}
void MainWindow::onGameListSelectionChanged()
{
@@ -779,14 +803,13 @@ void MainWindow::onStartFileActionTriggered()
return;
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
VMManager::SetBootParametersForPath(filename.toStdString(), params.get());
params->filename = filename.toStdString();
g_emu_thread->startVM(std::move(params));
}
void MainWindow::onStartBIOSActionTriggered()
{
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
params->source_type = CDVD_SourceType::NoDisc;
g_emu_thread->startVM(std::move(params));
}
@@ -801,7 +824,10 @@ void MainWindow::onChangeDiscFromFileActionTriggered()
g_emu_thread->changeDisc(filename);
}
void MainWindow::onChangeDiscFromGameListActionTriggered() { switchToGameListView(); }
void MainWindow::onChangeDiscFromGameListActionTriggered()
{
switchToGameListView();
}
void MainWindow::onChangeDiscFromDeviceActionTriggered()
{
@@ -885,11 +911,20 @@ void MainWindow::onViewGamePropertiesActionTriggered()
SettingsDialog::openGamePropertiesDialog(nullptr, m_current_game_crc);
}
void MainWindow::onGitHubRepositoryActionTriggered() { QtUtils::OpenURL(this, AboutDialog::getGitHubRepositoryUrl()); }
void MainWindow::onGitHubRepositoryActionTriggered()
{
QtUtils::OpenURL(this, AboutDialog::getGitHubRepositoryUrl());
}
void MainWindow::onSupportForumsActionTriggered() { QtUtils::OpenURL(this, AboutDialog::getSupportForumsUrl()); }
void MainWindow::onSupportForumsActionTriggered()
{
QtUtils::OpenURL(this, AboutDialog::getSupportForumsUrl());
}
void MainWindow::onDiscordServerActionTriggered() { QtUtils::OpenURL(this, AboutDialog::getDiscordServerUrl()); }
void MainWindow::onDiscordServerActionTriggered()
{
QtUtils::OpenURL(this, AboutDialog::getDiscordServerUrl());
}
void MainWindow::onAboutActionTriggered()
{
@@ -1191,7 +1226,10 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height)
resize(QSize(std::max<qint32>(width, 1), std::max<qint32>(height + extra_height, 1)));
}
void MainWindow::destroyDisplay() { destroyDisplayWidget(); }
void MainWindow::destroyDisplay()
{
destroyDisplayWidget();
}
void MainWindow::focusDisplayWidget()
{
@@ -1398,7 +1436,7 @@ void MainWindow::loadSaveStateFile(const QString& filename, const QString& state
else
{
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
VMManager::SetBootParametersForPath(filename.toStdString(), params.get());
params->filename = filename.toStdString();
params->save_state = state_filename.toStdString();
g_emu_thread->startVM(std::move(params));
}
+3 -3
View File
@@ -141,8 +141,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
// HW Renderer Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawStart, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawEnd, "EmuCore/GS", "UserHacks_SkipDraw", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
@@ -156,7 +156,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
// HW Upscaling Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfPixelOffset, "EmuCore/GS", "UserHacks_HalfPixelOffset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.roundSprite, "EmuCore/GS", "UserHacks_RoundSprite", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.roundSprite, "EmuCore/GS", "UserHacks_round_sprite_offset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureOffsetX, "EmuCore/GS", "UserHacks_TCOffsetX", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureOffsetY, "EmuCore/GS", "UserHacks_TCOffsetY", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false);
+21 -5
View File
@@ -586,7 +586,7 @@
<item row="1" column="1">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Enable Hardware Renderer Fixes</string>
<string>Manual Hardware Renderer Fixes</string>
</property>
</widget>
</item>
@@ -661,10 +661,18 @@
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawRangeStart"/>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawRangeCount"/>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
@@ -809,7 +817,11 @@
</widget>
</item>
<item>
<widget class="QSpinBox" name="textureOffsetX"/>
<widget class="QSpinBox" name="textureOffsetX">
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_18">
@@ -819,7 +831,11 @@
</widget>
</item>
<item>
<widget class="QSpinBox" name="textureOffsetY"/>
<widget class="QSpinBox" name="textureOffsetY">
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
</layout>
</item>
+2
View File
@@ -983,6 +983,7 @@ if(PCSX2_CORE)
Frontend/InputManager.cpp
Frontend/InputSource.cpp
Frontend/LayeredSettingsInterface.cpp
GSDumpReplayer.cpp
HostSettings.cpp
VMManager.cpp
)
@@ -992,6 +993,7 @@ if(PCSX2_CORE)
Frontend/InputManager.h
Frontend/InputSource.h
Frontend/LayeredSettingsInterface.h
GSDumpReplayer.h
HostSettings.h
VMManager.h)
endif()
+9 -4
View File
@@ -58,6 +58,7 @@ enum SpeedhackId
Speedhack_mvuFlag = SpeedhackId_FIRST,
Speedhack_InstantVU1,
Speedhack_MTVU,
SpeedhackId_COUNT
};
@@ -441,7 +442,8 @@ struct Pcsx2Config
WrapGSMem : 1,
Mipmap : 1,
AA1 : 1,
UserHacks : 1,
PointListPalette : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_AutoFlush : 1,
UserHacks_CPUFBConversion : 1,
@@ -509,8 +511,8 @@ struct Pcsx2Config
int SWExtraThreads{2};
int SWExtraThreadsHeight{4};
int TVShader{0};
int SkipDraw{0};
int SkipDrawOffset{0};
int SkipDrawStart{0};
int SkipDrawEnd{0};
int UserHacks_HalfBottomOverride{-1};
int UserHacks_HalfPixelOffset{0};
@@ -543,6 +545,9 @@ struct Pcsx2Config
/// Sets user hack values to defaults when user hacks are not enabled.
void MaskUserHacks();
/// Sets user hack values to defaults when upscaling is not enabled.
void MaskUpscalingHacks();
/// Returns true if any of the hardware renderers are selected.
bool UseHardwareRenderer() const;
@@ -668,7 +673,7 @@ struct Pcsx2Config
* which is 2^32 * 512 byte sectors
* Note that we don't yet support
* 48bit LBA, so our limit is lower */
uint HddSizeSectors{0};
uint HddSizeSectors{40 * (1024 * 1024 * 1024 / 512)};
DEV9Options();
+45 -7
View File
@@ -48,7 +48,7 @@ SERIAL-12345: # !required! Serial number for the game, this is how games are loo
memcardFilters:
- "SERIAL-123"
- "SERIAL-456"
# You can define multple patches, but they are identified by the CRC.
# You can define multiple patches, but they are identified by the CRC.
patches:
default: # Default CRC!
author: "Some Person"
@@ -124,6 +124,44 @@ The clamp modes are also numerically based.
* `2` = Clamp **Extra** (clamp results as well as operands)
* `3` = Clamp **Extra+Preserve Sign**
## GS Hardware Fixes
[ ] = GameDB values
{ } = GUI options
( ) = Default values
### GS Hardware Mipmap Fixes
* mipmap [`0` or `1` or `2`] {Off, Basic, Full} Default: Automatic (No value, looks up GameDB)
* trilinearFiltering [`0` or `1` or `2`] {None, Trilinear, Trilinear Ultra} Default: None (`0`)
### GS Hardware General Fixes
* conservativeFramebuffer [`0` or `1`] {Off or On} Default: On (`1`)
* texturePreloading [`0` or `1` or `2`] {None, Partial or Full Hash Cache} Default: None (`0`)
### GS Hardware Renderer Fixes
* autoFlush [`0` or `1`] {Off or On} Default: Off (`0`)
* disableDepthSupport [`0` or `1`] {Off or On} Default: Off (`0`)
* fastTextureInvalidation [`0` or `1`] {Off or On} Default: Off (`0`)
* cpuFramebufferConversion [`0` or `1`] {Off or On} Default: Off (`0`)
* wrapGSMem [`0` or `1`] {Off or On} Default: Off (`0`)
* preloadFrameData [`0` or `1`] {Off or On} Default: Off (`0`)
* textureInsideRT [`0` or `1`] {Off or On} Default: Off (`0`)
* halfBottomOverride [`0` or `1`] {Force-Disabled or Force-Enabled} Default: Automatic (No value, looks up GameDB)
* pointListPalette [`0` or `1`] {Off or On} Default: Off (`0`)
### GS Hardware Upscaling Fixes
* alignSprite [`0` or `1`] {Off or On} Default: Off (`0`)
* mergeSprite [`0` or `1`] {Off or On} Default: Off (`0`)
* wildArmsHack [`0` or `1`] {Off or On} Default: Off (`0`)
* skipDrawStart [Value between `0` to `100000`] {0-100000} Default: Off (`0`)
* skipDrawEnd [Value between `0` to `100000`] {0-100000} Default: Off (`0`)
* halfPixelOffset [`0` or `1` or `2` or `3`] {Off, Normal Vertex, Special Texture or Special Texture Aggressive} Default: Off (`0`)
* roundSprite [`0` or `1` or `2`] {Off, Half or Full} Default: Off (`0`)
## Game Fixes
These values are case-sensitive so take care. If you incorrectly specify a GameFix, you will get a validation error on startup. Any invalid game-fixes will be dropped from the game's list of fixes.
@@ -184,12 +222,12 @@ These values are in a key-value format, where the value is assumed to be an inte
* `mvuFlagSpeedHack`
* Accepted Values - `0` / `1`
* Katamari Damacy have weird speed bug when this speed hack is enabled (and it is by default)
<!-- [list-item-spacing] Missing new line after list item->
- `InstantVU1SpeedHack`
- Accepted Values - `0` / `1`
- Games such as Parappa the Rapper 2 need VU1 to sync, so you can force disable the speedhack here
* `MTVUSpeedHack`
* Accepted Values - `0` / `1`
* T-bit games dislike MTVU and some games are incompatible with MTVU.
* `InstantVU1SpeedHack`
* Accepted Values - `0` / `1`
* Games such as Parappa the Rapper 2 need VU1 to sync, so you can force sync with this parameter.
## Memory Card Filter Override
+14 -3
View File
@@ -123,19 +123,19 @@ void GameList::FillBootParametersForEntry(VMBootParameters* params, const Entry*
{
if (entry->type == GameList::EntryType::PS1Disc || entry->type == GameList::EntryType::PS2Disc)
{
params->source = entry->path;
params->filename = entry->path;
params->source_type = CDVD_SourceType::Iso;
params->elf_override.clear();
}
else if (entry->type == GameList::EntryType::ELF)
{
params->source.clear();
params->filename.clear();
params->source_type = CDVD_SourceType::NoDisc;
params->elf_override = entry->path;
}
else
{
params->source.clear();
params->filename.clear();
params->source_type = CDVD_SourceType::NoDisc;
params->elf_override.clear();
}
@@ -568,6 +568,17 @@ const GameList::Entry* GameList::GetEntryForPath(const char* path)
return nullptr;
}
const GameList::Entry* GameList::GetEntryByCRC(u32 crc)
{
for (const Entry& entry : m_entries)
{
if (entry.crc == crc)
return &entry;
}
return nullptr;
}
const GameList::Entry* GameList::GetEntryBySerialAndCRC(const std::string_view& serial, u32 crc)
{
for (const Entry& entry : m_entries)
+1
View File
@@ -80,6 +80,7 @@ namespace GameList
std::unique_lock<std::recursive_mutex> GetLock();
const Entry* GetEntryByIndex(u32 index);
const Entry* GetEntryForPath(const char* path);
const Entry* GetEntryByCRC(u32 crc);
const Entry* GetEntryBySerialAndCRC(const std::string_view& serial, u32 crc);
u32 GetEntryCount();
+14
View File
@@ -630,3 +630,17 @@ void ImGuiManager::RenderOSD()
DrawOSDMessages();
}
float ImGuiManager::GetGlobalScale()
{
return s_global_scale;
}
ImFont* ImGuiManager::GetStandardFont()
{
return s_standard_font;
}
ImFont* ImGuiManager::GetFixedFont()
{
return s_fixed_font;
}
+11
View File
@@ -15,6 +15,8 @@
#pragma once
struct ImFont;
namespace ImGuiManager
{
/// Initializes ImGui, creates fonts, etc.
@@ -34,5 +36,14 @@ namespace ImGuiManager
/// Renders any on-screen display elements.
void RenderOSD();
/// Returns the scale of all on-screen elements.
float GetGlobalScale();
/// Returns the standard font for external drawing.
ImFont* GetStandardFont();
/// Returns the fixed-width font for external drawing.
ImFont* GetFixedFont();
} // namespace ImGuiManager
+17 -27
View File
@@ -310,7 +310,6 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
GSConfig = config;
GSConfig.Renderer = renderer;
GSConfig.MaskUserHacks();
if (!Host::AcquireHostDisplay(GetAPIForRenderer(renderer)))
{
@@ -723,7 +722,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
Pcsx2Config::GSOptions old_config(std::move(GSConfig));
GSConfig = new_config;
GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer;
GSConfig.MaskUserHacks();
if (!s_gs)
return;
@@ -758,21 +756,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// Options which aren't using the global struct yet, so we need to recreate all GS objects.
if (
GSConfig.ConservativeFramebuffer != old_config.ConservativeFramebuffer ||
GSConfig.AutoFlushSW != old_config.AutoFlushSW ||
GSConfig.PreloadFrameWithGSData != old_config.PreloadFrameWithGSData ||
GSConfig.WrapGSMem != old_config.WrapGSMem ||
GSConfig.Mipmap != old_config.Mipmap ||
GSConfig.AA1 != old_config.AA1 ||
GSConfig.UserHacks_AlignSpriteX != old_config.UserHacks_AlignSpriteX ||
GSConfig.UserHacks_AutoFlush != old_config.UserHacks_AutoFlush ||
GSConfig.UserHacks_CPUFBConversion != old_config.UserHacks_CPUFBConversion ||
GSConfig.UserHacks_DisableDepthSupport != old_config.UserHacks_DisableDepthSupport ||
GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation ||
GSConfig.UserHacks_DisableSafeFeatures != old_config.UserHacks_DisableSafeFeatures ||
GSConfig.UserHacks_MergePPSprite != old_config.UserHacks_MergePPSprite ||
GSConfig.UserHacks_WildHack != old_config.UserHacks_WildHack ||
GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt ||
GSConfig.DumpGSData != old_config.DumpGSData ||
GSConfig.SaveRT != old_config.SaveRT ||
GSConfig.SaveFrame != old_config.SaveFrame ||
@@ -784,12 +767,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight ||
GSConfig.UserHacks_HalfBottomOverride != old_config.UserHacks_HalfBottomOverride ||
GSConfig.UserHacks_HalfPixelOffset != old_config.UserHacks_HalfPixelOffset ||
GSConfig.UserHacks_RoundSprite != old_config.UserHacks_RoundSprite ||
GSConfig.UserHacks_TCOffsetX != old_config.UserHacks_TCOffsetX ||
GSConfig.UserHacks_TCOffsetY != old_config.UserHacks_TCOffsetY ||
GSConfig.ShadeBoost_Brightness != old_config.ShadeBoost_Brightness ||
GSConfig.ShadeBoost_Contrast != old_config.ShadeBoost_Contrast ||
GSConfig.ShadeBoost_Saturation != old_config.ShadeBoost_Saturation ||
@@ -806,17 +783,29 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// This is where we would do finer-grained checks in the future.
// For example, flushing the texture cache when mipmap settings change.
if (GSConfig.HWMipmap != old_config.HWMipmap || GSConfig.CRCHack != old_config.CRCHack)
if (GSConfig.CRCHack != old_config.CRCHack ||
GSConfig.PointListPalette != old_config.PointListPalette)
{
// for automatic mipmaps, we need to reload the crc
s_gs->SetGameCRC(s_gs->GetGameCRC(), s_gs->GetGameCRCOptions());
}
// reload texture cache when trilinear filtering or mipmap options change
if (GSConfig.HWMipmap != old_config.HWMipmap ||
// renderer-specific options (e.g. auto flush, TC offset)
s_gs->UpdateSettings(old_config);
// reload texture cache when trilinear filtering or TC options change
if (
(GSConfig.UseHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) ||
GSConfig.ConservativeFramebuffer != old_config.ConservativeFramebuffer ||
GSConfig.TexturePreloading != old_config.TexturePreloading ||
GSConfig.UserHacks_TriFilter != old_config.UserHacks_TriFilter ||
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion)
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
GSConfig.PreloadFrameWithGSData != old_config.PreloadFrameWithGSData ||
GSConfig.WrapGSMem != old_config.WrapGSMem ||
GSConfig.UserHacks_CPUFBConversion != old_config.UserHacks_CPUFBConversion ||
GSConfig.UserHacks_DisableDepthSupport != old_config.UserHacks_DisableDepthSupport ||
GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation ||
GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt)
{
s_gs->PurgeTextureCache();
s_gs->PurgePool();
@@ -1358,6 +1347,7 @@ void GSApp::Init()
m_default_configuration["override_GL_ARB_texture_barrier"] = "-1";
m_default_configuration["paltex"] = "0";
m_default_configuration["png_compression_level"] = std::to_string(Z_BEST_SPEED);
m_default_configuration["PointListPalette"] = "0";
m_default_configuration["PrecacheTextureReplacements"] = "0";
m_default_configuration["preload_frame_with_gs_data"] = "0";
m_default_configuration["Renderer"] = std::to_string(static_cast<int>(GSRendererType::Auto));
+33 -185
View File
@@ -26,14 +26,7 @@ const CRC::Game CRC::m_games[] =
{0x68CE6801, ArTonelico2, JP, 0},
{0xCE2C1DBF, ArTonelico2, EU, 0},
{0x2113EA2E, MetalSlug6, JP, 0},
{0x42E05BAF, TomoyoAfter, JP, PointListPalette},
{0x7800DC84, Clannad, JP, PointListPalette},
{0xA6167B59, Lamune, JP, PointListPalette},
{0xDDB59F46, KyuuketsuKitanMoonties, JP, PointListPalette},
{0xC8EE2562, PiaCarroteYoukosoGPGakuenPrincess, JP, PointListPalette},
{0x6CF94A43, KazokuKeikakuKokoroNoKizuna, JP, PointListPalette},
{0xEDAF602D, DuelSaviorDestiny, JP, PointListPalette},
{0xBB63D785, OneTwentyYenStories, JP, PointListPalette}, // no Haru: 120 Yen Stories
{0xA6167B59, Lamune, JP, 0},
{0xA39517AB, FFX, EU, 0},
{0x78D83FD5, FFX, EU, 0}, // Demo
{0xA39517AE, FFX, FR, 0},
@@ -94,12 +87,6 @@ const CRC::Game CRC::m_games[] =
{0x71521863, SFEX3, US, 0},
{0x63642E9F, SFEX3, JP, 0},
{0xCA1F6E53, SFEX3, JP, 0}, // Taikenban Disc (Demo/Trial)
{0xC19A374E, SoTC, US, 0},
{0x7D8F539A, SoTC, EU, 0},
{0x0F0C4A9C, SoTC, EU, 0},
{0x877F3436, SoTC, JP, 0},
{0xA17D6AAA, SoTC, KO, 0},
{0x877B3D35, SoTC, CH, 0},
{0x6F8545DB, ICO, US, 0},
{0x48CDF317, ICO, US, 0}, // Demo
{0xB01A4C95, ICO, JP, 0},
@@ -163,9 +150,6 @@ const CRC::Game CRC::m_games[] =
{0xA32F7CD0, AceCombat4, US, 0}, // Also needed for automatic mipmapping
{0x5ED8FB53, AceCombat4, JP, 0},
{0x1B9B7563, AceCombat4, EU, 0},
{0x39B574F0, AceCombat5, US, 0}, // The Unsung War
{0x86089F31, AceCombat5, JP, 0},
{0x1D54FEA9, AceCombat5, EU, 0}, // Squadron Leader
{0xFC46EA61, Tekken5, JP, 0},
{0x1F88EE37, Tekken5, EU, 0},
{0x1F88BECD, Tekken5, EU, 0}, // language selector...
@@ -206,7 +190,6 @@ const CRC::Game CRC::m_games[] =
{0xA3643EB1, GiTS, KO, 0},
{0x28557423, GiTS, RU, 0},
{0xBF6F101F, GiTS, EU, 0}, // same CRC as another US disc
{0xF442260C, MajokkoALaMode2, JP, PointListPalette},
{0xA616A6C2, TalesOfAbyss, US, 0},
{0x14FE77F7, TalesOfAbyss, US, 0},
{0xAA5EC3A3, TalesOfAbyss, JP, 0},
@@ -243,10 +226,10 @@ const CRC::Game CRC::m_games[] =
{0xC5B75C7C, Oneechanbara2Special, JP, 0},
{0xC725CC6C, Oneechanbara2Special, JP, 0},
{0x07608CA2, Oneechanbara2Special, EU, 0}, // Zombie Hunters 2
{0xE0347841, XenosagaE3, JP, TextureInsideRt},
{0xA707236E, XenosagaE3, JP, TextureInsideRt}, // Demo
{0xA4E88698, XenosagaE3, CH, TextureInsideRt},
{0x2088950A, XenosagaE3, US, TextureInsideRt},
{0xE0347841, XenosagaE3, JP, 0},
{0xA707236E, XenosagaE3, JP, 0}, // Demo
{0xA4E88698, XenosagaE3, CH, 0},
{0x2088950A, XenosagaE3, US, 0},
{0xB1995E29, ShadowofRome, EU, 0},
{0x958DCA28, ShadowofRome, EU, 0},
{0x57818AF6, ShadowofRome, US, 0},
@@ -315,14 +298,14 @@ const CRC::Game CRC::m_games[] =
{0x812C5A96, ShinOnimusha, EU, 0},
{0xFE44479E, ShinOnimusha, US, 0},
{0xFFDE85E9, ShinOnimusha, US, 0},
{0xE21404E2, GetawayGames, US, TextureInsideRt}, // Getaway
{0xE8249852, GetawayGames, JP, TextureInsideRt}, // Getaway
{0x458485EF, GetawayGames, EU, TextureInsideRt}, // Getaway
{0x5DFBE144, GetawayGames, EU, TextureInsideRt}, // Getaway
{0xE78971DF, GetawayGames, US, TextureInsideRt}, // GetawayBlackMonday
{0x342D97FA, GetawayGames, US, TextureInsideRt}, // GetawayBlackMonday Demo
{0xE8C0AD1A, GetawayGames, JP, TextureInsideRt}, // GetawayBlackMonday
{0x09C3DF79, GetawayGames, EU, TextureInsideRt}, // GetawayBlackMonday
{0xE21404E2, GetawayGames, US, 0}, // Getaway
{0xE8249852, GetawayGames, JP, 0}, // Getaway
{0x458485EF, GetawayGames, EU, 0}, // Getaway
{0x5DFBE144, GetawayGames, EU, 0}, // Getaway
{0xE78971DF, GetawayGames, US, 0}, // GetawayBlackMonday
{0x342D97FA, GetawayGames, US, 0}, // GetawayBlackMonday Demo
{0xE8C0AD1A, GetawayGames, JP, 0}, // GetawayBlackMonday
{0x09C3DF79, GetawayGames, EU, 0}, // GetawayBlackMonday
{0x1130BF23, SakuraTaisen, CH, 0},
{0x4FAE8B83, SakuraTaisen, KO, 0},
{0xEF06DBD6, SakuraWarsSoLongMyLove, JP, 0},
@@ -350,161 +333,26 @@ const CRC::Game CRC::m_games[] =
{0XE1BF5DCA, SuperManReturns, US, 0},
{0XE8F7BAB6, SuperManReturns, EU, 0},
{0x06A7506A, SacredBlaze, JP, 0},
{0x9C712FF0, Jak1, EU, TextureInsideRt}, // Jak and Daxter: The Precursor Legacy
{0x1B3976AB, Jak1, US, TextureInsideRt},
{0x472E7699, Jak1, US, TextureInsideRt}, // Greatest Hits
{0x96A608C5, Jak1, US, TextureInsideRt}, // Cingular Wireless Demo, PS Underground Demo
{0xEDE4FE64, Jak1, JP, TextureInsideRt}, // Jak x Daxter: Kyuusekai no Isan
{0x2A7FD3B4, Jak1, JP, TextureInsideRt}, // Demo
{0x2479F4A9, Jak2, EU, TextureInsideRt},
{0xF41C1B29, Jak2, EU, TextureInsideRt}, // Demo
{0x9184AAF1, Jak2, US, TextureInsideRt},
{0xA2034C69, Jak2, US, TextureInsideRt}, // Demo
{0x25FE4D23, Jak2, KO, TextureInsideRt},
{0xB4976DAF, Jak2, JP, TextureInsideRt}, // Jak II: Jak x Daxter 2
{0x43D4FF3E, Jak2, JP, TextureInsideRt}, // Demo
{0x12804727, Jak3, EU, TextureInsideRt},
{0xE59E10BF, Jak3, EU, TextureInsideRt},
{0xCA68E4D5, Jak3, EU, TextureInsideRt}, // Demo
{0x644CFD03, Jak3, US, TextureInsideRt},
{0xD401BC20, Jak3, US, TextureInsideRt}, // Demo
{0xD1368EAE, Jak3, KO, TextureInsideRt},
{0xDF659E77, JakX, EU, TextureInsideRt}, // Jak X: Combat Racing
{0xC20596DB, JakX, EU, TextureInsideRt}, // Beta Trial Disc, v0.01
{0x3091E6FB, JakX, US, TextureInsideRt},
{0xC417D919, JakX, US, TextureInsideRt}, // Demo
{0xDA366A53, JakX, US, TextureInsideRt}, // Public Beta v.1
{0x7B564230, JakX, US, TextureInsideRt}, // Jak and Daxter Complete Trilogy Demo
{0xDBA28C59, JakX, US, TextureInsideRt}, // Greatest Hits
{0x4653CA3E, HarleyDavidson, US, 0},
// Games list for Automatic Mipmapping
// Basic mipmapping
{0x194C9F38, AceCombatZero, EU, 0}, // Ace Combat: The Belkan War
{0x65729657, AceCombatZero, US, 0},
{0xA04B52DB, AceCombatZero, JP, 0},
{0x2799A4E5, AceCombatZero, KO, 0},
{0x09B3AD4D, ApeEscape2, EU, 0},
{0xADCDCB88, ApeEscape2, EU, 0}, // Spanish version
{0xBBB21612, ApeEscape2, DE, 0},
{0xE2B8D3B2, ApeEscape2, IT, 0},
{0x8B6FE2EA, ApeEscape2, FR, 0},
{0xBDD9F5E1, ApeEscape2, US, 0},
{0xFE0A6AB6, ApeEscape2, JP, 0}, // Saru! Get You! 2
{0x64A9982B, ApeEscape2, CH, 0},
{0xEC8EF2DE, Barnyard, US, 0}, // Nickelodeon: Barnyard
{0x0B2F3DEE, Barnyard, KO, 0},
{0x5267A845, Barnyard, EU, 0},
{0x0940508D, BrianLaraInternationalCricket, EU, 0},
{0x0BAA8DD8, DarkCloud, EU, 0},
{0x1DF41F33, DarkCloud, US, 0},
{0xA5C05C78, DarkCloud, US, 0},
{0x60AA5049, DarkCloud, KO, 0},
{0xECD8E386, DarkCloud, JP, 0},
{0x67A29886, DestroyAllHumans, US, 0},
{0xE3E8E893, DestroyAllHumans, EU, 0},
{0x42DF8C8C, DestroyAllHumans2, US, 0},
{0x743E10C2, DestroyAllHumans2, EU, 0},
{0x67C38BAA, FIFA03, US, 0},
{0x722BBD62, FIFA03, EU, 0},
{0x2BCCF704, FIFA03, EU, 0},
{0xCC6AA742, FIFA04, KO, 0},
{0x2C6A4E2E, FIFA04, US, 0},
{0x684ADFC6, FIFA04, EU, 0},
{0x972611BB, FIFA05, US, 0},
{0x972719A3, FIFA05, EU, 0},
{0xC5473413, HarryPotterATCOS, NoRegion, 0}, // EU and US versions have the same CRC - Chamber Of Secrets
{0xE1963055, HarryPotterATCOS, JP, 0}, // Harry Potter to Himitsu no Heya
{0xE90BE9F8, HarryPotterATCOS, JP, 0}, // Coca Cola original Version
{0xB38CC628, HarryPotterATGOF, US, 0},
{0xCDE017A7, HarryPotterATGOF, KO, 0},
{0xB18DC525, HarryPotterATGOF, EU, 0},
{0x9C3A84F4, HarryPotterATHBP, US, 0}, // Half-Blood Prince
{0xCB598BC2, HarryPotterATHBP, EU, 0},
{0x51E019BC, HarryPotterATPOA, NoRegion, 0}, // EU and US versions have the same CRC - Prisoner of Azkaban
{0x99A8B4FF, HarryPotterATPOA, KO, 0},
{0xA8901AD6, HarryPotterATPOA, JP, 0}, // Harry Potter to Azkaban no Shuujin
{0x51E417AA, HarryPotterATPOA, EU, 0},
{0x4C01B1B0, HarryPotterOOTP, US, 0}, // Order Of The Phoenix
{0x01A9BF0E, HarryPotterOOTP, EU, 0},
{0x960FFA6A, JurassicPark, EU, 0},
{0xA99B8FE7, JurassicPark, US, 0},
{0x230CB71D, SoulReaver2, US, 0},
{0x1771BFE4, SoulReaver2, US, 0},
{0x6F991F52, SoulReaver2, JP, 0},
{0x1B7FF35A, SoulReaver2, KO, 0},
{0x6D8B4CD1, SoulReaver2, EU, 0},
{0x728AB07C, LegacyOfKainDefiance, US, 0},
{0xBCAD1E8A, LegacyOfKainDefiance, EU, 0},
{0x28D09BF9, NicktoonsUnite, US, 0},
{0xF25266C4, NicktoonsUnite, EU, 0}, // Nickelodeon SpongeBob SquarePants And Friends Unite
{0x7AE1C04B, Persona3, US, 0}, // Regular Version
{0x05C3D28F, Persona3, JP, 0},
{0xBCD68B1E, Persona3, KO, 0},
{0x8A557EE5, Persona3, EU, 0},
{0x94A82AAA, Persona3, US, 0}, // FES
{0x232C7D72, Persona3, JP, 0},
{0x8897C208, Persona3, KO, 0},
{0xF64A6AE5, Persona3, EU, 0},
{0x2BDA8ADB, ProjectSnowblind, US, 0},
{0xF00CA82B, ProjectSnowblind, EU, 0},
{0xF1583665, ProjectSnowblind, EU, 0},
{0xA56A0525, Quake3Revolution, US, 0},
{0x2064ACE6, Quake3Revolution, EU, 0},
{0xCE4933D0, RatchetAndClank, US, 0},
{0x6F191506, RatchetAndClank, US, 0}, // E3 Demo
{0x81CBFEA2, RatchetAndClank, US, 0}, // EB Games Demo
{0x56A35F77, RatchetAndClank, JP, 0},
{0x76F724A3, RatchetAndClank, EU, 0},
{0x6A8F18B9, RatchetAndClank, EU, 0},
{0x5C19F3B7, RatchetAndClank, EU, 0}, // Regular Demo
{0xB3A71D10, RatchetAndClank2, US, 0}, // Going Commando
{0x38996035, RatchetAndClank2, US, 0},
{0xF700EE7E, RatchetAndClank2, US, 0}, // Regular Demo
{0xF67ADF58, RatchetAndClank2, US, 0}, // Retail Employees Demo
{0xDF6F94A1, RatchetAndClank2, US, 0}, // Demo - Going Commando & Jak II
{0x89A26EC9, RatchetAndClank2, KO, 0},
{0x8CAA5F16, RatchetAndClank2, JP, 0}, // Gagaga! Ginga no Commando-ssu
{0x2F486E6F, RatchetAndClank2, EU, 0},
{0x45FE0CC4, RatchetAndClank3, US, 0}, // Up Your Arsenal
{0x2A12175A, RatchetAndClank3, US, 0}, // Regular Demo
{0xCC53C3B4, RatchetAndClank3, US, 0}, // Public Beta v1.0
{0x9FCC4BA4, RatchetAndClank3, KO, 0},
{0x64DC6000, RatchetAndClank3, JP, 0}, // Totsugeki! Galactic Rangers
{0x17125698, RatchetAndClank3, EU, 0},
{0x4A85FC67, RatchetAndClank3, EU, 0}, // Beta Trial Disc
{0x9BFBCD42, RatchetAndClank4, US, 0}, // Deadlocked
{0xD301186D, RatchetAndClank4, US, 0}, // Regular Demo
{0xBDF8A887, RatchetAndClank4, US, 0}, // Public Beta v.1
{0x529BC7CC, RatchetAndClank4, KO, 0},
{0x2EC9DA96, RatchetAndClank4, JP, 0}, // GiriGiri Ginga no Giga Battle
{0x76975025, RatchetAndClank4, JP, 0}, // Regular Demo
{0xD697D204, RatchetAndClank4, EU, 0}, // Ratchet Gladiator
{0x8661F7BA, RatchetAndClank5, US, 0}, // Size Matters
{0x7029DCE6, RatchetAndClank5, KO, 0},
{0x9ADCF7AF, RatchetAndClank5, JP, 0}, // Gekitotsu! Dodeka Ginga no Miri Miri Gundan
{0xFCB981D5, RatchetAndClank5, EU, 0},
{0x8634861F, RickyPontingInternationalCricket, EU, 0},
{0xDDAC3815, Shox, US, 0},
{0xF84FE9DE, Shox, KO, 0},
{0x09F4038B, Shox, EU, 0},
{0x78FFA39F, Shox, EU, 0},
{0x3DF10389, Shox, EU, 0},
{0x43CC009B, SlamTennis, EU, 0},
{0xF17AF8BD, TheIncredibleHulkUD, US, 0},
{0xEA8D4BDF, TheIncredibleHulkUD, US, 0},
{0x6B3D50A5, TheIncredibleHulkUD, EU, 0},
{0x2B58234D, TribesAerialAssault, US, 0},
{0x4D22DB95, Whiplash, US, 0},
{0xE8A97250, Whiplash, EU, 0},
{0xB1BE3E51, Whiplash, EU, 0},
{0x4C33FA2A, IndianaJonesAndTheEmperorsTomb, US, TextureInsideRt}, // TODO Add more CRCs (https://pcsx2.net/compatibility-list.html).
{0xAE0E098F, IndianaJonesAndTheEmperorsTomb, DE, TextureInsideRt},
{0xBBC3EFFA, WildArms4, US, TextureInsideRt}, // TODO Add more CRCs (https://pcsx2.net/compatibility-list.html).
{0x36802E57, BeyondGoodAndEvil, US, TextureInsideRt}, // TODO Add more CRCs (https://pcsx2.net/compatibility-list.html).
{0x08FFF00D, SSX3, US, TextureInsideRt},
{0xCE942B2A, SSX3, EU, TextureInsideRt}, // TODO Add more CRCs (https://pcsx2.net/compatibility-list.html).
{0x1FCC0CFB, DrivingEmotionTypeS, US, TextureInsideRt},
{0x034836F8, DrivingEmotionTypeS, JP, TextureInsideRt}, // TODO Add more CRCs (https://pcsx2.net/compatibility-list.html).
{0x2479F4A9, Jak2, EU, 0},
{0xF41C1B29, Jak2, EU, 0}, // Demo
{0x9184AAF1, Jak2, US, 0},
{0xA2034C69, Jak2, US, 0}, // Demo
{0x25FE4D23, Jak2, KO, 0},
{0xB4976DAF, Jak2, JP, 0}, // Jak II: Jak x Daxter 2
{0x43D4FF3E, Jak2, JP, 0}, // Demo
{0x12804727, Jak3, EU, 0},
{0xE59E10BF, Jak3, EU, 0},
{0xCA68E4D5, Jak3, EU, 0}, // Demo
{0x644CFD03, Jak3, US, 0},
{0xD401BC20, Jak3, US, 0}, // Demo
{0xD1368EAE, Jak3, KO, 0},
{0xDF659E77, JakX, EU, 0}, // Jak X: Combat Racing
{0xC20596DB, JakX, EU, 0}, // Beta Trial Disc, v0.01
{0x3091E6FB, JakX, US, 0},
{0xC417D919, JakX, US, 0}, // Demo
{0xDA366A53, JakX, US, 0}, // Public Beta v.1
{0x7B564230, JakX, US, 0}, // Jak and Daxter Complete Trilogy Demo
{0xDBA28C59, JakX, US, 0}, // Greatest Hits
};
std::map<u32, const CRC::Game*> CRC::m_map;
-57
View File
@@ -23,116 +23,65 @@ public:
enum Title
{
NoTitle,
AceCombatZero,
AceCombat4,
AceCombat5,
ApeEscape2,
ArTonelico2,
Barnyard,
BeyondGoodAndEvil,
BigMuthaTruckers,
BrianLaraInternationalCricket,
BurnoutGames,
Clannad,
CrashBandicootWoC,
DarkCloud,
DBZBT2,
DBZBT3,
DeathByDegreesTekkenNinaWilliams,
DestroyAllHumans,
DestroyAllHumans2,
DrivingEmotionTypeS,
DuelSaviorDestiny,
EvangelionJo,
FFX,
FFX2,
FFXII,
FIFA03,
FIFA04,
FIFA05,
FightingBeautyWulong,
GetawayGames,
GiTS,
GodHand,
GodOfWar,
GodOfWar2,
HarleyDavidson,
HarryPotterATCOS,
HarryPotterATGOF,
HarryPotterATHBP,
HarryPotterATPOA,
HarryPotterOOTP,
HauntingGround,
ICO,
IkkiTousen,
IndianaJonesAndTheEmperorsTomb,
Jak1,
Jak2,
Jak3,
JakX,
JurassicPark,
KazokuKeikakuKokoroNoKizuna,
KnightsOfTheTemple2,
Kunoichi,
KyuuketsuKitanMoonties,
Lamune,
LegacyOfKainDefiance,
MajokkoALaMode2,
Manhunt2,
MetalSlug6,
MidnightClub3,
NicktoonsUnite,
Okami,
Oneechanbara2Special,
OneTwentyYenStories,
Persona3,
PiaCarroteYoukosoGPGakuenPrincess,
PolyphonyDigitalGames,
ProjectSnowblind,
Quake3Revolution,
RatchetAndClank,
RatchetAndClank2,
RatchetAndClank3,
RatchetAndClank4,
RatchetAndClank5,
RedDeadRevolver,
RickyPontingInternationalCricket,
RozenMaidenGebetGarden,
SacredBlaze,
SakuraTaisen,
SakuraWarsSoLongMyLove,
SFEX3,
ShadowHearts,
ShadowofRome,
ShinOnimusha,
Shox,
Simple2000Vol114,
SkyGunner,
SlamTennis,
SMTNocturne,
SonicUnleashed,
SoTC,
SoulReaver2,
Spartan,
SteambotChronicles,
SSX3,
SuperManReturns,
SVCChaos,
TalesOfAbyss,
TalesOfLegendia,
TalesofSymphonia,
Tekken5,
TheIncredibleHulkUD,
TombRaiderAnniversary,
TombRaiderLegend,
TombRaiderUnderworld,
TriAceGames,
TribesAerialAssault,
TomoyoAfter,
UltramanFightingEvolution,
UrbanReign,
Whiplash,
WildArms4,
XenosagaE3,
YakuzaGames,
ZettaiZetsumeiToshi2,
@@ -156,12 +105,6 @@ public:
RegionCount,
};
enum Flags
{
PointListPalette = 1,
TextureInsideRt = 2,
};
struct Game
{
u32 crc;
+42 -9
View File
@@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "GSDump.h"
#include "GSExtra.h"
#include "GSState.h"
GSDumpBase::GSDumpBase(const std::string& fn)
: m_frames(0)
@@ -32,10 +33,38 @@ GSDumpBase::~GSDumpBase()
fclose(m_gs);
}
void GSDumpBase::AddHeader(u32 crc, const freezeData& fd, const GSPrivRegSet* regs)
void GSDumpBase::AddHeader(const std::string& serial, u32 crc,
u32 screenshot_width, u32 screenshot_height, const u32* screenshot_pixels,
const freezeData& fd, const GSPrivRegSet* regs)
{
AppendRawData(&crc, 4);
AppendRawData(&fd.size, 4);
// New header: CRC of FFFFFFFF, secondary header, full header follows.
const u32 fake_crc = 0xFFFFFFFFu;
AppendRawData(&fake_crc, 4);
// Compute full header size (with serial).
// This acts as the state size for loading older dumps.
const u32 screenshot_size = screenshot_width * screenshot_height * sizeof(screenshot_pixels[0]);
const u32 header_size = sizeof(GSDumpHeader) + static_cast<u32>(serial.size()) + screenshot_size;
AppendRawData(&header_size, 4);
// Write hader.
GSDumpHeader header = {};
header.state_version = GSState::STATE_VERSION;
header.state_size = fd.size;
header.crc = crc;
header.serial_offset = sizeof(header);
header.serial_size = static_cast<u32>(serial.size());
header.screenshot_width = screenshot_width;
header.screenshot_height = screenshot_height;
header.screenshot_offset = header.serial_offset + header.serial_size;
header.screenshot_size = screenshot_size;
AppendRawData(&header, sizeof(header));
if (!serial.empty())
AppendRawData(serial.data(), serial.size());
if (screenshot_pixels)
AppendRawData(screenshot_pixels, screenshot_size);
// Then the real state data.
AppendRawData(fd.data, fd.size);
AppendRawData(regs, sizeof(*regs));
}
@@ -92,18 +121,20 @@ void GSDumpBase::Write(const void* data, size_t size)
// GSDump implementation
//////////////////////////////////////////////////////////////////////
GSDump::GSDump(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs)
GSDumpUncompressed::GSDumpUncompressed(const std::string& fn, const std::string& serial, u32 crc,
u32 screenshot_width, u32 screenshot_height, const u32* screenshot_pixels,
const freezeData& fd, const GSPrivRegSet* regs)
: GSDumpBase(fn + ".gs")
{
AddHeader(crc, fd, regs);
AddHeader(serial, crc, screenshot_width, screenshot_height, screenshot_pixels, fd, regs);
}
void GSDump::AppendRawData(const void* data, size_t size)
void GSDumpUncompressed::AppendRawData(const void* data, size_t size)
{
Write(data, size);
}
void GSDump::AppendRawData(u8 c)
void GSDumpUncompressed::AppendRawData(u8 c)
{
Write(&c, 1);
}
@@ -112,7 +143,9 @@ void GSDump::AppendRawData(u8 c)
// GSDumpXz implementation
//////////////////////////////////////////////////////////////////////
GSDumpXz::GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs)
GSDumpXz::GSDumpXz(const std::string& fn, const std::string& serial, u32 crc,
u32 screenshot_width, u32 screenshot_height, const u32* screenshot_pixels,
const freezeData& fd, const GSPrivRegSet* regs)
: GSDumpBase(fn + ".gs.xz")
{
m_strm = LZMA_STREAM_INIT;
@@ -123,7 +156,7 @@ GSDumpXz::GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const G
return;
}
AddHeader(crc, fd, regs);
AddHeader(serial, crc, screenshot_width, screenshot_height, screenshot_pixels, fd, regs);
}
GSDumpXz::~GSDumpXz()
+27 -6
View File
@@ -23,7 +23,7 @@
/*
Dump file format:
- [crc/4] [state size/4] [state data/size] [PMODE/0x2000] [id/1] [data/?] .. [id/1] [data/?]
- [0xFFFFFFFF] [Header] [state size/4] [state data/size] [PMODE/0x2000] [id/1] [data/?] .. [id/1] [data/?]
Transfer data (id == 0)
- [0/1] [path index/1] [size/4] [data/size]
@@ -39,6 +39,21 @@ Regs data (id == 3)
*/
#pragma pack(push, 4)
struct GSDumpHeader
{
u32 state_version; ///< Must always be first in struct to safely prevent old PCSX2 versions from crashing.
u32 state_size;
u32 serial_offset;
u32 serial_size;
u32 crc;
u32 screenshot_width;
u32 screenshot_height;
u32 screenshot_offset;
u32 screenshot_size;
};
#pragma pack(pop)
class GSDumpBase
{
int m_frames;
@@ -46,7 +61,9 @@ class GSDumpBase
FILE* m_gs;
protected:
void AddHeader(u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
void AddHeader(const std::string& serial, u32 crc,
u32 screenshot_width, u32 screenshot_height, const u32* screenshot_pixels,
const freezeData& fd, const GSPrivRegSet* regs);
void Write(const void* data, size_t size);
virtual void AppendRawData(const void* data, size_t size) = 0;
@@ -61,14 +78,16 @@ public:
bool VSync(int field, bool last, const GSPrivRegSet* regs);
};
class GSDump final : public GSDumpBase
class GSDumpUncompressed final : public GSDumpBase
{
void AppendRawData(const void* data, size_t size) final;
void AppendRawData(u8 c) final;
public:
GSDump(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDump() = default;
GSDumpUncompressed(const std::string& fn, const std::string& serial, u32 crc,
u32 screenshot_width, u32 screenshot_height, const u32* screenshot_pixels,
const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDumpUncompressed() = default;
};
class GSDumpXz final : public GSDumpBase
@@ -83,6 +102,8 @@ class GSDumpXz final : public GSDumpBase
void AppendRawData(u8 c);
public:
GSDumpXz(const std::string& fn, u32 crc, const freezeData& fd, const GSPrivRegSet* regs);
GSDumpXz(const std::string& fn, const std::string& serial, u32 crc,
u32 screenshot_width, u32 screenshot_height, const u32* screenshot_pixels,
const freezeData& fd, const GSPrivRegSet* regs);
virtual ~GSDumpXz();
};
+12
View File
@@ -106,6 +106,7 @@ GSLocalMemory::GSLocalMemory()
psm.pgs = GSVector2i(64, 32);
psm.msk = 0xff;
psm.depth = 0;
psm.fmsk = 0xffffffff;
}
m_psm[PSM_PSGPU24].info = GSLocalMemory::swizzle16;
@@ -346,6 +347,17 @@ GSLocalMemory::GSLocalMemory()
m_psm[PSM_PSMZ24].depth = 1;
m_psm[PSM_PSMZ16].depth = 1;
m_psm[PSM_PSMZ16S].depth = 1;
m_psm[PSM_PSMCT24].fmsk = 0x00FFFFFF;
m_psm[PSM_PSGPU24].fmsk = 0x00FFFFFF;
m_psm[PSM_PSMCT16].fmsk = 0x80F8F8F8;
m_psm[PSM_PSMCT16S].fmsk = 0x80F8F8F8;
m_psm[PSM_PSMT8H].fmsk = 0xFF000000;
m_psm[PSM_PSMT4HL].fmsk = 0x0F000000;
m_psm[PSM_PSMT4HH].fmsk = 0xF0000000;
m_psm[PSM_PSMZ24].fmsk = 0x00FFFFFF;
m_psm[PSM_PSMZ16].fmsk = 0x80F8F8F8;
m_psm[PSM_PSMZ16S].fmsk = 0x80F8F8F8;
}
GSLocalMemory::~GSLocalMemory()
+1
View File
@@ -468,6 +468,7 @@ public:
u16 bpp, trbpp, pal, fmt;
GSVector2i bs, pgs;
u8 msk, depth;
u32 fmsk;
};
static psm_t m_psm[64];
+211 -43
View File
@@ -14,28 +14,18 @@
*/
#include "PrecompiledHeader.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "GSDump.h"
#include "GSLzma.h"
GSDumpFile::GSDumpFile(char* filename, const char* repack_filename)
{
m_fp = fopen(filename, "rb");
if (m_fp == nullptr)
{
fprintf(stderr, "failed to open %s\n", filename);
throw "BAD"; // Just exit the program
}
m_repack_fp = nullptr;
if (repack_filename)
{
m_repack_fp = fopen(repack_filename, "wb");
if (m_repack_fp == nullptr)
fprintf(stderr, "failed to open %s for repack\n", repack_filename);
}
}
using namespace GSDumpTypes;
GSDumpFile::GSDumpFile(FILE* file, FILE* repack_file)
: m_repack_fp(repack_file), m_fp(file)
: m_repack_fp(repack_file)
, m_fp(file)
{
}
@@ -57,13 +47,203 @@ GSDumpFile::~GSDumpFile()
fclose(m_repack_fp);
}
/******************************************************************/
GSDumpLzma::GSDumpLzma(char* filename, const char* repack_filename)
: GSDumpFile(filename, repack_filename)
std::unique_ptr<GSDumpFile> GSDumpFile::OpenGSDump(const char* filename, const char* repack_filename /*= nullptr*/)
{
Initialize();
std::FILE* fp = FileSystem::OpenCFile(filename, "rb");
if (!fp)
return nullptr;
std::FILE* repack_fp = nullptr;
if (repack_filename && std::strlen(repack_filename) > 0)
{
repack_fp = FileSystem::OpenCFile(repack_filename, "wb");
if (!repack_fp)
{
std::fclose(fp);
return nullptr;
}
}
if (StringUtil::EndsWithNoCase(filename, ".xz"))
return std::make_unique<GSDumpLzma>(fp, nullptr);
else
return std::make_unique<GSDumpRaw>(fp, nullptr);
}
bool GSDumpFile::GetPreviewImageFromDump(const char* filename, u32* width, u32* height, std::vector<u32>* pixels)
{
std::unique_ptr<GSDumpFile> dump = OpenGSDump(filename);
if (!dump)
return false;
u32 crc;
if (!dump->Read(&crc, sizeof(crc)) || crc != 0xFFFFFFFFu)
{
// not new header dump, so no preview
return false;
}
u32 header_size;
if (!dump->Read(&header_size, sizeof(header_size)) || header_size < sizeof(GSDumpHeader))
{
// doesn't have the screenshot fields
return false;
}
std::unique_ptr<u8[]> header_bits = std::make_unique<u8[]>(header_size);
if (!dump->Read(header_bits.get(), header_size))
return false;
GSDumpHeader header;
std::memcpy(&header, header_bits.get(), sizeof(header));
if (header.screenshot_size == 0 ||
header.screenshot_size < (header.screenshot_width * header.screenshot_height * sizeof(u32)) ||
(static_cast<u64>(header.screenshot_offset) + header.screenshot_size) > header_size)
{
// doesn't have a screenshot
return false;
}
*width = header.screenshot_width;
*height = header.screenshot_height;
pixels->resize(header.screenshot_width * header.screenshot_height);
std::memcpy(pixels->data(), header_bits.get() + header.screenshot_offset, header.screenshot_size);
return true;
}
bool GSDumpFile::ReadFile()
{
u32 ss;
if (Read(&m_crc, sizeof(m_crc)) != sizeof(m_crc) || Read(&ss, sizeof(ss)) != sizeof(ss))
return false;
m_state_data.resize(ss);
if (Read(m_state_data.data(), ss) != ss)
return false;
// Pull serial out of new header, if present.
if (m_crc == 0xFFFFFFFFu)
{
GSDumpHeader header;
if (m_state_data.size() < sizeof(header))
{
Console.Error("GSDump header is corrupted.");
return false;
}
std::memcpy(&header, m_state_data.data(), sizeof(header));
m_crc = header.crc;
if (header.serial_size > 0)
{
if (header.serial_offset > ss || (static_cast<u64>(header.serial_offset) + header.serial_size) > ss)
{
Console.Error("GSDump header is corrupted.");
return false;
}
if (header.serial_size > 0)
m_serial.assign(reinterpret_cast<const char*>(m_state_data.data()) + header.serial_offset, header.serial_size);
}
// Read the real state data
m_state_data.resize(header.state_size);
if (Read(m_state_data.data(), header.state_size) != header.state_size)
return false;
}
m_regs_data.resize(8192);
if (Read(m_regs_data.data(), m_regs_data.size()) != m_regs_data.size())
return false;
// read all the packet data in
// TODO: make this suck less by getting the full/extracted size and preallocating
for (;;)
{
const size_t packet_data_size = m_packet_data.size();
m_packet_data.resize(std::max<size_t>(packet_data_size * 2, 8 * _1mb));
const size_t read_size = m_packet_data.size() - packet_data_size;
const size_t read = Read(m_packet_data.data() + packet_data_size, read_size);
if (read != read_size)
{
if (!IsEof())
return false;
m_packet_data.resize(packet_data_size + read);
m_packet_data.shrink_to_fit();
break;
}
}
u8* data = m_packet_data.data();
size_t remaining = m_packet_data.size();
#define GET_BYTE(dst) \
do \
{ \
if (remaining < sizeof(u8)) \
return false; \
std::memcpy(dst, data, sizeof(u8)); \
data++; \
remaining--; \
} while (0)
#define GET_WORD(dst) \
do \
{ \
if (remaining < sizeof(u32)) \
return false; \
std::memcpy(dst, data, sizeof(u32)); \
data += sizeof(u32); \
remaining -= sizeof(u32); \
} while (0)
while (remaining > 0)
{
GSData packet = {};
packet.path = GSTransferPath::Dummy;
GET_BYTE(&packet.id);
switch (packet.id)
{
case GSType::Transfer:
GET_BYTE(&packet.path);
GET_WORD(&packet.length);
break;
case GSType::VSync:
packet.length = 1;
break;
case GSType::ReadFIFO2:
packet.length = 4;
break;
case GSType::Registers:
packet.length = 8192;
break;
default:
return false;
}
if (packet.length > 0)
{
if (remaining < packet.length)
return false;
packet.data = data;
data += packet.length;
remaining -= packet.length;
}
m_dump_packets.push_back(std::move(packet));
}
#undef GET_WORD
#undef GET_BYTE
return true;
}
/******************************************************************/
GSDumpLzma::GSDumpLzma(FILE* file, FILE* repack_file)
: GSDumpFile(file, repack_file)
{
@@ -137,11 +317,10 @@ bool GSDumpLzma::IsEof()
return feof(m_fp) && m_avail == 0 && m_strm.avail_in == 0;
}
bool GSDumpLzma::Read(void* ptr, size_t size)
size_t GSDumpLzma::Read(void* ptr, size_t size)
{
size_t off = 0;
uint8_t* dst = (uint8_t*)ptr;
size_t full_size = size;
while (size && !IsEof())
{
if (m_avail == 0)
@@ -157,13 +336,10 @@ bool GSDumpLzma::Read(void* ptr, size_t size)
off += l;
}
if (size == 0)
{
Repack(ptr, full_size);
return true;
}
if (off > 0)
Repack(ptr, off);
return false;
return off;
}
GSDumpLzma::~GSDumpLzma()
@@ -178,11 +354,6 @@ GSDumpLzma::~GSDumpLzma()
/******************************************************************/
GSDumpRaw::GSDumpRaw(char* filename, const char* repack_filename)
: GSDumpFile(filename, repack_filename)
{
}
GSDumpRaw::GSDumpRaw(FILE* file, FILE* repack_file)
: GSDumpFile(file, repack_file)
{
@@ -193,20 +364,17 @@ bool GSDumpRaw::IsEof()
return !!feof(m_fp);
}
bool GSDumpRaw::Read(void* ptr, size_t size)
size_t GSDumpRaw::Read(void* ptr, size_t size)
{
size_t ret = fread(ptr, 1, size, m_fp);
if (ret != size && ferror(m_fp))
{
fprintf(stderr, "GSDumpRaw:: Read error (%zu/%zu)\n", ret, size);
throw "BAD"; // Just exit the program
return ret;
}
if (ret == size)
{
Repack(ptr, size);
return true;
}
if (ret > 0)
Repack(ptr, ret);
return false;
return ret;
}
+313 -12
View File
@@ -13,24 +13,327 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <lzma.h>
#include <memory>
#include <string>
#include <vector>
#define GEN_REG_ENUM_CLASS_CONTENT(ClassName, EntryName, Value) \
EntryName = Value,
#define GEN_REG_GETNAME_CONTENT(ClassName, EntryName, Value) \
case ClassName::EntryName: \
return #EntryName;
#define GEN_REG_ENUM_CLASS_AND_GETNAME(Macro, ClassName, Type, DefaultString) \
enum class ClassName : Type \
{ \
Macro(GEN_REG_ENUM_CLASS_CONTENT) \
}; \
static constexpr const char* GetName(ClassName reg) \
{ \
switch (reg) \
{ \
Macro(GEN_REG_GETNAME_CONTENT) default : return DefaultString; \
} \
}
namespace GSDumpTypes
{
// clang-format off
#define DEF_GSType(X) \
X(GSType, Transfer, 0) \
X(GSType, VSync, 1) \
X(GSType, ReadFIFO2, 2) \
X(GSType, Registers, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSType, GSType, u8, "UnknownType")
#undef DEF_GSType
#define DEF_GSTransferPath(X) \
X(GSTransferPath, Path1Old, 0) \
X(GSTransferPath, Path2, 1) \
X(GSTransferPath, Path3, 2) \
X(GSTransferPath, Path1New, 3) \
X(GSTransferPath, Dummy, 4)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSTransferPath, GSTransferPath, u8, "UnknownPath")
#undef DEF_GSTransferPath
#define DEF_GIFFlag(X) \
X(GIFFlag, PACKED, 0) \
X(GIFFlag, REGLIST, 1) \
X(GIFFlag, IMAGE, 2) \
X(GIFFlag, IMAGE2, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFFlag, GIFFlag, u8, "UnknownFlag")
#undef DEF_GifFlag
#define DEF_GIFReg(X) \
X(GIFReg, PRIM, 0x00) \
X(GIFReg, RGBAQ, 0x01) \
X(GIFReg, ST, 0x02) \
X(GIFReg, UV, 0x03) \
X(GIFReg, XYZF2, 0x04) \
X(GIFReg, XYZ2, 0x05) \
X(GIFReg, TEX0_1, 0x06) \
X(GIFReg, TEX0_2, 0x07) \
X(GIFReg, CLAMP_1, 0x08) \
X(GIFReg, CLAMP_2, 0x09) \
X(GIFReg, FOG, 0x0a) \
X(GIFReg, XYZF3, 0x0c) \
X(GIFReg, XYZ3, 0x0d) \
X(GIFReg, AD, 0x0e) \
X(GIFReg, NOP, 0x0f) \
X(GIFReg, TEX1_1, 0x14) \
X(GIFReg, TEX1_2, 0x15) \
X(GIFReg, TEX2_1, 0x16) \
X(GIFReg, TEX2_2, 0x17) \
X(GIFReg, XYOFFSET_1, 0x18) \
X(GIFReg, XYOFFSET_2, 0x19) \
X(GIFReg, PRMODECONT, 0x1a) \
X(GIFReg, PRMODE, 0x1b) \
X(GIFReg, TEXCLUT, 0x1c) \
X(GIFReg, SCANMSK, 0x22) \
X(GIFReg, MIPTBP1_1, 0x34) \
X(GIFReg, MIPTBP1_2, 0x35) \
X(GIFReg, MIPTBP2_1, 0x36) \
X(GIFReg, MIPTBP2_2, 0x37) \
X(GIFReg, TEXA, 0x3b) \
X(GIFReg, FOGCOL, 0x3d) \
X(GIFReg, TEXFLUSH, 0x3f) \
X(GIFReg, SCISSOR_1, 0x40) \
X(GIFReg, SCISSOR_2, 0x41) \
X(GIFReg, ALPHA_1, 0x42) \
X(GIFReg, ALPHA_2, 0x43) \
X(GIFReg, DIMX, 0x44) \
X(GIFReg, DTHE, 0x45) \
X(GIFReg, COLCLAMP, 0x46) \
X(GIFReg, TEST_1, 0x47) \
X(GIFReg, TEST_2, 0x48) \
X(GIFReg, PABE, 0x49) \
X(GIFReg, FBA_1, 0x4a) \
X(GIFReg, FBA_2, 0x4b) \
X(GIFReg, FRAME_1, 0x4c) \
X(GIFReg, FRAME_2, 0x4d) \
X(GIFReg, ZBUF_1, 0x4e) \
X(GIFReg, ZBUF_2, 0x4f) \
X(GIFReg, BITBLTBUF, 0x50) \
X(GIFReg, TRXPOS, 0x51) \
X(GIFReg, TRXREG, 0x52) \
X(GIFReg, TRXDIR, 0x53) \
X(GIFReg, HWREG, 0x54) \
X(GIFReg, SIGNAL, 0x60) \
X(GIFReg, FINISH, 0x61) \
X(GIFReg, LABEL, 0x62)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFReg, GIFReg, u8, "UnknownReg")
#undef DEF_GIFReg
// clang-format on
template <typename Output, typename Input, typename std::enable_if<sizeof(Input) == sizeof(Output), bool>::type = true>
static constexpr Output BitCast(Input input)
{
Output output;
memcpy(&output, &input, sizeof(input));
return output;
}
template <typename Output = u32>
static constexpr Output GetBits(u64 value, u32 shift, u32 numbits)
{
return static_cast<Output>((value >> shift) & ((1ull << numbits) - 1));
}
template <typename Output = u32>
static constexpr Output GetBits(u128 value, u32 shift, u32 numbits)
{
u64 outval = 0;
if (shift == 0)
outval = value.lo;
else if (shift < 64)
outval = (value.lo >> shift) | (value.hi << (64 - shift));
else
outval = value.hi >> (shift - 64);
return static_cast<Output>(outval & ((1ull << numbits) - 1));
}
static constexpr const char* GetNameOneBit(u8 value, const char* zero, const char* one)
{
switch (value)
{
case 0:
return zero;
case 1:
return one;
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNameBool(bool value)
{
return value ? "True" : "False";
}
static constexpr const char* GetNamePRIMPRIM(u8 prim)
{
switch (prim)
{
case 0:
return "Point";
case 1:
return "Line";
case 2:
return "Line Strip";
case 3:
return "Triangle";
case 4:
return "Triangle Strip";
case 5:
return "Triangle Fan";
case 6:
return "Sprite";
case 7:
return "Invalid";
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNamePRIMIIP(u8 iip)
{
return GetNameOneBit(iip, "Flat Shading", "Gouraud Shading");
}
static constexpr const char* GetNamePRIMFST(u8 fst)
{
return GetNameOneBit(fst, "STQ Value", "UV Value");
}
static constexpr const char* GetNamePRIMCTXT(u8 ctxt)
{
return GetNameOneBit(ctxt, "Context 1", "Context 2");
}
static constexpr const char* GetNamePRIMFIX(u8 fix)
{
return GetNameOneBit(fix, "Unfixed", "Fixed");
}
static constexpr const char* GetNameTEXTCC(u8 tcc)
{
return GetNameOneBit(tcc, "RGB", "RGBA");
}
static constexpr const char* GetNameTEXTFX(u8 tfx)
{
switch (tfx)
{
case 0:
return "Modulate";
case 1:
return "Decal";
case 2:
return "Highlight";
case 3:
return "Highlight2";
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCSM(u8 csm)
{
return GetNameOneBit(csm, "CSM1", "CSM2");
}
static constexpr const char* GetNameTEXPSM(u8 psm)
{
switch (psm)
{
case 000:
return "PSMCT32";
case 001:
return "PSMCT24";
case 002:
return "PSMCT16";
case 012:
return "PSMCT16S";
case 023:
return "PSMT8";
case 024:
return "PSMT4";
case 033:
return "PSMT8H";
case 044:
return "PSMT4HL";
case 054:
return "PSMT4HH";
case 060:
return "PSMZ32";
case 061:
return "PSMZ24";
case 062:
return "PSMZ16";
case 072:
return "PSMZ16S";
default:
return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCPSM(u8 psm)
{
switch (psm)
{
case 000:
return "PSMCT32";
case 002:
return "PSMCT16";
case 012:
return "PSMCT16S";
default:
return "UNKNOWN";
}
}
} // namespace GSDumpTypes
class GSDumpFile
{
FILE* m_repack_fp;
public:
struct GSData
{
GSDumpTypes::GSType id;
const u8* data;
s32 length;
GSDumpTypes::GSTransferPath path;
};
using ByteArray = std::vector<u8>;
using GSDataArray = std::vector<GSData>;
virtual ~GSDumpFile();
static std::unique_ptr<GSDumpFile> OpenGSDump(const char* filename, const char* repack_filename = nullptr);
static bool GetPreviewImageFromDump(const char* filename, u32* width, u32* height, std::vector<u32>* pixels);
__fi const std::string& GetSerial() const { return m_serial; }
__fi u32 GetCRC() const { return m_crc; }
__fi const ByteArray& GetRegsData() const { return m_regs_data; }
__fi const ByteArray& GetStateData() const { return m_state_data; }
__fi const GSDataArray& GetPackets() const { return m_dump_packets; }
bool ReadFile();
protected:
FILE* m_fp;
GSDumpFile(FILE* file, FILE* repack_file);
virtual bool IsEof() = 0;
virtual size_t Read(void* ptr, size_t size) = 0;
void Repack(void* ptr, size_t size);
public:
virtual bool IsEof() = 0;
virtual bool Read(void* ptr, size_t size) = 0;
FILE* m_fp = nullptr;
GSDumpFile(char* filename, const char* repack_filename);
GSDumpFile(FILE* file, FILE* repack_file);
virtual ~GSDumpFile();
private:
FILE* m_repack_fp = nullptr;
std::string m_serial;
u32 m_crc = 0;
std::vector<u8> m_regs_data;
std::vector<u8> m_state_data;
std::vector<u8> m_packet_data;
GSDataArray m_dump_packets;
};
class GSDumpLzma : public GSDumpFile
@@ -48,21 +351,19 @@ class GSDumpLzma : public GSDumpFile
void Initialize();
public:
GSDumpLzma(char* filename, const char* repack_filename);
GSDumpLzma(FILE* file, FILE* repack_file);
virtual ~GSDumpLzma();
bool IsEof() final;
bool Read(void* ptr, size_t size) final;
size_t Read(void* ptr, size_t size) final;
};
class GSDumpRaw : public GSDumpFile
{
public:
GSDumpRaw(char* filename, const char* repack_filename);
GSDumpRaw(FILE* file, FILE* repack_file);
virtual ~GSDumpRaw() = default;
bool IsEof() final;
bool Read(void* ptr, size_t size) final;
size_t Read(void* ptr, size_t size) final;
};
+271 -98
View File
@@ -20,17 +20,29 @@
#include <algorithm> // clamp
#include <cfloat> // FLT_MAX
#include <iomanip> // Dump Verticles
int GSState::s_n = 0;
static __fi bool IsAutoFlushEnabled()
{
return (GSConfig.Renderer == GSRendererType::SW) ? GSConfig.AutoFlushSW : GSConfig.UserHacks_AutoFlush;
}
static __fi bool IsFirstProvokingVertex()
{
return (GSConfig.Renderer != GSRendererType::SW && !g_gs_device->Features().provoking_vertex_last);
}
GSState::GSState()
: m_version(7)
: m_version(STATE_VERSION)
, m_gsc(NULL)
, m_skip(0)
, m_skip_offset(0)
, m_q(1.0f)
, m_scanmask_used(false)
, m_vt(this)
, tex_flushed(true)
, m_vt(this, IsFirstProvokingVertex())
, m_regs(NULL)
, m_crc(0)
, m_options(0)
@@ -39,18 +51,8 @@ GSState::GSState()
// m_nativeres seems to be a hack. Unfortunately it impacts draw call number which make debug painful in the replayer.
// Let's keep it disabled to ease debug.
m_nativeres = GSConfig.UpscaleMultiplier == 1;
m_mipmap = theApp.GetConfigB("mipmap");
m_mipmap = GSConfig.Mipmap;
m_NTSC_Saturation = theApp.GetConfigB("NTSC_Saturation");
if (theApp.GetConfigB("UserHacks"))
{
m_userhacks_auto_flush = theApp.GetConfigB("UserHacks_AutoFlush");
m_userhacks_wildhack = theApp.GetConfigB("UserHacks_WildHack");
}
else
{
m_userhacks_auto_flush = false;
m_userhacks_wildhack = false;
}
s_n = 0;
s_dump = theApp.GetConfigB("dump");
@@ -132,7 +134,6 @@ GSState::GSState()
PRIM = &m_env.PRIM;
//CSR->rREV = 0x20;
m_env.PRMODECONT.AC = 1;
tex_flushed = true;
Reset();
@@ -246,7 +247,7 @@ void GSState::ResetHandlers()
m_fpGIFPackedRegHandlers[GIF_REG_PRIM] = (GIFPackedRegHandler)(GIFRegHandler)&GSState::GIFRegHandlerPRIM;
m_fpGIFPackedRegHandlers[GIF_REG_RGBA] = &GSState::GIFPackedRegHandlerRGBA;
m_fpGIFPackedRegHandlers[GIF_REG_STQ] = &GSState::GIFPackedRegHandlerSTQ;
m_fpGIFPackedRegHandlers[GIF_REG_UV] = m_userhacks_wildhack ? &GSState::GIFPackedRegHandlerUV_Hack : &GSState::GIFPackedRegHandlerUV;
m_fpGIFPackedRegHandlers[GIF_REG_UV] = GSConfig.UserHacks_WildHack ? &GSState::GIFPackedRegHandlerUV_Hack : &GSState::GIFPackedRegHandlerUV;
m_fpGIFPackedRegHandlers[GIF_REG_TEX0_1] = (GIFPackedRegHandler)(GIFRegHandler)&GSState::GIFRegHandlerTEX0<0>;
m_fpGIFPackedRegHandlers[GIF_REG_TEX0_2] = (GIFPackedRegHandler)(GIFRegHandler)&GSState::GIFRegHandlerTEX0<1>;
m_fpGIFPackedRegHandlers[GIF_REG_CLAMP_1] = (GIFPackedRegHandler)(GIFRegHandler)&GSState::GIFRegHandlerCLAMP<0>;
@@ -256,11 +257,10 @@ void GSState::ResetHandlers()
m_fpGIFPackedRegHandlers[GIF_REG_NOP] = &GSState::GIFPackedRegHandlerNOP;
// swap first/last indices when the provoking vertex is the first (D3D/Vulkan)
const bool index_swap = GSConfig.UseHardwareRenderer() && !g_gs_device->Features().provoking_vertex_last;
if (m_userhacks_auto_flush)
index_swap ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
if (IsAutoFlushEnabled())
IsFirstProvokingVertex() ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
else
index_swap ? SetPrimHandlers<false, true>() : SetPrimHandlers<false, false>();
IsFirstProvokingVertex() ? SetPrimHandlers<false, true>() : SetPrimHandlers<false, false>();
std::fill(std::begin(m_fpGIFRegHandlers), std::end(m_fpGIFRegHandlers), &GSState::GIFRegHandlerNull);
@@ -268,7 +268,7 @@ void GSState::ResetHandlers()
m_fpGIFRegHandlers[GIF_A_D_REG_RGBAQ] = &GSState::GIFRegHandlerRGBAQ;
m_fpGIFRegHandlers[GIF_A_D_REG_RGBAQ + 0x10] = &GSState::GIFRegHandlerRGBAQ;
m_fpGIFRegHandlers[GIF_A_D_REG_ST] = &GSState::GIFRegHandlerST;
m_fpGIFRegHandlers[GIF_A_D_REG_UV] = m_userhacks_wildhack ? &GSState::GIFRegHandlerUV_Hack : &GSState::GIFRegHandlerUV;
m_fpGIFRegHandlers[GIF_A_D_REG_UV] = GSConfig.UserHacks_WildHack ? &GSState::GIFRegHandlerUV_Hack : &GSState::GIFRegHandlerUV;
m_fpGIFRegHandlers[GIF_A_D_REG_TEX0_1] = &GSState::GIFRegHandlerTEX0<0>;
m_fpGIFRegHandlers[GIF_A_D_REG_TEX0_2] = &GSState::GIFRegHandlerTEX0<1>;
m_fpGIFRegHandlers[GIF_A_D_REG_CLAMP_1] = &GSState::GIFRegHandlerCLAMP<0>;
@@ -319,6 +319,19 @@ void GSState::ResetHandlers()
m_fpGIFRegHandlers[GIF_A_D_REG_LABEL] = &GSState::GIFRegHandlerNull;
}
void GSState::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
{
m_mipmap = GSConfig.Mipmap;
if (
GSConfig.AutoFlushSW != old_config.AutoFlushSW ||
GSConfig.UserHacks_AutoFlush != old_config.UserHacks_AutoFlush ||
GSConfig.UserHacks_WildHack != old_config.UserHacks_WildHack)
{
ResetHandlers();
}
}
bool GSState::isinterlaced()
{
return !!m_regs->SMODE2.INT;
@@ -543,6 +556,100 @@ float GSState::GetTvRefreshRate()
__assume(0); // unreachable
}
void GSState::DumpVertices(const std::string& filename)
{
std::ofstream file(filename);
if (!file.is_open())
return;
size_t count = m_index.tail;
GSVertex* buffer = &m_vertex.buff[0];
const char* DEL = ", ";
file << "VERTEX COORDS (XYZ)" << std::endl;
file << std::fixed << std::setprecision(4);
for (size_t i = 0; i < count; ++i)
{
file << "\t" << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
float x = (v.XYZ.X - (int)m_context->XYOFFSET.OFX) / 16.0f;
float y = (v.XYZ.Y - (int)m_context->XYOFFSET.OFY) / 16.0f;
file << x << DEL;
file << y << DEL;
file << v.XYZ.Z;
file << std::endl;
}
file << std::endl;
file << "VERTEX COLOR (RGBA)" << std::endl;
file << std::fixed << std::setprecision(6);
for (size_t i = 0; i < count; ++i)
{
file << "\t" << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.R) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.G) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.B) << DEL;
file << std::setfill('0') << std::setw(3) << unsigned(v.RGBAQ.A);
file << std::endl;
}
file << std::endl;
bool use_uv = PRIM->FST;
std::string qualifier = use_uv ? "UV" : "STQ";
file << "TEXTURE COORDS (" << qualifier << ")" << std::endl;;
for (size_t i = 0; i < count; ++i)
{
file << "\t" << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
// note
// Yes, technically as far as the GS is concerned Q belongs
// to RGBAQ. However, the purpose of this dump is to print
// our data in a more human readable format and typically Q
// is associated with STQ.
if (use_uv)
{
float uv_U = v.U / 16.0f;
float uv_V = v.V / 16.0f;
file << uv_U << DEL << uv_V;
}
else
file << v.ST.S << DEL << v.ST.T << DEL << v.RGBAQ.Q;
file << std::endl;
}
file << std::endl;
file << "TRACER" << std::endl;
GSVector4i v = m_vt.m_min.c;
file << "\tmin c (x,y,z,w): " << v.x << DEL << v.y << DEL << v.z << DEL << v.w << std::endl;
v = m_vt.m_max.c;
file << "\tmax c (x,y,z,w): " << v.x << DEL << v.y << DEL << v.z << DEL << v.w << std::endl;
GSVector4 v2 = m_vt.m_min.p;
file << "\tmin p (x,y,z,w): " << v2.x << DEL << v2.y << DEL << v2.z << DEL << v2.w << std::endl;
v2 = m_vt.m_max.p;
file << "\tmax p (x,y,z,w): " << v2.x << DEL << v2.y << DEL << v2.z << DEL << v2.w << std::endl;
v2 = m_vt.m_min.t;
file << "\tmin t (x,y,z,w): " << v2.x << DEL << v2.y << DEL << v2.z << DEL << v2.w << std::endl;
v2 = m_vt.m_max.t;
file << "\tmax t (x,y,z,w): " << v2.x << DEL << v2.y << DEL << v2.z << DEL << v2.w << std::endl;
file.close();
}
void GSState::GIFPackedRegHandlerNull(const GIFPackedReg* RESTRICT r)
{
}
@@ -894,7 +1001,10 @@ void GSState::GIFRegHandlerTEX0(const GIFReg* RESTRICT r)
GL_REG("TEX0_%d = 0x%x_%x", i, r->U32[1], r->U32[0]);
GIFRegTEX0 TEX0 = r->TEX0;
const bool MTBAreload = ((tex_flushed && (i == m_env.PRIM.CTXT)) || (r->TEX0.TBP0 != m_env.CTXT[i].TEX0.TBP0)) ? true : false;
GIFRegMIPTBP1 temp_MIPTBP1;
bool MTBAReloaded = false;
// Max allowed MTBA size for 32bit swizzled textures (including 8H 4HL etc) is 512, 16bit and normal 8/4bit formats can be 1024
const u32 maxTex = (GSLocalMemory::m_psm[TEX0.PSM].bpp < 32) ? 10 : 9;
// Spec max is 10
//
@@ -910,61 +1020,53 @@ void GSState::GIFRegHandlerTEX0(const GIFReg* RESTRICT r)
TEX0.TW = std::clamp<u32>(TEX0.TW, 0, 10);
TEX0.TH = std::clamp<u32>(TEX0.TH, 0, 10);
ApplyTEX0<i>(TEX0);
// When the texture cache reloads any MIPS need to update too, so let's do that here.
// This is essentially triggered by the texture page change, so if TEXFLUSH is called and a draw doesn't proceed it, or TBP changes.
// Textures must be of equal width/height and a minimum of 32x32
if (MTBAreload && m_env.CTXT[i].TEX1.MTBA && m_env.CTXT[i].TEX0.TW == m_env.CTXT[i].TEX0.TH && m_env.CTXT[i].TEX0.TW >= 5)
// MTBA loads are triggered by writes to TEX0 (but not TEX2!)
// Textures MUST be a minimum width of 32 pixels
// Format must be a color, Z formats do not trigger MTBA (but are valid for Mipmapping)
if (m_env.CTXT[i].TEX1.MTBA && TEX0.TW >= 5 && TEX0.TW <= maxTex && (TEX0.PSM & 0x30) != 0x30)
{
// NOTE 1: TEX1.MXL must not be automatically set to 3 here.
// NOTE 2: Mipmap levels are tightly packed, if (tbw << 6) > (1 << tw) then the left-over space to the right is used. (common for PSM_PSMT4)
// NOTE 3: Non-rectangular textures are treated as rectangular when calculating the occupied space (height is extended, not sure about width)
// NOTE 1: TEX1.MXL must not be automatically set to 3 here and it has no effect on MTBA.
// NOTE 2: Mipmap levels are packed with a minimum distance between them of 1 block, even down at 4bit textures under 16x16.
// NOTE 3: Everything is derrived from the width of the texture, TBW and TH are completely ignored (useful for handling non-rectangular ones)
// NOTE 4: Cartoon Network Racing's menu is VERY sensitive to this as it uses 4bit sized textures for the sky.
u32 bp = TEX0.TBP0;
u32 bw = std::max(1u, (1u << TEX0.TW) >> 6);
u32 bp = m_env.CTXT[i].TEX0.TBP0;
u32 bw = m_env.CTXT[i]. TEX0.TBW;
u32 w = 1u << m_env.CTXT[i].TEX0.TW;
u32 h = 1u << m_env.CTXT[i].TEX0.TH;
u32 minwidth = m_env.CTXT[i].TEX1.MMIN >= 4 ? 8 : 1;
// Address is calculated as a 4bit address space, then converted (/8) to 32bit address space
// ((w * w * bpp) / 8) / 64. No the 'w' is not a typo ;)
const u32 bpp = GSLocalMemory::m_psm[TEX0.PSM].bpp >> 2;
u32 tex_size = ((1u << TEX0.TW) * (1u << TEX0.TW) * bpp) >> 9;
const u32 bpp = GSLocalMemory::m_psm[m_env.CTXT[i].TEX0.PSM].bpp;
bp += tex_size;
bw = std::max<u32>(bw >> 1, 1);
tex_size = std::max<u32>(tex_size >> 2, 1);
bp += (int)((w * h * ((float)bpp / 8))) >> 8;
temp_MIPTBP1.TBP1 = bp;
temp_MIPTBP1.TBW1 = bw;
if (w > minwidth)
{
bw = std::max<u32>(bw >> 1, 1);
w = std::max<u32>(w >> 1, 1);
h = std::max<u32>(h >> 1, 1);
}
bp += tex_size;
bw = std::max<u32>(bw >> 1, 1);
tex_size = std::max<u32>(tex_size >> 2, 1);
m_env.CTXT[i].MIPTBP1.TBP1 = bp;
m_env.CTXT[i].MIPTBP1.TBW1 = bw;
temp_MIPTBP1.TBP2 = bp;
temp_MIPTBP1.TBW2 = bw;
bp += (int)((w * h * ((float)bpp / 8))) >> 8;
bp += tex_size;
bw = std::max<u32>(bw >> 1, 1);
if (w > minwidth)
{
bw = std::max<u32>(bw >> 1, 1);
w = std::max<u32>(w >> 1, 1);
h = std::max<u32>(h >> 1, 1);
}
temp_MIPTBP1.TBP3 = bp;
temp_MIPTBP1.TBW3 = bw;
m_env.CTXT[i].MIPTBP1.TBP2 = bp;
m_env.CTXT[i].MIPTBP1.TBW2 = bw;
if (temp_MIPTBP1 != m_env.CTXT[i].MIPTBP1)
Flush();
bp += (int)((w * h * ((float)bpp / 8))) >> 8;
if (w > minwidth)
{
bw = std::max<u32>(bw >> 1, 1);
}
m_env.CTXT[i].MIPTBP1.TBP3 = bp;
m_env.CTXT[i].MIPTBP1.TBW3 = bw;
MTBAReloaded = true;
}
tex_flushed = false;
ApplyTEX0<i>(TEX0);
if (MTBAReloaded)
m_env.CTXT[i].MIPTBP1 = temp_MIPTBP1;
}
template <int i>
@@ -1132,11 +1234,10 @@ void GSState::GIFRegHandlerTEXFLUSH(const GIFReg* RESTRICT r)
{
GL_REG("TEXFLUSH = 0x%x_%x PRIM TME %x", r->U32[1], r->U32[0], PRIM->TME);
tex_flushed = true;
// Some games do a single sprite draw to itself, then flush the texture cache, then use that texture again.
// This won't get picked up by the new autoflush logic (which checks for page crossings for the PS2 Texture Cache flush)
// so we need to do it here.
if(m_userhacks_auto_flush)
if (IsAutoFlushEnabled())
Flush();
}
@@ -1705,14 +1806,59 @@ void GSState::Move()
int _sy = sy, _dy = dy; // Faster with local copied variables, compiler optimizations are dumb
if (xinc > 0)
{
for (int y = 0; y < h; y++, _sy += yinc, _dy += yinc)
const int page_width = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].pgs.x;
const int page_height = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].pgs.y;
const int xpage = sx & ~(page_width - 1);
const int ypage = _sy & ~(page_height - 1);
// Copying from itself to itself (rotating textures) used in Gitaroo Man stage 8
// What probably happens is because the copy is buffered, the source stays just ahead of the destination.
if (sbp == dbp && (((_sy < _dy) && ((ypage + page_height) > _dy)) || ((sx < dx) && ((xpage + page_width) > dx))))
{
auto s = getPAHelper(spo, sx, _sy);
auto d = getPAHelper(dpo, dx, _dy);
int starty = 0;
int endy = h;
int y_inc = yinc;
for (int x = 0; x < w; x++)
if (((_sy < _dy) && ((ypage + page_height) > _dy)))
{
pxCopyFn(d, s, x);
_sy += h;
_dy += h;
starty = h-1;
endy = -1;
y_inc = -y_inc;
}
for (int y = starty; y != endy; y+= y_inc, _sy += y_inc, _dy += y_inc)
{
auto s = getPAHelper(spo, sx, _sy);
auto d = getPAHelper(dpo, dx, _dy);
if (((sx < dx) && ((xpage + page_width) > dx)))
{
for (int x = w - 1; x >= 0; x--)
{
pxCopyFn(d, s, x);
}
}
else
{
for (int x = 0; x < w; x++)
{
pxCopyFn(d, s, x);
}
}
}
}
else
{
for (int y = 0; y < h; y++, _sy += yinc, _dy += yinc)
{
auto s = getPAHelper(spo, sx, _sy);
auto d = getPAHelper(dpo, dx, _dy);
for (int x = 0; x < w; x++)
{
pxCopyFn(d, s, x);
}
}
}
}
@@ -2127,7 +2273,7 @@ int GSState::Defrost(const freezeData* fd)
u8* data = fd->data;
int version;
u32 version;
ReadState(&version, data);
@@ -2433,10 +2579,17 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
__forceinline void GSState::HandleAutoFlush()
{
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
const bool frame_hit = (m_context->FRAME.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
const u32 frame_z_bp = frame_hit ? m_context->FRAME.Block() : m_context->ZBUF.Block();
// To briefly explain what's going on here, what we are checking for is draws over a texture when the source and destination are themselves.
// Because one page of the texture gets buffered in the Texture Cache (the PS2's one) if any of those pixels are overwritten, you still read the old data.
// So we need to calculate if a page boundary is being crossed for the format it is in and if the same part of the texture being written and read inside the draw.
if ((m_context->FRAME.Block() == m_context->TEX0.TBP0) && (m_context->TEX0.PSM == m_context->FRAME.PSM) && PRIM->TME && (m_context->FRAME.FBMSK != 0xFFFFFFFF))
if (PRIM->TME && (frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM))
{
const int page_mask_x = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x - 1);
const int page_mask_y = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y - 1);
@@ -2513,14 +2666,46 @@ __forceinline void GSState::HandleAutoFlush()
if(page_crossed)
{
// Update the vertex trace, scissor it (important for Jak 3!) and intersect with the current texture.
if((m_index.tail - 1) == current_tex_end)
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
if (!area_out.rintersect(tex_rect).rempty())
// Make sure the format matches, otherwise the coordinates aren't gonna match, so the draws won't intersect.
if (GSUtil::HasCompatibleBits(m_context->TEX0.PSM, frame_z_psm) && (m_context->FRAME.FBW == m_context->TEX0.TBW))
{
Flush();
// Update the vertex trace, scissor it (important for Jak 3!) and intersect with the current texture.
if ((m_index.tail - 1) == current_tex_end)
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
// Intersect goes on space inside the rect
GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)) + GSVector4i(0, 0, 1, 1);
// Scissor output
area_out = area_out.rintersect(GSVector4i(m_context->scissor.in));
// Intersect with texture
if (!area_out.rintersect(tex_rect).rempty())
Flush();
}
else // Storage of the TEX and FRAME/Z is different, so uhh, just fall back to flushing each page. It's slower, sorry.
{
if (m_context->FRAME.FBW == m_context->TEX0.TBW)
{
//We know we've changed page, so let's set the dimension to cover the page they're in (for different pixel orders)
tex_rect = tex_rect & page_mask;
tex_rect += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
tex_rect.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
tex_rect.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
if ((m_index.tail - 1) == current_tex_end)
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
area_out = area_out & page_mask;
area_out += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
area_out.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
area_out.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
if (!area_out.rintersect(tex_rect).rempty())
Flush();
}
else // Page width is different, so it's much more difficult to calculate where it's modifying.
Flush();
}
}
}
@@ -2534,29 +2719,19 @@ __forceinline void GSState::VertexKick(u32 skip)
switch (prim)
{
case GS_POINTLIST:
case GS_INVALID:
n = 1;
break;
case GS_LINELIST:
n = 2;
break;
case GS_SPRITE:
case GS_LINESTRIP:
n = 2;
break;
case GS_TRIANGLELIST:
n = 3;
break;
case GS_TRIANGLESTRIP:
n = 3;
break;
case GS_TRIANGLEFAN:
n = 3;
break;
case GS_SPRITE:
n = 2;
break;
case GS_INVALID:
n = 1;
break;
}
if (m_context->FRAME.FBMSK != 0xFFFFFFFF)
@@ -2768,9 +2943,6 @@ __forceinline void GSState::VertexKick(u32 skip)
default:
__assume(0);
}
if (PRIM->TME)
tex_flushed = false;
}
/// Checks if region repeat is used (applying it does something to at least one of the values in min...max)
@@ -3074,8 +3246,9 @@ bool GSState::TryAlphaTest(u32& fm, u32& zm)
}
else
{
const int amin = GetAlphaMinMax().min;
const int amax = GetAlphaMinMax().max;
const GSVertexTrace::VertexAlpha& aminmax = GetAlphaMinMax();
const int amin = aminmax.min;
const int amax = aminmax.max;
const int aref = m_context->TEST.AREF;
@@ -3201,7 +3374,7 @@ bool GSState::IsOpaque()
bool GSState::IsMipMapDraw()
{
return m_context->TEX1.MXL > 0 && m_context->TEX1.MMIN >= 2 && m_context->TEX1.MMIN <= 5 && m_vt.m_lod.y > 0 && (!m_context->TEX1.MTBA || m_context->TEX0.TH == m_context->TEX0.TW);
return m_context->TEX1.MXL > 0 && m_context->TEX1.MMIN >= 2 && m_context->TEX1.MMIN <= 5 && m_vt.m_lod.y > 0;
}
bool GSState::IsMipMapActive()
+7 -4
View File
@@ -123,7 +123,7 @@ class GSState : public GSAlignedClass<32>
template<bool auto_flush, bool index_swap>
void SetPrimHandlers();
int m_version;
u32 m_version;
int m_sssize;
struct GSTransferBuffer
@@ -149,14 +149,11 @@ protected:
bool IsBadFrame();
void SetupCrcHack();
bool m_userhacks_wildhack;
bool m_isPackedUV_HackFlag;
CRCHackLevel m_crc_hack_level;
GetSkipCount m_gsc;
int m_skip;
int m_skip_offset;
bool m_userhacks_auto_flush;
bool tex_flushed;
GSVertex m_v;
float m_q;
@@ -164,6 +161,7 @@ protected:
GSVector4i m_ofxy;
bool m_scanmask_used;
bool tex_flushed;
struct
{
@@ -245,6 +243,8 @@ public:
int s_savel;
std::string m_dump_root;
static constexpr u32 STATE_VERSION = 8;
enum PRIM_OVERLAP
{
PRIM_OVERLAP_UNKNOW,
@@ -273,6 +273,8 @@ public:
float GetTvRefreshRate();
virtual void Reset();
virtual void UpdateSettings(const Pcsx2Config::GSOptions& old_config);
void Flush();
void FlushPrim();
void FlushWrite();
@@ -301,6 +303,7 @@ public:
void SetRegsMem(u8* basemem) { m_regs = reinterpret_cast<GSPrivRegSet*>(basemem); }
void SetFrameSkip(int skip);
void DumpVertices(const std::string& filename);
PRIM_OVERLAP PrimitiveOverlap();
GIFRegTEX0 GetTex0Layer(u32 lod);
+56 -3
View File
@@ -20,11 +20,36 @@
#include "HostDisplay.h"
#include "PerformanceMetrics.h"
#include "pcsx2/Config.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#ifndef PCSX2_CORE
#include "gui/AppCoreThread.h"
#if defined(__unix__)
#include <X11/keysym.h>
#endif
static std::string GetDumpName()
{
return StringUtil::wxStringToUTF8String(GameInfo::gameName);
}
static std::string GetDumpSerial()
{
return StringUtil::wxStringToUTF8String(GameInfo::gameSerial);
}
#else
#include "VMManager.h"
static std::string GetDumpName()
{
return VMManager::GetGameName();
}
static std::string GetDumpSerial()
{
return VMManager::GetGameSerial();
}
#endif
GSRenderer::GSRenderer()
: m_shift_key(false)
, m_control_key(false)
@@ -454,10 +479,26 @@ void GSRenderer::VSync(u32 field, bool registers_written)
fd.data = new u8[fd.size];
Freeze(&fd, false);
// keep the screenshot relatively small so we don't bloat the dump
static constexpr u32 DUMP_SCREENSHOT_WIDTH = 640;
static constexpr u32 DUMP_SCREENSHOT_HEIGHT = 480;
std::vector<u32> screenshot_pixels;
SaveSnapshotToMemory(DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT, &screenshot_pixels);
if (m_control_key)
m_dump = std::unique_ptr<GSDumpBase>(new GSDump(m_snapshot, m_crc, fd, m_regs));
{
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpUncompressed(m_snapshot, GetDumpSerial(), m_crc,
DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT,
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
fd, m_regs));
}
else
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, m_crc, fd, m_regs));
{
m_dump = std::unique_ptr<GSDumpBase>(new GSDumpXz(m_snapshot, GetDumpSerial(), m_crc,
DUMP_SCREENSHOT_WIDTH, DUMP_SCREENSHOT_HEIGHT,
screenshot_pixels.empty() ? nullptr : screenshot_pixels.data(),
fd, m_regs));
}
delete[] fd.data;
}
@@ -528,6 +569,18 @@ bool GSRenderer::MakeSnapshot(const std::string& path)
}
prev_snap = cur_time;
}
// append the game serial and title
if (std::string name(GetDumpName()); !name.empty())
{
FileSystem::SanitizeFileName(name);
m_snapshot += format("_%s", name.c_str());
}
if (std::string serial(GetDumpSerial()); !serial.empty())
{
FileSystem::SanitizeFileName(serial);
m_snapshot += format("_%s", serial.c_str());
}
}
}
@@ -546,7 +599,7 @@ void GSRenderer::EndCapture()
void GSRenderer::KeyEvent(const HostKeyEvent& e)
{
#ifndef __APPLE__ // TODO: Add hotkey support on macOS
#if !defined(PCSX2_CORE) && !defined(__APPLE__) // TODO: Add hotkey support on macOS
#ifdef _WIN32
m_shift_key = !!(::GetAsyncKeyState(VK_SHIFT) & 0x8000);
m_control_key = !!(::GetAsyncKeyState(VK_CONTROL) & 0x8000);
+9 -7
View File
@@ -21,13 +21,15 @@
CONSTINIT const GSVector4 GSVertexTrace::s_minmax = GSVector4::cxpr(FLT_MAX, -FLT_MAX, 0.f, 0.f);
GSVertexTrace::GSVertexTrace(const GSState* state)
GSVertexTrace::GSVertexTrace(const GSState* state, bool provoking_vertex_first)
: m_accurate_stq(false), m_state(state), m_primclass(GS_INVALID_CLASS)
{
memset(&m_alpha, 0, sizeof(m_alpha));
#define InitUpdate3(P, IIP, TME, FST, COLOR) \
m_fmm[COLOR][FST][TME][IIP][P] = &GSVertexTrace::FindMinMax<P, IIP, TME, FST, COLOR>;
m_fmm[COLOR][FST][TME][IIP][P] = \
provoking_vertex_first ? &GSVertexTrace::FindMinMax<P, IIP, TME, FST, COLOR, true> : \
&GSVertexTrace::FindMinMax<P, IIP, TME, FST, COLOR, false>;
#define InitUpdate2(P, IIP, TME) \
InitUpdate3(P, IIP, TME, 0, 0) \
@@ -151,7 +153,7 @@ void GSVertexTrace::Update(const void* vertex, const u32* index, int v_count, in
}
}
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color, bool flat_swapped>
void GSVertexTrace::FindMinMax(const void* vertex, const u32* index, int count)
{
const GSDrawingContext* context = m_state->m_context;
@@ -283,16 +285,16 @@ void GSVertexTrace::FindMinMax(const void* vertex, const u32* index, int count)
int i = 0;
for (; i < (count - 3); i += 6)
{
processVertices(v[index[i + 0]], v[index[i + 3]], false);
processVertices(v[index[i + 0]], v[index[i + 3]], flat_swapped);
processVertices(v[index[i + 1]], v[index[i + 4]], false);
processVertices(v[index[i + 2]], v[index[i + 5]], true);
processVertices(v[index[i + 2]], v[index[i + 5]], !flat_swapped);
}
if (count & 1)
{
processVertices(v[index[i + 0]], v[index[i + 1]], false);
processVertices(v[index[i + 0]], v[index[i + 1]], flat_swapped);
// Compiler optimizations go!
// (And if they don't, it's only one vertex out of many)
processVertices(v[index[i + 2]], v[index[i + 2]], true);
processVertices(v[index[i + 2]], v[index[i + 2]], !flat_swapped);
}
}
else
+2 -2
View File
@@ -48,7 +48,7 @@ protected:
FindMinMaxPtr m_fmm[2][2][2][2][4];
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color, bool provoking_vertex_first>
void FindMinMax(const void* vertex, const u32* index, int count);
public:
@@ -73,7 +73,7 @@ public:
GSVector2 m_lod; // x = min, y = max
public:
GSVertexTrace(const GSState* state);
GSVertexTrace(const GSState* state, bool provoking_vertex_first);
virtual ~GSVertexTrace() {}
void Update(const void* vertex, const u32* index, int v_count, int i_count, GS_PRIM_CLASS primclass);
+4 -5
View File
@@ -18,7 +18,6 @@
bool s_nativeres;
static CRCHackLevel s_crc_hack_level = CRCHackLevel::Full;
// hacks
#define CRC_Partial (s_crc_hack_level >= CRCHackLevel::Partial)
#define CRC_Full (s_crc_hack_level >= CRCHackLevel::Full)
@@ -419,7 +418,7 @@ bool GSC_BurnoutGames(const GSFrameInfo& fi, int& skip)
// 0x01dc0 01c00(MP) ntsc, 0x01f00 0x01d40(MP) ntsc progressive, 0x02200(MP) pal.
// Yellow stripes.
// Multiplayer tested only on Takedown.
skip = 4;
skip = GSConfig.UserHacks_AutoFlush ? 2 : 4;
}
}
@@ -1054,7 +1053,7 @@ bool GSState::IsBadFrame()
return false;
}
if (m_skip == 0 && GSConfig.UserHacks && (GSConfig.SkipDraw > 0))
if (m_skip == 0 && GSConfig.SkipDrawEnd > 0)
{
if (fi.TME)
{
@@ -1062,8 +1061,8 @@ bool GSState::IsBadFrame()
// General, often problematic post processing
if (GSLocalMemory::m_psm[fi.TPSM].depth || GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM))
{
m_skip_offset = GSConfig.SkipDrawOffset;
m_skip = std::max(GSConfig.SkipDraw, m_skip_offset);
m_skip_offset = GSConfig.SkipDrawStart;
m_skip = GSConfig.SkipDrawEnd;
}
}
}
+65 -112
View File
@@ -25,10 +25,8 @@ GSRendererHW::GSRendererHW()
, m_height(default_rt_size.y)
, m_custom_width(1024)
, m_custom_height(1024)
, m_userhacks_ts_half_bottom(-1)
, m_tc(new GSTextureCache(this))
, m_src(nullptr)
, m_hw_mipmap(GSConfig.HWMipmap)
, m_userhacks_tcoffset(false)
, m_userhacks_tcoffset_x(0)
, m_userhacks_tcoffset_y(0)
@@ -36,30 +34,8 @@ GSRendererHW::GSRendererHW()
, m_reset(false)
, m_lod(GSVector2i(0, 0))
{
m_mipmap = (m_hw_mipmap >= HWMipmapLevel::Basic);
m_conservative_framebuffer = theApp.GetConfigB("conservative_framebuffer");
if (theApp.GetConfigB("UserHacks"))
{
m_userhacks_enabled_gs_mem_clear = !theApp.GetConfigB("UserHacks_Disable_Safe_Features");
m_userHacks_enabled_unscale_ptln = !theApp.GetConfigB("UserHacks_Disable_Safe_Features");
m_userhacks_align_sprite_X = theApp.GetConfigB("UserHacks_align_sprite_X");
m_userHacks_merge_sprite = theApp.GetConfigB("UserHacks_merge_pp_sprite");
m_userhacks_ts_half_bottom = theApp.GetConfigI("UserHacks_Half_Bottom_Override");
m_userhacks_round_sprite_offset = theApp.GetConfigI("UserHacks_round_sprite_offset");
m_userhacks_tcoffset_x = theApp.GetConfigI("UserHacks_TCOffsetX") / -1000.0f;
m_userhacks_tcoffset_y = theApp.GetConfigI("UserHacks_TCOffsetY") / -1000.0f;
m_userhacks_tcoffset = m_userhacks_tcoffset_x < 0.0f || m_userhacks_tcoffset_y < 0.0f;
}
else
{
m_userhacks_enabled_gs_mem_clear = true;
m_userHacks_enabled_unscale_ptln = true;
m_userhacks_align_sprite_X = false;
m_userHacks_merge_sprite = false;
m_userhacks_ts_half_bottom = -1;
m_userhacks_round_sprite_offset = 0;
}
m_mipmap = (GSConfig.HWMipmap >= HWMipmapLevel::Basic);
SetTCOffset();
if (!GSConfig.UpscaleMultiplier) // Custom Resolution
{
@@ -67,13 +43,6 @@ GSRendererHW::GSRendererHW()
m_custom_height = m_height = theApp.GetConfigI("resy");
}
if (GSConfig.UpscaleMultiplier == 1) // hacks are only needed for upscaling issues.
{
m_userhacks_round_sprite_offset = 0;
m_userhacks_align_sprite_X = false;
m_userHacks_merge_sprite = false;
}
m_dump_root = root_hw;
GSTextureReplacements::Initialize(m_tc);
}
@@ -127,7 +96,7 @@ void GSRendererHW::SetScaling()
//
// m_large_framebuffer has been inverted to m_conservative_framebuffer, it isn't an option that benefits being enabled all the time for everyone.
int fb_height = 1280;
if (m_conservative_framebuffer)
if (GSConfig.ConservativeFramebuffer)
{
fb_height = fb_width < 1024 ? std::max(512, crtc_size.y) : 1024;
}
@@ -183,6 +152,13 @@ void GSRendererHW::CustomResolutionScaling()
printf("Frame buffer size set to %dx%d (%dx%d)\n", scissored_buffer_size.x, scissored_buffer_size.y, m_width, m_height);
}
void GSRendererHW::SetTCOffset()
{
m_userhacks_tcoffset_x = std::max<s32>(GSConfig.UserHacks_TCOffsetX, 0) / -1000.0f;
m_userhacks_tcoffset_y = std::max<s32>(GSConfig.UserHacks_TCOffsetY, 0) / -1000.0f;
m_userhacks_tcoffset = m_userhacks_tcoffset_x < 0.0f || m_userhacks_tcoffset_y < 0.0f;
}
GSRendererHW::~GSRendererHW()
{
delete m_tc;
@@ -207,63 +183,6 @@ void GSRendererHW::SetGameCRC(u32 crc, int options)
m_hacks.SetGameCRC(m_game);
// Code for Automatic Mipmapping. Relies on game CRCs.
m_hw_mipmap = GSConfig.HWMipmap;
m_mipmap = (m_hw_mipmap >= HWMipmapLevel::Basic);
if (m_hw_mipmap == HWMipmapLevel::Automatic)
{
switch (CRC::Lookup(crc).title)
{
case CRC::AceCombatZero:
case CRC::AceCombat4:
case CRC::AceCombat5:
case CRC::ApeEscape2:
case CRC::Barnyard:
case CRC::BrianLaraInternationalCricket:
case CRC::DarkCloud:
case CRC::DestroyAllHumans:
case CRC::DestroyAllHumans2:
case CRC::FIFA03:
case CRC::FIFA04:
case CRC::FIFA05:
case CRC::HarryPotterATCOS:
case CRC::HarryPotterATGOF:
case CRC::HarryPotterATHBP:
case CRC::HarryPotterATPOA:
case CRC::HarryPotterOOTP:
case CRC::ICO:
case CRC::Jak1:
case CRC::Jak3:
case CRC::JurassicPark:
case CRC::LegacyOfKainDefiance:
case CRC::NicktoonsUnite:
case CRC::Persona3:
case CRC::ProjectSnowblind:
case CRC::Quake3Revolution:
case CRC::RatchetAndClank:
case CRC::RatchetAndClank2:
case CRC::RatchetAndClank3:
case CRC::RatchetAndClank4:
case CRC::RatchetAndClank5:
case CRC::RickyPontingInternationalCricket:
case CRC::Shox:
case CRC::SlamTennis:
case CRC::SoTC:
case CRC::SoulReaver2:
case CRC::TheIncredibleHulkUD:
case CRC::TombRaiderAnniversary:
case CRC::TribesAerialAssault:
case CRC::Whiplash:
m_hw_mipmap = HWMipmapLevel::Basic;
m_mipmap = true;
break;
default:
m_hw_mipmap = HWMipmapLevel::Off;
m_mipmap = false;
break;
}
}
GSTextureReplacements::GameChanged();
}
@@ -297,6 +216,13 @@ void GSRendererHW::Reset()
GSRenderer::Reset();
}
void GSRendererHW::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
{
GSRenderer::UpdateSettings(old_config);
m_mipmap = (GSConfig.HWMipmap >= HWMipmapLevel::Basic);
SetTCOffset();
}
void GSRendererHW::VSync(u32 field, bool registers_written)
{
if (m_reset)
@@ -544,7 +470,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
read_ba = (tex_pos > 112 && tex_pos < 144);
bool half_bottom = false;
switch (m_userhacks_ts_half_bottom)
switch (GSConfig.UserHacks_HalfBottomOverride)
{
case 0:
// Force Disabled.
@@ -572,9 +498,30 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
//
// 32bits emulation means we can do the effect once but double the size.
// Test cases: Crash Twinsantiy and DBZ BT3
const int height_delta = m_src->m_valid_rect.height() - m_r.height();
// Test Case: NFS: HP2 splits the effect h:256 and h:192 so 64
half_bottom = abs(height_delta) <= 64;
// Other games: Midnight Club 3 headlights, black bar in Xenosaga 3 dialogue,
// Firefighter FD18 fire occlusion, PSI Ops half screen green overlay, Lord of the Rings - Two Towers,
// Demon Stone , Sonic Unleashed, Lord of the Rings Two Towers,
// Superman Shadow of Apokolips, Matrix Path of Neo, Big Mutha Truckers
int maxvert = 0;
int minvert = 4096;
for (size_t i = 0; i < count; i ++)
{
int YCord = 0;
if (!PRIM->FST)
YCord = (int)((1 << m_context->TEX0.TH) * (v[i].ST.T / v[i].RGBAQ.Q));
else
YCord = (v[i].V >> 4);
if (maxvert < YCord)
maxvert = YCord;
if (minvert > YCord)
minvert = YCord;
}
half_bottom = minvert == 0 && m_r.height() <= maxvert;
break;
}
@@ -753,7 +700,7 @@ GSVector4i GSRendererHW::ComputeBoundingBox(const GSVector2& rtscale, const GSVe
void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
{
// Upscaling hack to avoid various line/grid issues
if (m_userHacks_merge_sprite && tex && tex->m_target && (m_vt.m_primclass == GS_SPRITE_CLASS))
if (GSConfig.UserHacks_MergePPSprite && tex && tex->m_target && (m_vt.m_primclass == GS_SPRITE_CLASS))
{
if (PRIM->FST && GSLocalMemory::m_psm[tex->m_TEX0.PSM].fmt < 2 && ((m_vt.m_eq.value & 0xCFFFF) == 0xCFFFF))
{
@@ -1249,6 +1196,20 @@ void GSRendererHW::RoundSpriteOffset()
void GSRendererHW::Draw()
{
if (s_dump && (s_n >= s_saven))
{
std::string s;
// Dump Register state
s = format("%05d_context.txt", s_n);
m_env.Dump(m_dump_root + s);
m_context->Dump(m_dump_root + s);
// Dump vertices
s = format("%05d_vertex.txt", s_n);
DumpVertices(m_dump_root + s);
}
if (IsBadFrame())
{
GL_INS("Warning skipping a draw call (%d)", s_n);
@@ -1424,7 +1385,7 @@ void GSRendererHW::Draw()
// upload the full chain (with offset) for the hash cache, in case some other texture uses more levels
// for basic mipmapping, we can get away with just doing the base image, since all the mips get generated anyway.
hash_lod_range = GSVector2i(m_lod.x, (m_hw_mipmap == HWMipmapLevel::Full) ? mxl : m_lod.x);
hash_lod_range = GSVector2i(m_lod.x, (GSConfig.HWMipmap == HWMipmapLevel::Full) ? mxl : m_lod.x);
MIP_CLAMP.MINU >>= m_lod.x;
MIP_CLAMP.MINV >>= m_lod.x;
@@ -1449,7 +1410,7 @@ void GSRendererHW::Draw()
TextureMinMaxResult tmm = GetTextureMinMax(TEX0, MIP_CLAMP, m_vt.IsLinear());
m_src = tex_psm.depth ? m_tc->LookupDepthSource(TEX0, env.TEXA, tmm.coverage) :
m_tc->LookupSource(TEX0, env.TEXA, tmm.coverage, (m_hw_mipmap >= HWMipmapLevel::Basic ||
m_tc->LookupSource(TEX0, env.TEXA, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic ||
GSConfig.UserHacks_TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr);
int tw = 1 << TEX0.TW;
@@ -1504,7 +1465,7 @@ void GSRendererHW::Draw()
}
// Round 2
if (IsMipMapActive() && m_hw_mipmap == HWMipmapLevel::Full && !tex_psm.depth && !m_src->m_from_hash_cache)
if (IsMipMapActive() && GSConfig.HWMipmap == HWMipmapLevel::Full && !tex_psm.depth && !m_src->m_from_hash_cache)
{
// Upload remaining texture layers
const GSVector4 tmin = m_vt.m_min.t;
@@ -1655,15 +1616,6 @@ void GSRendererHW::Draw()
std::string s;
if (s_n >= s_saven)
{
// Dump Register state
s = format("%05d_context.txt", s_n);
m_env.Dump(m_dump_root + s);
m_context->Dump(m_dump_root + s);
}
if (s_savet && s_n >= s_saven && m_src)
{
s = format("%05d_f%lld_itex_%05x_%s_%d%d_%02x_%02x_%02x_%02x.dds",
@@ -1711,7 +1663,7 @@ void GSRendererHW::Draw()
return;
}
if (m_userhacks_enabled_gs_mem_clear)
if (!GSConfig.UserHacks_DisableSafeFeatures)
{
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
if ((m_vt.m_primclass == GS_SPRITE_CLASS) && !PRIM->TME // Direct write
@@ -1737,7 +1689,7 @@ void GSRendererHW::Draw()
GSVertex* v = &m_vertex.buff[0];
// Hack to avoid vertical black line in various games (ace combat/tekken)
if (m_userhacks_align_sprite_X)
if (GSConfig.UserHacks_AlignSpriteX)
{
// Note for performance reason I do the check only once on the first
// primitive
@@ -1763,7 +1715,7 @@ void GSRendererHW::Draw()
// Noting to do if no texture is sampled
if (PRIM->FST && draw_sprite_tex)
{
if ((m_userhacks_round_sprite_offset > 1) || (m_userhacks_round_sprite_offset == 1 && !m_vt.IsLinear()))
if ((GSConfig.UserHacks_RoundSprite > 1) || (GSConfig.UserHacks_RoundSprite == 1 && !m_vt.IsLinear()))
{
if (m_vt.IsLinear())
RoundSpriteOffset<true>();
@@ -1888,9 +1840,10 @@ void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
m_oo = m_oo_map[hash];
m_cu = m_cu_map[hash];
if (game.flags & CRC::PointListPalette)
if (GSConfig.PointListPalette)
{
ASSERT(m_oi == NULL);
if (m_oi)
Console.Warning("Overriding m_oi with PointListPalette");
m_oi = &GSRendererHW::OI_PointListPalette;
}
+2 -9
View File
@@ -27,12 +27,6 @@ private:
int m_height;
int m_custom_width;
int m_custom_height;
int m_userhacks_ts_half_bottom;
bool m_conservative_framebuffer;
bool m_userhacks_align_sprite_X;
bool m_userhacks_enabled_gs_mem_clear;
bool m_userHacks_merge_sprite;
static constexpr float SSR_UV_TOLERANCE = 1.0f;
@@ -144,12 +138,10 @@ protected:
GSTextureCache* m_tc;
GSVector4i m_r;
GSTextureCache::Source* m_src;
HWMipmapLevel m_hw_mipmap;
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
int m_userhacks_round_sprite_offset;
bool m_userHacks_enabled_unscale_ptln;
void SetTCOffset();
bool m_userhacks_tcoffset;
float m_userhacks_tcoffset_x;
@@ -185,6 +177,7 @@ public:
GSVector2i GetTargetSize();
void Reset() override;
void UpdateSettings(const Pcsx2Config::GSOptions& old_config) override;
void VSync(u32 field, bool registers_written) override;
GSTexture* GetOutput(int i, int& y_offset) override;
+75 -43
View File
@@ -33,12 +33,12 @@ void GSRendererNew::SetupIA(const float& sx, const float& sy)
{
GL_PUSH("IA");
if (m_userhacks_wildhack && !m_isPackedUV_HackFlag && PRIM->TME && PRIM->FST)
if (GSConfig.UserHacks_WildHack && !m_isPackedUV_HackFlag && PRIM->TME && PRIM->FST)
{
for (unsigned int i = 0; i < m_vertex.next; i++)
m_vertex.buff[i].UV &= 0x3FEF3FEF;
}
const bool unscale_pt_ln = m_userHacks_enabled_unscale_ptln && (GetUpscaleMultiplier() != 1);
const bool unscale_pt_ln = !GSConfig.UserHacks_DisableSafeFeatures && (GetUpscaleMultiplier() != 1);
const GSDevice::FeatureSupport features = g_gs_device->Features();
switch (m_vt.m_primclass)
@@ -540,23 +540,68 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
// Compute the blending equation to detect special case
const GIFRegALPHA& ALPHA = m_context->ALPHA;
// Set blending to shader bits
m_conf.ps.blend_a = ALPHA.A;
m_conf.ps.blend_b = ALPHA.B;
m_conf.ps.blend_c = ALPHA.C;
m_conf.ps.blend_d = ALPHA.D;
// Get alpha value
const bool alpha_c0_zero = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max == 0);
const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128));
const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128);
const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && ALPHA.FIX == 0u);
const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && ALPHA.FIX == 128u);
const bool alpha_c2_high_one = (m_conf.ps.blend_c == 2 && ALPHA.FIX > 128u);
// Optimize blending equations, must be done before index calculation
if ((m_conf.ps.blend_a == m_conf.ps.blend_b) || ((m_conf.ps.blend_b == m_conf.ps.blend_d) && (alpha_c0_one || alpha_c2_one)))
{
// Condition 1:
// A == B
// (A - B) * C, result will be 0.0f so set A B to Cs, C to As
// Condition 2:
// B == D
// Swap D with A
// A == B
// (A - B) * C, result will be 0.0f so set A B to Cs, C to As
if (m_conf.ps.blend_a != m_conf.ps.blend_b)
m_conf.ps.blend_d = m_conf.ps.blend_a;
m_conf.ps.blend_a = 0;
m_conf.ps.blend_b = 0;
m_conf.ps.blend_c = 0;
}
else if (alpha_c0_zero || alpha_c2_zero)
{
// C == 0.0f
// (A - B) * C, result will be 0.0f so set A B to Cs
m_conf.ps.blend_a = 0;
m_conf.ps.blend_b = 0;
}
// Ad cases, alpha write is masked, one barrier is enough, for d3d11 read the fb
// Replace Ad with As, blend flags will be used from As since we are chaging the blend_index value.
bool blend_ad_alpha_masked = (ALPHA.C == 1) && (m_context->FRAME.FBMSK & 0xFF000000) == 0xFF000000;
u8 ALPHA_C = ALPHA.C;
if (g_gs_device->Features().texture_barrier && blend_ad_alpha_masked)
ALPHA_C = 0;
// Must be done before index calculation, after blending equation optimizations
bool blend_ad_alpha_masked = (m_conf.ps.blend_c == 1) && (m_context->FRAME.FBMSK & 0xFF000000) == 0xFF000000;
if (((GSConfig.AccurateBlendingUnit >= AccBlendLevel::Basic) || (m_env.COLCLAMP.CLAMP == 0))
&& g_gs_device->Features().texture_barrier && blend_ad_alpha_masked)
m_conf.ps.blend_c = 0;
else if (((GSConfig.AccurateBlendingUnit >= AccBlendLevel::Medium)
// Detect barrier aka fbmask on d3d11.
|| m_conf.require_one_barrier)
&& blend_ad_alpha_masked)
ALPHA_C = 0;
m_conf.ps.blend_c = 0;
else
blend_ad_alpha_masked = false;
u8 blend_index = u8(((ALPHA.A * 3 + ALPHA.B) * 3 + ALPHA_C) * 3 + ALPHA.D);
u8 blend_index = u8(((m_conf.ps.blend_a * 3 + m_conf.ps.blend_b) * 3 + m_conf.ps.blend_c) * 3 + m_conf.ps.blend_d);
const int blend_flag = g_gs_device->GetBlendFlags(blend_index);
// Re set alpha, it was modified, must be done after index calculation
if (blend_ad_alpha_masked)
m_conf.ps.blend_c = ALPHA.C;
// HW blend can handle Cd output.
bool color_dest_blend = !!(blend_flag & BLEND_CD);
@@ -572,15 +617,12 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
const bool blend_mix3 = !!(blend_flag & BLEND_MIX3);
bool blend_mix = (blend_mix1 || blend_mix2 || blend_mix3);
const bool alpha_c2_high_one = (ALPHA.C == 2 && ALPHA.FIX > 128u);
const bool alpha_c0_high_max_one = (ALPHA.C == 0 && GetAlphaMinMax().max > 128);
// Blend can be done on hw. As and F cases should be accurate.
// BLEND_C_CLR1 with Ad, BLEND_C_CLR3 Cs > 0.5f will require sw blend.
// BLEND_C_CLR1 with As/F, BLEND_C_CLR2_AF, BLEND_C_CLR2_AS can be done in hw.
const bool clr_blend = !!(blend_flag & (BLEND_C_CLR1 | BLEND_C_CLR2_AF | BLEND_C_CLR2_AS | BLEND_C_CLR3));
bool clr_blend1_2 = (blend_flag & (BLEND_C_CLR1 | BLEND_C_CLR2_AF | BLEND_C_CLR2_AS))
&& (ALPHA.C != 1) // Make sure it isn't an Ad case
&& (m_conf.ps.blend_c != 1) // Make sure it isn't an Ad case
&& !m_env.PABE.PABE // No PABE as it will require sw blending.
&& (m_env.COLCLAMP.CLAMP) // Let's add a colclamp check too, hw blend will clamp to 0-1.
&& !(m_conf.require_one_barrier || m_conf.require_full_barrier); // Also don't run if there are barriers present.
@@ -608,10 +650,10 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
sw_blending |= true;
[[fallthrough]];
case AccBlendLevel::Full:
sw_blending |= ALPHA.A != ALPHA.B && alpha_c0_high_max_one;
sw_blending |= m_conf.ps.blend_a != m_conf.ps.blend_b && alpha_c0_high_max_one;
[[fallthrough]];
case AccBlendLevel::High:
sw_blending |= ALPHA.C == 1 || (ALPHA.A != ALPHA.B && alpha_c2_high_one);
sw_blending |= m_conf.ps.blend_c == 1 || (m_conf.ps.blend_a != m_conf.ps.blend_b && alpha_c2_high_one);
[[fallthrough]];
case AccBlendLevel::Medium:
// Initial idea was to enable accurate blending for sprite rendering to handle
@@ -654,7 +696,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
}
[[fallthrough]];
case AccBlendLevel::Full:
sw_blending |= ((ALPHA.C == 1 || (blend_mix && (alpha_c2_high_one || alpha_c0_high_max_one))) && (m_prim_overlap == PRIM_OVERLAP_NO));
sw_blending |= ((m_conf.ps.blend_c == 1 || (blend_mix && (alpha_c2_high_one || alpha_c0_high_max_one))) && (m_prim_overlap == PRIM_OVERLAP_NO));
[[fallthrough]];
case AccBlendLevel::High:
sw_blending |= (!(clr_blend || blend_mix) && (m_prim_overlap == PRIM_OVERLAP_NO));
@@ -694,7 +736,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
else
free_colclip = blend_non_recursive;
GL_DBG("COLCLIP Info (Blending: %u/%u/%u/%u, OVERLAP: %d)", ALPHA.A, ALPHA.B, ALPHA.C, ALPHA.D, m_prim_overlap);
GL_DBG("COLCLIP Info (Blending: %u/%u/%u/%u, OVERLAP: %d)", m_conf.ps.blend_a, m_conf.ps.blend_b, m_conf.ps.blend_c, m_conf.ps.blend_d, m_prim_overlap);
if (color_dest_blend)
{
// No overflow, disable colclip.
@@ -758,7 +800,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
m_conf.ps.pabe = !(accumulation_blend || blend_mix);
}
}
else if (ALPHA.A == 0 && ALPHA.B == 1 && ALPHA.C == 0 && ALPHA.D == 1)
else if (m_conf.ps.blend_a == 0 && m_conf.ps.blend_b == 1 && m_conf.ps.blend_c == 0 && m_conf.ps.blend_d == 1)
{
// this works because with PABE alpha blending is on when alpha >= 0x80, but since the pixel shader
// cannot output anything over 0x80 (== 1.0) blending with 0x80 or turning it off gives the same result
@@ -769,7 +811,8 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
// For stat to optimize accurate option
#if 0
GL_INS("BLEND_INFO: %u/%u/%u/%u. Clamp:%u. Prim:%d number %u (drawlist %u) (sw %d)",
ALPHA.A, ALPHA.B, ALPHA.C, ALPHA.D, m_env.COLCLAMP.CLAMP, m_vt.m_primclass, m_vertex.next, m_drawlist.size(), sw_blending);
m_conf.ps.blend_a, m_conf.ps.blend_b, m_conf.ps.blend_c, m_conf.ps.blend_d,
m_env.COLCLAMP.CLAMP, m_vt.m_primclass, m_vertex.next, m_drawlist.size(), sw_blending);
#endif
if (color_dest_blend)
{
@@ -784,24 +827,9 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
}
else if (sw_blending)
{
m_conf.ps.blend_a = ALPHA.A;
m_conf.ps.blend_b = ALPHA.B;
m_conf.ps.blend_c = ALPHA.C;
m_conf.ps.blend_d = ALPHA.D;
if (m_conf.ps.blend_a == m_conf.ps.blend_b)
{
// A == B
// (A - B) * C will be 0 so set A B to Cs, C to As
m_conf.ps.blend_a = 0;
m_conf.ps.blend_b = 0;
m_conf.ps.blend_c = 0;
}
else if (m_conf.ps.blend_c == 2)
{
// Require the fix alpha vlaue
// Require the fix alpha vlaue
if (m_conf.ps.blend_c == 2)
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(ALPHA.FIX) / 128.0f;
}
if (accumulation_blend)
{
@@ -869,8 +897,12 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
}
else
{
// Care for clr_hw value, 6 is for hw/sw, sw blending used.
// No sw blending
m_conf.ps.blend_a = 0;
m_conf.ps.blend_b = 0;
m_conf.ps.blend_d = 0;
// Care for clr_hw value, 6 is for hw/sw, sw blending used.
if (blend_flag & BLEND_C_CLR1)
{
if (blend_ad_alpha_masked)
@@ -892,13 +924,13 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
m_conf.ps.clr_hw = 4;
m_conf.require_one_barrier |= true;
}
else if (ALPHA.C == 2)
else if (m_conf.ps.blend_c == 2)
{
m_conf.ps.blend_c = 2;
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(ALPHA.FIX) / 128.0f;
m_conf.ps.clr_hw = 2;
}
else // ALPHA.C == 0
else // m_conf.ps.blend_c == 0
{
m_conf.ps.blend_c = 0;
m_conf.ps.clr_hw = 2;
@@ -915,7 +947,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
m_conf.require_one_barrier |= true;
}
if (m_conf.ps.dfmt == 1 && ALPHA.C == 1)
if (m_conf.ps.dfmt == 1 && m_conf.ps.blend_c == 1)
{
// 24 bits doesn't have an alpha channel so use 1.0f fix factor as equivalent
const u8 hacked_blend_index = blend_index + 3; // +3 <=> +1 on C
@@ -923,7 +955,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
}
else
{
m_conf.blend = {blend_index, ALPHA.FIX, ALPHA.C == 2, false, false};
m_conf.blend = {blend_index, ALPHA.FIX, m_conf.ps.blend_c == 2, false, false};
}
}
@@ -955,7 +987,7 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
const bool need_mipmap = IsMipMapDraw();
const bool shader_emulated_sampler = tex->m_palette || cpsm.fmt != 0 || complex_wms_wmt || psm.depth;
const bool trilinear_manual = need_mipmap && m_hw_mipmap == HWMipmapLevel::Full;
const bool trilinear_manual = need_mipmap && GSConfig.HWMipmap == HWMipmapLevel::Full;
bool bilinear = m_vt.IsLinear();
int trilinear = 0;
@@ -964,11 +996,11 @@ void GSRendererNew::EmulateTextureSampler(const GSTextureCache::Source* tex)
{
case TriFiltering::Forced:
trilinear = static_cast<u8>(GS_MIN_FILTER::Linear_Mipmap_Linear);
trilinear_auto = !need_mipmap || m_hw_mipmap != HWMipmapLevel::Full;
trilinear_auto = !need_mipmap || GSConfig.HWMipmap != HWMipmapLevel::Full;
break;
case TriFiltering::PS2:
if (need_mipmap && m_hw_mipmap != HWMipmapLevel::Full)
if (need_mipmap && GSConfig.HWMipmap != HWMipmapLevel::Full)
{
trilinear = m_context->TEX1.MMIN;
trilinear_auto = true;
+15 -45
View File
@@ -27,35 +27,12 @@
#define XXH_INLINE_ALL 1
#include "xxhash.h"
bool GSTextureCache::m_disable_partial_invalidation = false;
bool GSTextureCache::m_wrap_gs_mem = false;
u8* GSTextureCache::m_temp;
GSTextureCache::GSTextureCache(GSRenderer* r)
: m_renderer(r)
, m_palette_map(r)
{
if (theApp.GetConfigB("UserHacks"))
{
UserHacks_HalfPixelOffset = theApp.GetConfigI("UserHacks_HalfPixelOffset") == 1;
m_preload_frame = theApp.GetConfigB("preload_frame_with_gs_data");
m_disable_partial_invalidation = theApp.GetConfigB("UserHacks_DisablePartialInvalidation");
m_can_convert_depth = !theApp.GetConfigB("UserHacks_DisableDepthSupport");
m_cpu_fb_conversion = theApp.GetConfigB("UserHacks_CPU_FB_Conversion");
m_texture_inside_rt = theApp.GetConfigB("UserHacks_TextureInsideRt");
m_wrap_gs_mem = theApp.GetConfigB("wrap_gs_mem");
}
else
{
UserHacks_HalfPixelOffset = false;
m_preload_frame = false;
m_disable_partial_invalidation = false;
m_can_convert_depth = true;
m_cpu_fb_conversion = false;
m_texture_inside_rt = false;
m_wrap_gs_mem = false;
}
// In theory 4MB is enough but 9MB is safer for overflow (8MB
// isn't enough in custom resolution)
// Test: onimusha 3 PAL 60Hz
@@ -110,7 +87,7 @@ void GSTextureCache::RemoveAll()
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette)
{
if (!m_can_convert_depth)
if (GSConfig.UserHacks_DisableDepthSupport)
{
GL_CACHE("LookupDepthSource not supported (0x%x, F:0x%x)", TEX0.TBP0, TEX0.PSM);
throw GSRecoverableError();
@@ -280,8 +257,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// (Simply not doing this code at all makes a lot of previsouly missing stuff show (but breaks pretty much everything
// else.)
const bool texture_inside_rt = ShallSearchTextureInsideRt();
bool found_t = false;
for (auto t : m_dst[RenderTarget])
{
@@ -305,7 +280,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// 1/ it just works :)
// 2/ even with upscaling
// 3/ for both Direct3D and OpenGL
if (m_cpu_fb_conversion && (psm == PSM_PSMT4 || psm == PSM_PSMT8))
if (GSConfig.UserHacks_CPUFBConversion && (psm == PSM_PSMT4 || psm == PSM_PSMT8))
// Forces 4-bit and 8-bit frame buffer conversion to be done on the CPU instead of the GPU, but performance will be slower.
// There is no dedicated shader to handle 4-bit conversion (Stuntman has been confirmed to use 4-bit).
// Direct3D10/11 and OpenGL support 8-bit fb conversion but don't render some corner cases properly (Harry Potter games).
@@ -326,7 +301,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
found_t = true;
break;
}
else if (texture_inside_rt && psm == PSM_PSMCT32 && t->m_TEX0.PSM == psm &&
else if (GSConfig.UserHacks_TextureInsideRt && psm == PSM_PSMCT32 && t->m_TEX0.PSM == psm &&
((t->m_TEX0.TBP0 < bp && t->m_end_block >= bp) || t_wraps))
{
// Only PSMCT32 to limit false hits.
@@ -366,7 +341,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
//
// Sigh... They don't help us.
if (!found_t && m_can_convert_depth)
if (!found_t && !GSConfig.UserHacks_DisableDepthSupport)
{
// Let's try a trick to avoid to use wrongly a depth buffer
// Unfortunately, I don't have any Arc the Lad testcase
@@ -439,11 +414,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
return src;
}
bool GSTextureCache::ShallSearchTextureInsideRt()
{
return m_texture_inside_rt || (m_renderer->m_game.flags & CRC::Flags::TextureInsideRt);
}
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_h)
{
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
@@ -545,7 +515,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
if (!is_frame)
dst->m_dirty_alpha |= (psm_s.trbpp == 32 && (fbmask & 0xFF000000) != 0xFF000000) || (psm_s.trbpp == 16);
}
else if (!is_frame && m_can_convert_depth)
else if (!is_frame && !GSConfig.UserHacks_DisableDepthSupport)
{
int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
@@ -608,8 +578,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
//
// From a performance point of view, it might cost a little on big upscaling
// but normally few RT are miss so it must remain reasonable.
bool supported_fmt = m_can_convert_depth || psm_s.depth == 0;
if (m_preload_frame && TEX0.TBW > 0 && supported_fmt)
bool supported_fmt = !GSConfig.UserHacks_DisableDepthSupport || psm_s.depth == 0;
if (GSConfig.PreloadFrameWithGSData && TEX0.TBW > 0 && supported_fmt)
{
GL_INS("Preloading the RT DATA");
// RT doesn't have height but if we use a too big value, we will read outside of the GS memory.
@@ -644,7 +614,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
// must invalidate the Target/Depth respectively
void GSTextureCache::InvalidateVideoMemType(int type, u32 bp)
{
if (!m_can_convert_depth)
if (GSConfig.UserHacks_DisableDepthSupport)
return;
auto& list = m_dst[type];
@@ -761,7 +731,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
// No point keeping invalidated sources around when the hash cache is active,
// we can just re-hash and create a new source from the cached texture.
if (s->m_from_hash_cache || (m_disable_partial_invalidation && s->m_repeating))
if (s->m_from_hash_cache || (GSConfig.UserHacks_DisablePartialInvalidation && s->m_repeating))
{
m_src.RemoveAt(s);
}
@@ -937,7 +907,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
if (psm == PSM_PSMZ32 || psm == PSM_PSMZ24 || psm == PSM_PSMZ16 || psm == PSM_PSMZ16S)
{
GL_INS("ERROR: InvalidateLocalMem depth format isn't supported (%d,%d to %d,%d)", r.x, r.y, r.z, r.w);
if (m_can_convert_depth)
if (!GSConfig.UserHacks_DisableDepthSupport)
{
auto& dss = m_dst[DepthStencil];
for (auto it = dss.rbegin(); it != dss.rend(); ++it) // Iterate targets from LRU to MRU.
@@ -986,7 +956,7 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
if (t->m_32_bits_fmt && t->m_TEX0.PSM > PSM_PSMCT24)
t->m_TEX0.PSM = PSM_PSMCT32;
if (GSTextureCache::m_disable_partial_invalidation)
if (GSConfig.UserHacks_DisablePartialInvalidation)
{
Read(t, r.rintersect(t->m_valid));
}
@@ -1430,7 +1400,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
float modx = 0.0f;
float mody = 0.0f;
if (UserHacks_HalfPixelOffset && hack)
if (GSConfig.UserHacks_HalfPixelOffset == 1 && hack)
{
switch(m_renderer->GetUpscaleMultiplier())
{
@@ -1607,7 +1577,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
{
ASSERT(type == RenderTarget || type == DepthStencil);
Target* t = new Target(m_renderer, TEX0, m_can_convert_depth, type);
Target* t = new Target(m_renderer, TEX0, !GSConfig.UserHacks_DisableDepthSupport, type);
// FIXME: initial data should be unswizzled from local mem in Update() if dirty
@@ -1886,7 +1856,7 @@ void GSTextureCache::Source::Update(const GSVector4i& rect, int level)
int i = (bn.blkY() << 7) + bn.blkX();
u32 block = bn.valueNoWrap();
if (block < MAX_BLOCKS || m_wrap_gs_mem)
if (block < MAX_BLOCKS || GSConfig.WrapGSMem)
{
u32 addr = i % MAX_BLOCKS;
@@ -1913,7 +1883,7 @@ void GSTextureCache::Source::Update(const GSVector4i& rect, int level)
{
u32 block = bn.valueNoWrap();
if (block < MAX_BLOCKS || m_wrap_gs_mem)
if (block < MAX_BLOCKS || GSConfig.WrapGSMem)
{
block %= MAX_BLOCKS;
-9
View File
@@ -282,13 +282,7 @@ protected:
std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache;
u64 m_hash_cache_memory_usage = 0;
FastList<Target*> m_dst[2];
bool m_preload_frame;
static u8* m_temp;
bool m_can_convert_depth;
bool m_cpu_fb_conversion;
static bool m_disable_partial_invalidation;
bool m_texture_inside_rt;
static bool m_wrap_gs_mem;
constexpr static size_t S_SURFACE_OFFSET_CACHE_MAX_SIZE = std::numeric_limits<u16>::max();
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
@@ -326,9 +320,6 @@ public:
void InvalidateLocalMem(const GSOffset& off, const GSVector4i& r);
void IncAge();
bool UserHacks_HalfPixelOffset;
bool ShallSearchTextureInsideRt();
const char* to_string(int type)
{
+1 -1
View File
@@ -881,7 +881,7 @@ GLuint GSDeviceOGL::CreateSampler(PSSamplerSelector sel)
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
const int anisotropy = theApp.GetConfigI("MaxAnisotropy");
const int anisotropy = GSConfig.MaxAnisotropy;
if (anisotropy && sel.aniso)
{
if (GLExtension::Has("GL_ARB_texture_filter_anisotropic"))
+20 -19
View File
@@ -58,14 +58,6 @@ GSRendererSW::GSRendererSW(int threads)
InitCVB(GS_SPRITE_CLASS);
m_dump_root = root_sw;
// Reset handler with the auto flush hack enabled on the SW renderer.
// Some games run better without the hack so rely on ini/gui option.
if (theApp.GetConfigB("autoflush_sw"))
{
m_userhacks_auto_flush = true;
ResetHandlers();
}
}
GSRendererSW::~GSRendererSW()
@@ -325,6 +317,24 @@ void GSRendererSW::Draw()
{
const GSDrawingContext* context = m_context;
if (s_dump)
{
std::string s;
if (s_n >= s_saven)
{
// Dump Register state
s = format("%05d_context.txt", s_n);
m_env.Dump(m_dump_root + s);
m_context->Dump(m_dump_root + s);
// Dump vertices
s = format("%05d_vertex.txt", s_n);
DumpVertices(m_dump_root + s);
}
}
auto data = m_vertex_heap.make_shared<SharedData>(this).cast<GSRasterizerData>();
SharedData* sd = static_cast<SharedData*>(data.get());
@@ -434,22 +444,13 @@ void GSRendererSW::Draw()
{
Sync(2);
std::string s;
u64 frame = g_perfmon.GetFrame();
// Dump the texture in 32 bits format. It helps to debug texture shuffle effect
// It will breaks the few games that really uses 16 bits RT
bool texture_shuffle = ((context->FRAME.PSM & 0x2) && ((context->TEX0.PSM & 3) == 2) && (m_vt.m_primclass == GS_SPRITE_CLASS));
std::string s;
if (s_n >= s_saven)
{
// Dump Register state
s = format("%05d_context.txt", s_n);
m_env.Dump(m_dump_root + s);
m_context->Dump(m_dump_root + s);
}
if (s_savet && s_n >= s_saven && PRIM->TME)
{
if (texture_shuffle)
+18 -4
View File
@@ -2769,11 +2769,25 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
const bool render_area_okay =
(!hdr_rt && DATE_rp != DATE_RENDER_PASS_STENCIL_ONE && CheckRenderPassArea(render_area));
const bool same_framebuffer =
(InRenderPass() && m_current_render_target == draw_rt && m_current_depth_target == draw_ds);
// Prefer keeping feedback loop enabled, that way we're not constantly restarting render passes
pipe.feedback_loop |= render_area_okay && same_framebuffer && CurrentFramebufferHasFeedbackLoop();
// render pass restart optimizations
if (render_area_okay)
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
if (!draw_ds && m_current_depth_target && m_current_render_target == draw_rt &&
config.tex != m_current_depth_target && !(pipe.feedback_loop && !CurrentFramebufferHasFeedbackLoop()))
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
m_pipeline_selector.dss.ztst = ZTST_ALWAYS;
m_pipeline_selector.dss.zwe = false;
}
// Prefer keeping feedback loop enabled, that way we're not constantly restarting render passes
pipe.feedback_loop |= m_current_render_target == draw_rt && m_current_depth_target == draw_ds &&
CurrentFramebufferHasFeedbackLoop();
}
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, pipe.feedback_loop);
if (pipe.feedback_loop)
PSSetShaderResource(2, draw_rt, false);
+1 -1
View File
@@ -328,7 +328,7 @@ HacksTab::HacksTab(wxWindow* parent)
PaddedBoxSizer<wxBoxSizer> tab_box(wxVERTICAL);
auto hw_prereq = [this]{ return m_is_hardware; };
auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Enable HW Hacks", "UserHacks", -1, hw_prereq);
auto* hacks_check_box = m_ui.addCheckBox(tab_box.inner, "Manual HW Hacks", "UserHacks", -1, hw_prereq);
auto hacks_prereq = [this, hacks_check_box]{ return m_is_hardware && hacks_check_box->GetValue(); };
auto upscale_hacks_prereq = [this, hacks_check_box]{ return !m_is_native_res && hacks_check_box->GetValue(); };
+342
View File
@@ -0,0 +1,342 @@
#include "PrecompiledHeader.h"
#include <atomic>
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "common/Timer.h"
#include "imgui.h"
// Has to come before Gif.h
#include "MemoryTypes.h"
#include "Frontend/ImGuiManager.h"
#include "Frontend/GameList.h"
#include "Gif.h"
#include "Gif_Unit.h"
#include "GSDumpReplayer.h"
#include "GS/GSLzma.h"
#include "GS.h"
#include "Host.h"
#include "R3000A.h"
#include "R5900.h"
#include "VMManager.h"
#include "VUmicro.h"
static void GSDumpReplayerCpuReserve();
static void GSDumpReplayerCpuShutdown();
static void GSDumpReplayerCpuReset();
static void GSDumpReplayerCpuStep();
static void GSDumpReplayerCpuExecute();
static void GSDumpReplayerCpuCheckExecutionState();
static void GSDumpReplayerCpuThrowException(const BaseException& ex);
static void GSDumpReplayerCpuThrowCpuException(const BaseR5900Exception& ex);
static void GSDumpReplayerCpuClear(u32 addr, u32 size);
static uint GSDumpReplayerCpuGetCacheReserve();
static void GSDumpReplayerCpuSetCacheReserve(uint reserveInMegs);
static std::unique_ptr<GSDumpFile> s_dump_file;
static u32 s_current_packet = 0;
static u32 s_dump_frame_number = 0;
static bool s_dump_running = false;
static bool s_needs_state_loaded = false;
static u64 s_frame_ticks = 0;
static u64 s_next_frame_time = 0;
R5900cpu GSDumpReplayerCpu = {
GSDumpReplayerCpuReserve,
GSDumpReplayerCpuShutdown,
GSDumpReplayerCpuReset,
GSDumpReplayerCpuStep,
GSDumpReplayerCpuExecute,
GSDumpReplayerCpuCheckExecutionState,
GSDumpReplayerCpuThrowException,
GSDumpReplayerCpuThrowCpuException,
GSDumpReplayerCpuClear,
GSDumpReplayerCpuGetCacheReserve,
GSDumpReplayerCpuSetCacheReserve};
static InterpVU0 gsDumpVU0;
static InterpVU1 gsDumpVU1;
bool GSDumpReplayer::IsReplayingDump()
{
return static_cast<bool>(s_dump_file);
}
bool GSDumpReplayer::Initialize(const char* filename)
{
Common::Timer timer;
Console.WriteLn("(GSDumpReplayer) Reading file...");
s_dump_file = GSDumpFile::OpenGSDump(filename);
if (!s_dump_file || !s_dump_file->ReadFile())
{
Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to open or read '%s'.", filename);
s_dump_file.reset();
return false;
}
Console.WriteLn("(GSDumpReplayer) Read file in %.2f ms.", timer.GetTimeMilliseconds());
// We replace all CPUs.
Cpu = &GSDumpReplayerCpu;
psxCpu = &psxInt;
CpuVU0 = &gsDumpVU0;
CpuVU1 = &gsDumpVU1;
return true;
}
void GSDumpReplayer::Reset()
{
GSDumpReplayerCpuReset();
}
void GSDumpReplayer::Shutdown()
{
Console.WriteLn("(GSDumpReplayer) Shutting down.");
Cpu = nullptr;
psxCpu = nullptr;
CpuVU0 = nullptr;
CpuVU1 = nullptr;
s_dump_file.reset();
}
std::string GSDumpReplayer::GetDumpSerial()
{
std::string ret;
if (!s_dump_file->GetSerial().empty())
{
ret = s_dump_file->GetSerial();
}
else if (s_dump_file->GetCRC() != 0)
{
// old dump files don't have serials, but we have the crc...
// so, let's try searching the game list for a crc match.
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryByCRC(s_dump_file->GetCRC());
if (entry)
ret = entry->serial;
}
return ret;
}
u32 GSDumpReplayer::GetDumpCRC()
{
return s_dump_file->GetCRC();
}
void GSDumpReplayerCpuReserve()
{
}
void GSDumpReplayerCpuShutdown()
{
}
void GSDumpReplayerCpuReset()
{
s_needs_state_loaded = true;
s_current_packet = 0;
s_dump_frame_number = 0;
}
static void GSDumpReplayerLoadInitialState()
{
// reset GS registers to initial dump values
std::memcpy(PS2MEM_GS, s_dump_file->GetRegsData().data(),
std::min(Ps2MemSize::GSregs, static_cast<u32>(s_dump_file->GetRegsData().size())));
// load GS state
freezeData fd = {static_cast<int>(s_dump_file->GetStateData().size()),
const_cast<u8*>(s_dump_file->GetStateData().data())};
MTGS_FreezeData mfd = {&fd, 0};
GetMTGS().Freeze(FreezeAction::Load, mfd);
if (mfd.retval != 0)
Host::ReportFormattedErrorAsync("GSDumpReplayer", "Failed to load GS state.");
}
static void GSDumpReplayerSendPacketToMTGS(GIF_PATH path, const u8* data, u32 length)
{
pxAssert((length % 16) == 0);
Gif_Path& gifPath = gifUnit.gifPath[path];
gifPath.CopyGSPacketData(const_cast<u8*>(data), length);
GS_Packet gsPack;
gsPack.offset = gifPath.curOffset;
gsPack.size = length;
gifPath.curOffset += length;
Gif_AddCompletedGSPacket(gsPack, path);
}
static void GSDumpReplayerUpdateFrameLimit()
{
constexpr u32 default_frame_limit = 60;
const u32 frame_limit = static_cast<u32>(default_frame_limit * EmuConfig.GS.LimitScalar);
if (frame_limit > 0)
s_frame_ticks = (GetTickFrequency() + (frame_limit / 2)) / frame_limit;
else
s_frame_ticks = 0;
}
static void GSDumpReplayerFrameLimit()
{
if (s_frame_ticks == 0)
return;
// Frame limiter
u64 now = GetCPUTicks();
const s64 ms = GetTickFrequency() / 1000;
const s64 sleep = s_next_frame_time - now - ms;
if (sleep > ms)
Threading::Sleep(sleep / ms);
while ((now = GetCPUTicks()) < s_next_frame_time)
ShortSpin();
s_next_frame_time = std::max(now, s_next_frame_time + s_frame_ticks);
}
void GSDumpReplayerCpuStep()
{
if (s_needs_state_loaded)
{
GSDumpReplayerLoadInitialState();
s_needs_state_loaded = false;
}
const GSDumpFile::GSData& packet = s_dump_file->GetPackets()[s_current_packet];
s_current_packet = (s_current_packet + 1) % static_cast<u32>(s_dump_file->GetPackets().size());
if (s_current_packet == 0)
s_dump_frame_number = 0;
switch (packet.id)
{
case GSDumpTypes::GSType::Transfer:
{
switch (packet.path)
{
case GSDumpTypes::GSTransferPath::Path1Old:
{
std::unique_ptr<u8[]> data(new u8[16384]);
const s32 addr = 16384 - packet.length;
std::memcpy(data.get(), packet.data + addr, packet.length);
GSDumpReplayerSendPacketToMTGS(GIF_PATH_1, data.get(), packet.length);
}
break;
case GSDumpTypes::GSTransferPath::Path1New:
case GSDumpTypes::GSTransferPath::Path2:
case GSDumpTypes::GSTransferPath::Path3:
{
GSDumpReplayerSendPacketToMTGS(static_cast<GIF_PATH>(static_cast<u8>(packet.path) - 1),
packet.data, packet.length);
}
break;
default:
break;
}
break;
}
case GSDumpTypes::GSType::VSync:
{
s_dump_frame_number++;
GSDumpReplayerCpuCheckExecutionState();
GSDumpReplayerUpdateFrameLimit();
GSDumpReplayerFrameLimit();
GetMTGS().PostVsyncStart(false);
VMManager::Internal::VSyncOnCPUThread();
}
break;
case GSDumpTypes::GSType::ReadFIFO2:
{
std::unique_ptr<char[]> arr(new char[*((int*)packet.data)]);
GSreadFIFO2((u8*)arr.get(), *((int*)packet.data));
}
break;
case GSDumpTypes::GSType::Registers:
{
std::memcpy(PS2MEM_GS, packet.data, std::min<s32>(packet.length, Ps2MemSize::GSregs));
}
break;
}
}
void GSDumpReplayerCpuExecute()
{
s_dump_running = true;
s_next_frame_time = GetCPUTicks();
while (s_dump_running)
{
GSDumpReplayerCpuStep();
}
}
void GSDumpReplayerCpuCheckExecutionState()
{
if (VMManager::Internal::IsExecutionInterrupted())
s_dump_running = false;
}
void GSDumpReplayerCpuThrowException(const BaseException& ex)
{
}
void GSDumpReplayerCpuThrowCpuException(const BaseR5900Exception& ex)
{
}
void GSDumpReplayerCpuClear(u32 addr, u32 size)
{
}
uint GSDumpReplayerCpuGetCacheReserve()
{
return 0;
}
void GSDumpReplayerCpuSetCacheReserve(uint reserveInMegs)
{
}
void GSDumpReplayer::RenderUI()
{
const float scale = ImGuiManager::GetGlobalScale();
const float shadow_offset = std::ceil(1.0f * scale);
const float margin = std::ceil(10.0f * scale);
const float spacing = std::ceil(5.0f * scale);
float position_y = margin;
ImDrawList* dl = ImGui::GetBackgroundDrawList();
ImFont* font = ImGuiManager::GetFixedFont();
FastFormatAscii text;
ImVec2 text_size;
#define DRAW_LINE(font, text, color) \
do \
{ \
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
dl->AddText(font, font->FontSize, ImVec2(margin + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100), (text)); \
dl->AddText(font, font->FontSize, ImVec2(margin, position_y), color, (text)); \
position_y += text_size.y + spacing; \
} while (0)
text.Write("Dump Frame: %u", s_dump_frame_number);
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
text.Clear();
text.Write("Packet Number: %u/%u", s_current_packet, static_cast<u32>(s_dump_file->GetPackets().size()));
DRAW_LINE(font, text.c_str(), IM_COL32(255, 255, 255, 255));
#undef DRAW_LINE
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
namespace GSDumpReplayer
{
bool IsReplayingDump();
bool Initialize(const char* filename);
void Reset();
void Shutdown();
std::string GetDumpSerial();
u32 GetDumpCRC();
void RenderUI();
}
+220 -11
View File
@@ -16,8 +16,8 @@
#include "PrecompiledHeader.h"
#include "GameDatabase.h"
#include "Config.h"
#include "Host.h"
#include "Patch.h"
#include "common/FileSystem.h"
#include "common/Path.h"
@@ -31,6 +31,20 @@
#include "fmt/ranges.h"
#include <fstream>
#include <mutex>
#include <optional>
namespace GameDatabaseSchema
{
static const char* getHWFixName(GSHWFixId id);
static std::optional<GSHWFixId> parseHWFixName(const std::string_view& name);
static bool isUserHackHWFix(GSHWFixId id);
} // namespace GameDatabaseSchema
namespace GameDatabase
{
static void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node);
static void initDatabase();
} // namespace GameDatabase
static constexpr char GAMEDB_YAML_FILE_NAME[] = "GameIndex.yaml";
@@ -85,7 +99,7 @@ const char* GameDatabaseSchema::GameEntry::compatAsString() const
}
}
void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node)
void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node)
{
GameDatabaseSchema::GameEntry gameEntry;
if (node.has_child("name"))
@@ -195,6 +209,25 @@ void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node
}
}
if (node.has_child("gsHWFixes"))
{
for (const ryml::NodeRef& n : node["gsHWFixes"].children())
{
const std::string_view id_name(n.key().data(), n.key().size());
std::optional<GameDatabaseSchema::GSHWFixId> id = GameDatabaseSchema::parseHWFixName(id_name);
std::optional<s32> value = n.has_val() ? StringUtil::FromChars<s32>(std::string_view(n.val().data(), n.val().size())) : 1;
if (!id.has_value() || !value.has_value())
{
Console.Error("[GameDB] Invalid GS HW Fix: '%*s' specified for serial '%*s'. Dropping!",
static_cast<int>(id_name.size()), id_name.data(),
static_cast<int>(serial.size()), serial.data());
continue;
}
gameEntry.gsHWFixes.emplace_back(id.value(), value.value());
}
}
// Memory Card Filters - Store as a vector to allow flexibility in the future
// - currently they are used as a '\n' delimited string in the app
if (node.has_child("memcardFilters") && node["memcardFilters"].has_children())
@@ -231,16 +264,194 @@ void parseAndInsert(const std::string_view& serial, const c4::yml::NodeRef& node
s_game_db.emplace(std::move(serial), std::move(gameEntry));
}
static std::ifstream getFileStream(std::string path)
static const char* s_gs_hw_fix_names[] = {
"autoFlush",
"conservativeFramebuffer",
"cpuFramebufferConversion",
"disableDepthSupport",
"wrapGSMem",
"preloadFrameData",
"fastTextureInvalidation",
"textureInsideRT",
"alignSprite",
"mergeSprite",
"wildArmsHack",
"pointListPalette",
"mipmap",
"trilinearFiltering",
"skipDrawStart",
"skipDrawEnd",
"halfBottomOverride",
"halfPixelOffset",
"roundSprite",
"texturePreloading",
};
static_assert(std::size(s_gs_hw_fix_names) == static_cast<u32>(GameDatabaseSchema::GSHWFixId::Count), "HW fix name lookup is correct size");
const char* GameDatabaseSchema::getHWFixName(GSHWFixId id)
{
#ifdef _WIN32
return std::ifstream(StringUtil::UTF8StringToWideString(path));
#else
return std::ifstream(path.c_str());
#endif
return s_gs_hw_fix_names[static_cast<u32>(id)];
}
static void initDatabase()
static std::optional<GameDatabaseSchema::GSHWFixId> GameDatabaseSchema::parseHWFixName(const std::string_view& name)
{
for (u32 i = 0; i < std::size(s_gs_hw_fix_names); i++)
{
if (name.compare(s_gs_hw_fix_names[i]) == 0)
return static_cast<GameDatabaseSchema::GSHWFixId>(i);
}
return std::nullopt;
}
bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
{
switch (id)
{
case GSHWFixId::Mipmap:
case GSHWFixId::TexturePreloading:
case GSHWFixId::ConservativeFramebuffer:
case GSHWFixId::PointListPalette:
return false;
#ifdef PCSX2_CORE
// Trifiltering isn't a hack in Qt.
case GSHWFixId::TrilinearFiltering:
return false;
#endif
default:
return true;
}
}
u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions& config) const
{
// Only apply GS HW fixes if the user hasn't manually enabled HW fixes.
const bool apply_auto_fixes = !config.ManualUserHacks;
if (!apply_auto_fixes)
Console.Warning("[GameDB] Manual GS hardware renderer fixes are enabled, not using automatic hardware renderer fixes from GameDB.");
u32 num_applied_fixes = 0;
for (const auto& [id, value] : gsHWFixes)
{
if (isUserHackHWFix(id) && !apply_auto_fixes)
{
PatchesCon->Warning("[GameDB] Skipping GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
continue;
}
switch (id)
{
case GSHWFixId::AutoFlush:
config.UserHacks_AutoFlush = (value > 0);
break;
case GSHWFixId::ConservativeFramebuffer:
config.ConservativeFramebuffer = (value > 0);
break;
case GSHWFixId::CPUFramebufferConversion:
config.UserHacks_CPUFBConversion = (value > 0);
break;
case GSHWFixId::DisableDepthSupport:
config.UserHacks_DisableDepthSupport = (value > 0);
break;
case GSHWFixId::WrapGSMem:
config.WrapGSMem = (value > 0);
break;
case GSHWFixId::PreloadFrameData:
config.PreloadFrameWithGSData = (value > 0);
break;
case GSHWFixId::FastTextureInvalidation:
config.UserHacks_DisablePartialInvalidation = (value > 0);
break;
case GSHWFixId::TextureInsideRT:
config.UserHacks_TextureInsideRt = (value > 0);
break;
case GSHWFixId::AlignSprite:
config.UserHacks_AlignSpriteX = (value > 0);
break;
case GSHWFixId::MergeSprite:
config.UserHacks_MergePPSprite = (value > 0);
break;
case GSHWFixId::WildArmsHack:
config.UserHacks_WildHack = (value > 0);
break;
case GSHWFixId::PointListPalette:
config.PointListPalette = (value > 0);
break;
case GSHWFixId::Mipmap:
{
if (value >= 0 && value <= static_cast<int>(HWMipmapLevel::Full))
{
if (config.HWMipmap == HWMipmapLevel::Automatic)
config.HWMipmap = static_cast<HWMipmapLevel>(value);
else if (config.HWMipmap == HWMipmapLevel::Off)
Console.Warning("[GameDB] Game requires mipmapping but it has been force disabled.");
}
}
break;
case GSHWFixId::TrilinearFiltering:
{
if (value >= 0 && value <= static_cast<int>(TriFiltering::Forced))
config.UserHacks_TriFilter = static_cast<TriFiltering>(value);
}
break;
case GSHWFixId::SkipDrawStart:
config.SkipDrawStart = value;
break;
case GSHWFixId::SkipDrawEnd:
config.SkipDrawEnd = value;
break;
case GSHWFixId::HalfBottomOverride:
config.UserHacks_HalfBottomOverride = value;
break;
case GSHWFixId::HalfPixelOffset:
config.UserHacks_HalfPixelOffset = value;
break;
case GSHWFixId::RoundSprite:
config.UserHacks_RoundSprite = value;
break;
case GSHWFixId::TexturePreloading:
{
if (value >= 0 && value <= static_cast<int>(TexturePreloadingLevel::Full))
config.TexturePreloading = std::min(config.TexturePreloading, static_cast<TexturePreloadingLevel>(value));
}
break;
default:
break;
}
PatchesCon->WriteLn("[GameDB] Enabled GS Hardware Fix: %s to [mode=%d]", getHWFixName(id), value);
num_applied_fixes++;
}
// fixup skipdraw range just in case the db has a bad range (but the linter should catch this)
config.SkipDrawEnd = std::max(config.SkipDrawStart, config.SkipDrawEnd);
return num_applied_fixes;
}
void GameDatabase::initDatabase()
{
ryml::Callbacks rymlCallbacks = ryml::get_callbacks();
rymlCallbacks.m_error = [](const char* msg, size_t msg_len, ryml::Location loc, void*) {
@@ -291,8 +502,6 @@ static void initDatabase()
ryml::reset_callbacks();
}
void GameDatabase::ensureLoaded()
{
std::call_once(s_load_once_flag, []() {
+38 -3
View File
@@ -15,16 +15,18 @@
#pragma once
#include "Config.h"
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <string>
enum GamefixId;
enum SpeedhackId;
class GameDatabaseSchema
namespace GameDatabaseSchema
{
public:
enum class Compatibility
{
Unknown = 0,
@@ -54,6 +56,35 @@ public:
Full
};
enum class GSHWFixId : u32
{
// boolean settings
AutoFlush,
ConservativeFramebuffer,
CPUFramebufferConversion,
DisableDepthSupport,
WrapGSMem,
PreloadFrameData,
FastTextureInvalidation,
TextureInsideRT,
AlignSprite,
MergeSprite,
WildArmsHack,
PointListPalette,
// integer settings
Mipmap,
TrilinearFiltering,
SkipDrawStart,
SkipDrawEnd,
HalfBottomOverride,
HalfPixelOffset,
RoundSprite,
TexturePreloading,
Count
};
using Patch = std::vector<std::string>;
struct GameEntry
@@ -67,6 +98,7 @@ public:
ClampMode vuClampMode = ClampMode::Undefined;
std::vector<GamefixId> gameFixes;
std::vector<std::pair<SpeedhackId, int>> speedHacks;
std::vector<std::pair<GSHWFixId, s32>> gsHWFixes;
std::vector<std::string> memcardFilters;
std::unordered_map<std::string, Patch> patches;
@@ -74,6 +106,9 @@ public:
std::string memcardFiltersAsString() const;
const Patch* findPatch(const std::string_view& crc) const;
const char* compatAsString() const;
/// Applies GS hardware fixes to an existing config. Returns the number of applied fixes.
u32 applyGSHardwareFixes(Pcsx2Config::GSOptions& config) const;
};
};
+23 -7
View File
@@ -60,16 +60,16 @@ struct Gif_Tag
bool hasAD; // Has an A+D Write
bool isValid; // Tag is valid
Gif_Tag() { Reset(); }
Gif_Tag(u8* pMem, bool analyze = false)
__ri Gif_Tag() { Reset(); }
__ri Gif_Tag(u8* pMem, bool analyze = false)
{
setTag(pMem, analyze);
}
void Reset() { memzero(*this); }
u8 curReg() { return regs[nRegIdx & 0xf]; }
__ri void Reset() { memzero(*this); }
__ri u8 curReg() { return regs[nRegIdx & 0xf]; }
void packedStep()
__ri void packedStep()
{
if (nLoop > 0)
{
@@ -82,7 +82,7 @@ struct Gif_Tag
}
}
void setTag(u8* pMem, bool analyze = false)
__ri void setTag(u8* pMem, bool analyze = false)
{
tag = *(HW_Gif_Tag*)pMem;
nLoop = tag.NLOOP;
@@ -115,8 +115,23 @@ struct Gif_Tag
}
}
void analyzeTag()
__ri void analyzeTag()
{
#ifdef _M_X86
// zero out bits for registers which shouldn't be tested
__m128i vregs = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(tag.REGS));
vregs = _mm_and_si128(vregs, _mm_srli_epi64(_mm_set1_epi32(0xFFFFFFFFu), (64 - nRegs * 4)));
// get upper nibbles, interleave with lower nibbles, clear upper bits from low nibbles
vregs = _mm_and_si128(_mm_unpacklo_epi8(vregs, _mm_srli_epi32(vregs, 4)), _mm_set1_epi8(0x0F));
// compare with GIF_REG_A_D, set hasAD if any lanes passed
hasAD = (_mm_movemask_epi8(_mm_cmpeq_epi8(vregs, _mm_set1_epi8(GIF_REG_A_D))) != 0);
// write out unpacked registers
_mm_storeu_si128(reinterpret_cast<__m128i*>(regs), vregs);
#else
// Reference C implementation.
hasAD = false;
u32 t = tag.REGS[0];
u32 i = 0;
@@ -135,6 +150,7 @@ struct Gif_Tag
hasAD |= (regs[i] == GIF_REG_A_D);
t >>= 4;
}
#endif
}
};
+6 -5
View File
@@ -146,7 +146,7 @@ void VU_Thread::ExecuteRingBuffer()
s32 addr = Read();
vifRegs.top = Read();
vifRegs.itop = Read();
vuFBRST = Read();
if (addr != -1)
vuRegs.VI[REG_TPC].UL = addr & 0x7FF;
vuCPU->SetStartPC(vuRegs.VI[REG_TPC].UL << 3);
@@ -406,13 +406,13 @@ void VU_Thread::Get_MTVUChanges()
{
mtvuInterrupts.fetch_and(~InterruptFlagVUEBit, std::memory_order_relaxed);
VU0.VI[REG_VPU_STAT].UL &= ~0x0100;
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
//DevCon.Warning("E-Bit registered %x", VU0.VI[REG_VPU_STAT].UL);
}
if (interrupts & InterruptFlagVUTBit)
{
mtvuInterrupts.fetch_and(~InterruptFlagVUTBit, std::memory_order_relaxed);
VU0.VI[REG_VPU_STAT].UL &= ~0x0100;
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
VU0.VI[REG_VPU_STAT].UL |= 0x0400;
//DevCon.Warning("T-Bit registered %x", VU0.VI[REG_VPU_STAT].UL);
hwIntcIrq(7);
@@ -445,15 +445,16 @@ void VU_Thread::WaitVU()
}
}
void VU_Thread::ExecuteVU(u32 vu_addr, u32 vif_top, u32 vif_itop)
void VU_Thread::ExecuteVU(u32 vu_addr, u32 vif_top, u32 vif_itop, u32 fbrst)
{
MTVU_LOG("MTVU - ExecuteVU!");
Get_MTVUChanges(); // Clear any pending interrupts
ReserveSpace(4);
ReserveSpace(5);
Write(MTVU_VU_EXECUTE);
Write(vu_addr);
Write(vif_top);
Write(vif_itop);
Write(fbrst);
CommitWritePos();
gifUnit.TransferGSPacketData(GIF_TRANS_MTVU, NULL, 0);
KickStart();
+2 -1
View File
@@ -47,6 +47,7 @@ public:
Semaphore semaXGkick;
std::atomic<unsigned int> vuCycles[4]; // Used for VU cycle stealing hack
u32 vuCycleIdx; // Used for VU cycle stealing hack
u32 vuFBRST;
enum InterruptFlag {
InterruptFlagFinish = 1 << 0,
@@ -76,7 +77,7 @@ public:
void Get_MTVUChanges();
void ExecuteVU(u32 vu_addr, u32 vif_top, u32 vif_itop);
void ExecuteVU(u32 vu_addr, u32 vif_top, u32 vif_itop, u32 fbrst);
void VifUnpack(vifStruct& _vif, VIFregisters& _vifRegs, u8* data, u32 size);
+11 -1
View File
@@ -49,6 +49,10 @@ BIOS
#include "common/PageFaultSource.h"
#ifdef PCSX2_CORE
#include "GSDumpReplayer.h"
#endif
#ifdef ENABLECACHE
#include "Cache.h"
#endif
@@ -850,7 +854,13 @@ void eeMemoryReserve::Reset()
vtlb_VMap(0x00000000,0x00000000,0x20000000);
vtlb_VMapUnmap(0x20000000,0x60000000);
if (!LoadBIOS())
#ifdef PCSX2_CORE
const bool needs_bios = !GSDumpReplayer::IsReplayingDump();
#else
constexpr bool needs_bios = true;
#endif
if (needs_bios && !LoadBIOS())
pxFailRel("Failed to load BIOS");
}
+37 -10
View File
@@ -65,9 +65,11 @@ void TraceLogFilters::LoadSave(SettingsWrapper& wrap)
}
const char* const tbl_SpeedhackNames[] =
{
"mvuFlag",
"InstantVU1"};
{
"mvuFlag",
"InstantVU1",
"MTVU"
};
const char* EnumToString(SpeedhackId id)
{
@@ -85,6 +87,9 @@ void Pcsx2Config::SpeedhackOptions::Set(SpeedhackId id, bool enabled)
case Speedhack_InstantVU1:
vu1Instant = enabled;
break;
case Speedhack_MTVU:
vuThread = enabled;
break;
jNO_DEFAULT;
}
}
@@ -308,8 +313,9 @@ Pcsx2Config::GSOptions::GSOptions()
WrapGSMem = false;
Mipmap = true;
AA1 = true;
PointListPalette = false;
UserHacks = false;
ManualUserHacks = false;
UserHacks_AlignSpriteX = false;
UserHacks_AutoFlush = false;
UserHacks_CPUFBConversion = false;
@@ -380,8 +386,8 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(SWExtraThreads) &&
OpEqu(SWExtraThreadsHeight) &&
OpEqu(TVShader) &&
OpEqu(SkipDraw) &&
OpEqu(SkipDrawOffset) &&
OpEqu(SkipDrawEnd) &&
OpEqu(SkipDrawStart) &&
OpEqu(UserHacks_HalfBottomOverride) &&
OpEqu(UserHacks_HalfPixelOffset) &&
@@ -508,7 +514,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");
GSSettingBoolEx(Mipmap, "mipmap");
GSSettingBoolEx(AA1, "aa1");
GSSettingBoolEx(UserHacks, "UserHacks");
GSSettingBoolEx(ManualUserHacks, "UserHacks");
GSSettingBoolEx(UserHacks_AlignSpriteX, "UserHacks_align_sprite_X");
GSSettingBoolEx(UserHacks_AutoFlush, "UserHacks_AutoFlush");
GSSettingBoolEx(UserHacks_CPUFBConversion, "UserHacks_CPU_FB_Conversion");
@@ -550,8 +556,9 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingIntEx(SWExtraThreads, "extrathreads");
GSSettingIntEx(SWExtraThreadsHeight, "extrathreads_height");
GSSettingIntEx(TVShader, "TVShader");
GSSettingIntEx(SkipDraw, "UserHacks_SkipDraw");
GSSettingIntEx(SkipDrawOffset, "UserHacks_SkipDraw_Offset");
GSSettingIntEx(SkipDrawStart, "UserHacks_SkipDraw_Offset");
GSSettingIntEx(SkipDrawEnd, "UserHacks_SkipDraw");
SkipDrawEnd = std::max(SkipDrawStart, SkipDrawEnd);
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
@@ -583,22 +590,27 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
void Pcsx2Config::GSOptions::MaskUserHacks()
{
if (UserHacks)
if (ManualUserHacks)
return;
UserHacks_AlignSpriteX = false;
UserHacks_MergePPSprite = false;
UserHacks_WildHack = false;
UserHacks_DisableSafeFeatures = false;
UserHacks_HalfBottomOverride = -1;
UserHacks_HalfPixelOffset = 0;
UserHacks_RoundSprite = 0;
UserHacks_AutoFlush = false;
PreloadFrameWithGSData = false;
WrapGSMem = false;
UserHacks_DisablePartialInvalidation = false;
UserHacks_DisableDepthSupport = false;
UserHacks_CPUFBConversion = false;
UserHacks_TextureInsideRt = false;
UserHacks_TCOffsetX = 0;
UserHacks_TCOffsetY = 0;
SkipDrawStart = 0;
SkipDrawEnd = 0;
// in wx, we put trilinear filtering behind user hacks, but not in qt.
#ifndef PCSX2_CORE
@@ -606,6 +618,17 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
#endif
}
void Pcsx2Config::GSOptions::MaskUpscalingHacks()
{
if (UpscaleMultiplier == 1 || ManualUserHacks)
return;
UserHacks_AlignSpriteX = false;
UserHacks_MergePPSprite = false;
UserHacks_HalfPixelOffset = 0;
UserHacks_RoundSprite = 0;
}
bool Pcsx2Config::GSOptions::UseHardwareRenderer() const
{
return (Renderer == GSRendererType::DX11 || Renderer == GSRendererType::OGL || Renderer == GSRendererType::VK);
@@ -680,23 +703,27 @@ void Pcsx2Config::DEV9Options::LoadSave(SettingsWrapper& wrap)
SettingsWrapEntry(InterceptDHCP);
std::string ps2IPStr = "0.0.0.0";
std::string maskStr = "0.0.0.0";
std::string gatewayStr = "0.0.0.0";
std::string dns1Str = "0.0.0.0";
std::string dns2Str = "0.0.0.0";
if (wrap.IsSaving())
{
ps2IPStr = SaveIPHelper(PS2IP);
maskStr = SaveIPHelper(Mask);
gatewayStr = SaveIPHelper(Gateway);
dns1Str = SaveIPHelper(DNS1);
dns2Str = SaveIPHelper(DNS2);
}
SettingsWrapEntryEx(ps2IPStr, "PS2IP");
SettingsWrapEntryEx(maskStr, "Mask");
SettingsWrapEntryEx(gatewayStr, "Gateway");
SettingsWrapEntryEx(dns1Str, "DNS1");
SettingsWrapEntryEx(dns2Str, "DNS2");
if (wrap.IsLoading())
{
LoadIPHelper(PS2IP, ps2IPStr);
LoadIPHelper(Mask, maskStr);
LoadIPHelper(Gateway, gatewayStr);
LoadIPHelper(DNS1, dns1Str);
LoadIPHelper(DNS1, dns1Str);
+5 -2
View File
@@ -37,9 +37,12 @@ static void TestClearVUs(u32 madr, u32 qwc, bool isWrite)
//Catch up VU1 too
CpuVU1->ExecuteBlock(0);
}
if ((madr >= 0x11008000) && (VU0.VI[REG_VPU_STAT].UL & 0x100) && !THREAD_VU1)
if ((madr >= 0x11008000) && (VU0.VI[REG_VPU_STAT].UL & 0x100) && (!THREAD_VU1 || !isWrite))
{
CpuVU1->Execute(vu1RunCycles);
if (THREAD_VU1)
vu1Thread.WaitVU();
else
CpuVU1->Execute(vu1RunCycles);
cpuRegs.cycle = VU1.cycle;
//Catch up VU0 too
CpuVU0->ExecuteBlock(0);
+10
View File
@@ -27,6 +27,11 @@
#include "common/MemsetFast.inl"
#include "common/Perf.h"
#ifdef PCSX2_CORE
#include "GSDumpReplayer.h"
extern R5900cpu GSDumpReplayerCpu;
#endif
// --------------------------------------------------------------------------------------
// RecompiledCodeReserve (implementations)
@@ -576,6 +581,11 @@ void SysCpuProviderPack::ApplyConfig() const
if( EmuConfig.Cpu.Recompiler.EnableVU1 )
CpuVU1 = (BaseVUmicroCPU*)CpuProviders->microVU1;
#ifdef PCSX2_CORE
if (GSDumpReplayer::IsReplayingDump())
Cpu = &GSDumpReplayerCpu;
#endif
}
// Resets all PS2 cpu execution caches, which does not affect that actual PS2 state/condition.
+124 -48
View File
@@ -35,6 +35,7 @@
#include "Elfheader.h"
#include "FW.h"
#include "GS.h"
#include "GSDumpReplayer.h"
#include "HostDisplay.h"
#include "HostSettings.h"
#include "IopBios.h"
@@ -74,6 +75,9 @@ namespace VMManager
static void CheckForSPU2ConfigChanges(const Pcsx2Config& old_config);
static void CheckForDEV9ConfigChanges(const Pcsx2Config& old_config);
static void CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config);
static bool AutoDetectSource(const std::string& filename);
static bool ApplyBootParameters(const VMBootParameters& params);
static void UpdateRunningGame(bool force);
static std::string GetCurrentSaveStateFileName(s32 slot);
@@ -218,6 +222,14 @@ void VMManager::LoadSettings()
InputManager::ReloadSources(*si);
InputManager::ReloadBindings(*si);
// Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled).
EmuConfig.GS.MaskUserHacks();
EmuConfig.GS.MaskUpscalingHacks();
// Force MTVU off when playing back GS dumps, it doesn't get used.
if (GSDumpReplayer::IsReplayingDump())
EmuConfig.Speedhacks.vuThread = false;
if (HasValidVM())
ApplyGameFixes();
}
@@ -295,6 +307,8 @@ void VMManager::ApplyGameFixes()
if (id == Fix_GoemonTlbMiss && true)
vtlb_Alloc_Ppmap();
}
s_active_game_fixes += game->applyGSHardwareFixes(EmuConfig.GS);
}
std::string VMManager::GetGameSettingsPath(u32 game_crc)
@@ -448,11 +462,20 @@ void VMManager::UpdateRunningGame(bool force)
// we have the CRC but we're still at the bios and the settings are changed
// (e.g. the user presses TAB to speed up emulation), we don't want to apply the
// settings as if the game is already running (title, loadeding patches, etc).
bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted));
const u32 new_crc = ingame ? ElfCRC : 0;
const std::string crc_string(StringUtil::StdStringFromFormat("%08X", new_crc));
u32 new_crc;
std::string new_serial;
if (!GSDumpReplayer::IsReplayingDump())
{
const bool ingame = (ElfCRC && (g_GameLoading || g_GameStarted));
new_crc = ingame ? ElfCRC : 0;
new_serial = ingame ? SysGetDiscID().ToStdString() : SysGetBiosDiscID().ToStdString();
}
else
{
new_crc = GSDumpReplayer::GetDumpCRC();
new_serial = GSDumpReplayer::GetDumpSerial();
}
std::string new_serial(ingame ? SysGetDiscID().ToStdString() : SysGetBiosDiscID().ToStdString());
if (!force && s_game_crc == new_crc && s_game_serial == new_serial)
return;
@@ -482,7 +505,7 @@ void VMManager::UpdateRunningGame(bool force)
ApplySettings();
ForgetLoadedPatches();
LoadPatches(crc_string, true, false);
LoadPatches(StringUtil::StdStringFromFormat("%08X", new_crc), true, false);
GetMTGS().SendGameCRC(new_crc);
Host::OnGameChanged(s_disc_path, s_game_serial, s_game_name, s_game_crc);
@@ -499,30 +522,86 @@ static LimiterModeType GetInitialLimiterMode()
return EmuConfig.GS.FrameLimitEnable ? LimiterModeType::Nominal : LimiterModeType::Unlimited;
}
static void ApplyBootParameters(const VMBootParameters& params)
bool VMManager::AutoDetectSource(const std::string& filename)
{
if (!filename.empty())
{
if (!FileSystem::FileExists(filename.c_str()))
{
Host::ReportFormattedErrorAsync("Error", "Requested filename '%s' does not exist.", filename.c_str());
return false;
}
const std::string display_name(FileSystem::GetDisplayNameFromPath(filename));
if (IsGSDumpFileName(display_name))
{
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
return GSDumpReplayer::Initialize(filename.c_str());
}
else if (IsElfFileName(display_name))
{
// alternative way of booting an elf, change the elf override, and use no disc.
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
s_elf_override = filename;
return true;
}
else
{
// TODO: Maybe we should check if it's a valid iso here...
CDVDsys_SetFile(CDVD_SourceType::Iso, filename);
CDVDsys_ChangeSource(CDVD_SourceType::Iso);
s_disc_path = filename;
return true;
}
}
else
{
// make sure we're not fast booting when we have no filename
CDVDsys_ChangeSource(CDVD_SourceType::NoDisc);
EmuConfig.UseBOOT2Injection = false;
return true;
}
}
bool VMManager::ApplyBootParameters(const VMBootParameters& params)
{
const bool default_fast_boot = Host::GetBoolSettingValue("EmuCore", "EnableFastBoot", true);
EmuConfig.UseBOOT2Injection =
(params.source_type != CDVD_SourceType::NoDisc && params.fast_boot.value_or(default_fast_boot));
EmuConfig.UseBOOT2Injection = params.fast_boot.value_or(default_fast_boot);
s_elf_override = params.elf_override;
s_disc_path.clear();
CDVDsys_SetFile(CDVD_SourceType::Iso, params.source);
CDVDsys_ChangeSource(params.source_type);
if (!params.elf_override.empty())
if (params.source_type.has_value())
{
Hle_SetElfPath(params.elf_override.c_str());
s_elf_override = std::move(params.elf_override);
if (params.source_type.value() == CDVD_SourceType::Iso && !FileSystem::FileExists(params.filename.c_str()))
{
Host::ReportFormattedErrorAsync("Error", "Requested filename '%s' does not exist.", params.filename.c_str());
return false;
}
// Use specified source type.
CDVDsys_SetFile(params.source_type.value(), params.filename);
CDVDsys_ChangeSource(params.source_type.value());
}
else
{
// Automatic type detection of boot parameter based on filename.
if (!AutoDetectSource(params.filename))
return false;
}
if (!s_elf_override.empty())
{
if (!FileSystem::FileExists(s_elf_override.c_str()))
{
Host::ReportFormattedErrorAsync("Error", "Requested boot ELF '%s' does not exist.", s_elf_override.c_str());
return false;
}
Hle_SetElfPath(s_elf_override.c_str());
EmuConfig.UseBOOT2Injection = true;
}
else
{
std::string().swap(s_elf_override);
}
if (params.source_type == CDVD_SourceType::Iso)
s_disc_path = params.source;
else
s_disc_path.clear();
return true;
}
bool VMManager::Initialize(const VMBootParameters& boot_params)
@@ -538,7 +617,10 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
};
LoadSettings();
ApplyBootParameters(boot_params);
if (!ApplyBootParameters(boot_params))
return false;
EmuConfig.LimiterMode = GetInitialLimiterMode();
Console.WriteLn("Allocating memory map...");
@@ -655,17 +737,18 @@ bool VMManager::Initialize(const VMBootParameters& boot_params)
frameLimitReset();
cpuReset();
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
s_state.store(VMState::Paused);
Host::OnVMStarted();
UpdateRunningGame(true);
SetEmuThreadAffinities(true);
PerformanceMetrics::Clear();
Console.WriteLn("VM subsystems initialized in %.2f ms", init_timer.GetTimeMilliseconds());
s_state.store(VMState::Paused);
Host::OnVMStarted();
// do we want to load state?
if (!boot_params.save_state.empty())
if (!GSDumpReplayer::IsReplayingDump() && !boot_params.save_state.empty())
{
if (!DoLoadState(boot_params.save_state.c_str()))
{
@@ -686,12 +769,16 @@ void VMManager::Shutdown(bool allow_save_resume_state /* = true */)
vu1Thread.WaitVU();
GetMTGS().WaitGS();
if (allow_save_resume_state && ShouldSaveResumeState())
if (!GSDumpReplayer::IsReplayingDump() && allow_save_resume_state && ShouldSaveResumeState())
{
std::string resume_file_name(GetCurrentSaveStateFileName(-1));
if (!resume_file_name.empty() && !DoSaveState(resume_file_name.c_str(), -1))
Console.Error("Failed to save resume state");
}
else if (GSDumpReplayer::IsReplayingDump())
{
GSDumpReplayer::Shutdown();
}
{
std::unique_lock lock(s_info_mutex);
@@ -784,6 +871,9 @@ std::string VMManager::GetCurrentSaveStateFileName(s32 slot)
bool VMManager::DoLoadState(const char* filename)
{
if (GSDumpReplayer::IsReplayingDump())
return false;
try
{
Host::OnSaveStateLoading(filename);
@@ -802,6 +892,9 @@ bool VMManager::DoLoadState(const char* filename)
bool VMManager::DoSaveState(const char* filename, s32 slot_for_message)
{
if (GSDumpReplayer::IsReplayingDump())
return false;
try
{
std::unique_ptr<ArchiveEntryList> elist = std::make_unique<ArchiveEntryList>(new VmStateBuffer(L"Zippable Savestate"));
@@ -907,29 +1000,12 @@ bool VMManager::ChangeDisc(std::string path)
bool VMManager::IsElfFileName(const std::string& path)
{
const std::string::size_type pos = path.rfind('.');
if (pos == std::string::npos)
return false;
return (StringUtil::Strcasecmp(&path[pos], ".elf") == 0);
return StringUtil::EndsWithNoCase(path, ".elf");
}
void VMManager::SetBootParametersForPath(const std::string& path, VMBootParameters* params)
bool VMManager::IsGSDumpFileName(const std::string& path)
{
if (IsElfFileName(path))
{
params->elf_override = path;
params->source_type = CDVD_SourceType::NoDisc;
}
else if (!path.empty())
{
params->source_type = CDVD_SourceType::Iso;
params->source = path;
}
else
{
params->source_type = CDVD_SourceType::NoDisc;
}
return (StringUtil::EndsWithNoCase(path, ".gs") || StringUtil::EndsWithNoCase(path, ".gs.xz"));
}
void VMManager::Execute()
+6 -5
View File
@@ -39,10 +39,11 @@ enum class VMState
struct VMBootParameters
{
std::string source;
std::string save_state;
CDVD_SourceType source_type;
std::string filename;
std::string elf_override;
std::string save_state;
std::optional<CDVD_SourceType> source_type;
std::optional<bool> fast_boot;
std::optional<bool> fullscreen;
std::optional<bool> batch_mode;
@@ -135,8 +136,8 @@ namespace VMManager
/// Returns true if the specified path is an ELF.
bool IsElfFileName(const std::string& path);
/// Updates boot parameters for a given start filename. If it's an elf, it'll set elf_override, otherwise source.
void SetBootParametersForPath(const std::string& path, VMBootParameters* params);
/// Returns true if the specified path is a GS Dump.
bool IsGSDumpFileName(const std::string& path);
/// Returns the path for the game settings ini file for the specified CRC.
std::string GetGameSettingsPath(u32 game_crc);
+1
View File
@@ -30,6 +30,7 @@
#include "R5900OpcodeTables.h"
#include "VUmicro.h"
#include "Vif_Dma.h"
#include "MTVU.h"
#define _Ft_ _Rt_
#define _Fs_ _Rd_
+4 -3
View File
@@ -61,15 +61,16 @@ void __fastcall vu1ExecMicro(u32 addr)
{
if (THREAD_VU1) {
VU0.VI[REG_VPU_STAT].UL &= ~0xFF00;
// Okay this is a little bit of a hack, but with good reason.
// Most of the time with MTVU we want to pretend the VU has finished quickly as to gain the benefit from running another thread
// however with T-Bit games when the T-Bit is enabled, it needs to wait in case a T-Bit happens, so we need to set "Busy"
// We shouldn't do this all the time as it negates the extra thread and causes games like Ratchet & Clank to be no faster.
if(VU0.VI[REG_FBRST].UL & 0x800)
if (VU0.VI[REG_FBRST].UL & 0x800)
{
VU0.VI[REG_VPU_STAT].UL |= 0x0100;
}
vu1Thread.ExecuteVU(addr, vif1Regs.top, vif1Regs.itop);
vu1Thread.ExecuteVU(addr, vif1Regs.top, vif1Regs.itop, VU0.VI[REG_FBRST].UL);
return;
}
static int count = 0;
+9 -16
View File
@@ -305,6 +305,7 @@ _vifT __fi u32 vifRead32(u32 mem)
{
vifStruct& vif = MTVU_VifX;
bool wait = idx && THREAD_VU1;
switch (mem)
{
case caseVif(ROW0):
@@ -380,44 +381,36 @@ _vifT __fi bool vifWrite32(u32 mem, u32 value)
case caseVif(ROW0):
vif.MaskRow._u32[0] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteRow(vif);
vu1Thread.WriteRow(vif);
return false;
case caseVif(ROW1):
vif.MaskRow._u32[1] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteRow(vif);
vu1Thread.WriteRow(vif);
return false;
case caseVif(ROW2):
vif.MaskRow._u32[2] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteRow(vif);
vu1Thread.WriteRow(vif);
return false;
case caseVif(ROW3):
vif.MaskRow._u32[3] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteRow(vif);
vu1Thread.WriteRow(vif);
return false;
case caseVif(COL0):
vif.MaskCol._u32[0] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteCol(vif);
vu1Thread.WriteCol(vif);
return false;
case caseVif(COL1):
vif.MaskCol._u32[1] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteCol(vif);
vu1Thread.WriteCol(vif);
return false;
case caseVif(COL2):
vif.MaskCol._u32[2] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteCol(vif);
vu1Thread.WriteCol(vif);
return false;
case caseVif(COL3):
vif.MaskCol._u32[3] = value;
if (idx && THREAD_VU1)
vu1Thread.WriteCol(vif);
vu1Thread.WriteCol(vif);
return false;
}
+4 -8
View File
@@ -173,9 +173,6 @@ __fi void vif1SetupTransfer()
}
}
if (vif1ch.chcr.TTE)
{
// Transfer dma tag if tte is set
@@ -203,7 +200,7 @@ __fi void vif1SetupTransfer()
ret = VIF1transfer((u32*)&masked_tag + 2, 2, true); //Transfer Tag
//ret = VIF1transfer((u32*)ptag + 2, 2); //Transfer Tag
}
if (!ret && vif1.irqoffset.enabled)
{
vif1.inprogress &= ~1; // Better clear this so it has to do it again (Jak 1)
@@ -233,8 +230,7 @@ __fi void vif1VUFinish()
{
if (VU0.VI[REG_VPU_STAT].UL & 0x500)
{
if(THREAD_VU1)
vu1Thread.Get_MTVUChanges();
vu1Thread.Get_MTVUChanges();
CPU_INT(VIF_VU1_FINISH, 128);
return;
@@ -355,11 +351,11 @@ __fi void vif1Interrupt()
vif1.vifstalled.enabled = false;
//Mirroring change to VIF0
if (vif1.cmd)
if (vif1.cmd)
{
if (vif1.done && (vif1ch.qwc == 0)) vif1Regs.stat.VPS = VPS_WAITING;
}
else
else
{
vif1Regs.stat.VPS = VPS_IDLE;
}
+6
View File
@@ -333,6 +333,8 @@ static int loadGameSettings(Pcsx2Config& dest, const GameDatabaseSchema::GameEnt
vtlb_Alloc_Ppmap();
}
gf += game.applyGSHardwareFixes(dest.GS);
return gf;
}
@@ -404,6 +406,10 @@ static void _ApplySettings(const Pcsx2Config& src, Pcsx2Config& fixup)
fixup.GS.VsyncEnable = VsyncMode::Off;
}
// Remove any user-specified hacks in the config (we don't want stale/conflicting values when it's globally disabled).
fixup.GS.MaskUserHacks();
fixup.GS.MaskUpscalingHacks();
wxString gamePatch;
wxString gameFixes;
wxString gameCheats;
+114 -207
View File
@@ -22,8 +22,9 @@
#include "common/EmbeddedImage.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "gui/Resources/NoIcon.h"
#include "GS.h"
#include "HostDisplay.h"
#include "PathDefs.h"
@@ -31,6 +32,7 @@
#include "gui/GSFrame.h"
#include "Counters.h"
#include "PerformanceMetrics.h"
#include "GameDatabase.h"
#include <wx/mstream.h>
#include <wx/listctrl.h>
@@ -45,125 +47,35 @@
#include <wx/wfstream.h>
#include <array>
#include <functional>
#include <optional>
template <typename Output, typename Input, typename std::enable_if<sizeof(Input) == sizeof(Output), bool>::type = true>
static constexpr Output BitCast(Input input)
{
Output output;
memcpy(&output, &input, sizeof(input));
return output;
}
template <typename Output = u32>
static constexpr Output GetBits(u64 value, u32 shift, u32 numbits)
{
return static_cast<Output>((value >> shift) & ((1ull << numbits) - 1));
}
using namespace GSDumpTypes;
template <typename Output = u32>
static constexpr Output GetBits(u128 value, u32 shift, u32 numbits)
static std::optional<wxImage> GetPreviewImageFromDump(const std::string& filename)
{
u64 outval = 0;
if (shift == 0)
outval = value.lo;
else if (shift < 64)
outval = (value.lo >> shift) | (value.hi << (64 - shift));
else
outval = value.hi >> (shift - 64);
return static_cast<Output>(outval & ((1ull << numbits) - 1));
}
static constexpr const char* GetNameOneBit(u8 value, const char* zero, const char* one)
{
switch (value)
{
case 0: return zero;
case 1: return one;
default: return "UNKNOWN";
}
}
static constexpr const char* GetNameBool(bool value)
{
return value ? "True" : "False";
}
std::vector<u32> pixels;
u32 width, height;
if (!GSDumpFile::GetPreviewImageFromDump(filename.c_str(), &width, &height, &pixels))
return std::nullopt;
static constexpr const char* GetNamePRIMPRIM(u8 prim)
{
switch (prim)
// strip alpha bytes because wx is dumb and stores on a separate plane
// apparently this isn't aligned? stupidity...
const u32 pitch = width * 3;
u8* wxpixels = static_cast<u8*>(std::malloc(pitch * height));
for (u32 y = 0; y < height; y++)
{
case 0: return "Point";
case 1: return "Line";
case 2: return "Line Strip";
case 3: return "Triangle";
case 4: return "Triangle Strip";
case 5: return "Triangle Fan";
case 6: return "Sprite";
case 7: return "Invalid";
default: return "UNKNOWN";
}
}
static constexpr const char* GetNamePRIMIIP(u8 iip)
{
return GetNameOneBit(iip, "Flat Shading", "Gouraud Shading");
}
static constexpr const char* GetNamePRIMFST(u8 fst)
{
return GetNameOneBit(fst, "STQ Value", "UV Value");
}
static constexpr const char* GetNamePRIMCTXT(u8 ctxt)
{
return GetNameOneBit(ctxt, "Context 1", "Context 2");
}
static constexpr const char* GetNamePRIMFIX(u8 fix)
{
return GetNameOneBit(fix, "Unfixed", "Fixed");
}
static constexpr const char* GetNameTEXTCC(u8 tcc)
{
return GetNameOneBit(tcc, "RGB", "RGBA");
}
static constexpr const char* GetNameTEXTFX(u8 tfx)
{
switch (tfx)
{
case 0: return "Modulate";
case 1: return "Decal";
case 2: return "Highlight";
case 3: return "Highlight2";
default: return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCSM(u8 csm)
{
return GetNameOneBit(csm, "CSM1", "CSM2");
}
static constexpr const char* GetNameTEXPSM(u8 psm)
{
switch (psm)
{
case 000: return "PSMCT32";
case 001: return "PSMCT24";
case 002: return "PSMCT16";
case 012: return "PSMCT16S";
case 023: return "PSMT8";
case 024: return "PSMT4";
case 033: return "PSMT8H";
case 044: return "PSMT4HL";
case 054: return "PSMT4HH";
case 060: return "PSMZ32";
case 061: return "PSMZ24";
case 062: return "PSMZ16";
case 072: return "PSMZ16S";
default: return "UNKNOWN";
}
}
static constexpr const char* GetNameTEXCPSM(u8 psm)
{
switch (psm)
{
case 000: return "PSMCT32";
case 002: return "PSMCT16";
case 012: return "PSMCT16S";
default: return "UNKNOWN";
const u8* in = reinterpret_cast<const u8*>(pixels.data() + y * width);
u8* out = wxpixels + y * pitch;
for (u32 x = 0; x < width; x++)
{
*(out++) = in[0];
*(out++) = in[1];
*(out++) = in[2];
in += sizeof(u32);
}
}
return wxImage(wxSize(width, height), wxpixels);
}
namespace GSDump
@@ -333,8 +245,17 @@ void Dialogs::GSDumpDialog::SelectedDump(wxListEvent& evt)
img.Rescale(400,250, wxIMAGE_QUALITY_HIGH);
m_preview_image->SetBitmap(wxBitmap(img));
}
else if (std::optional<wxImage> img = GetPreviewImageFromDump(StringUtil::wxStringToUTF8String(filename)); img.has_value())
{
// try embedded dump
img->Rescale(400, 250, wxIMAGE_QUALITY_HIGH);
m_preview_image->SetBitmap(img.value());
}
else
{
m_preview_image->SetBitmap(EmbeddedImage<res_NoIcon>().Get());
}
m_selected_dump = wxString(filename);
}
@@ -368,18 +289,16 @@ void Dialogs::GSDumpDialog::RunDump(wxCommandEvent& event)
{
if (!m_run->IsEnabled())
return;
FILE* dumpfile = wxFopen(m_selected_dump, L"rb");
if (!dumpfile)
m_thread->m_dump_file = GSDumpFile::OpenGSDump(m_selected_dump.ToUTF8());
if (!m_thread->m_dump_file)
{
wxString s;
s.Printf(_("Failed to load the dump %s !"), m_selected_dump);
wxMessageBox(s, _("GS Debugger"), wxICON_ERROR);
return;
}
if (m_selected_dump.EndsWith(".xz"))
m_thread->m_dump_file = std::make_unique<GSDumpLzma>(dumpfile, nullptr);
else
m_thread->m_dump_file = std::make_unique<GSDumpRaw >(dumpfile, nullptr);
m_run->Disable();
m_settings->Disable();
m_debug_mode->Enable();
@@ -483,11 +402,11 @@ void Dialogs::GSDumpDialog::GenPacketList()
m_gif_items.clear();
wxTreeItemId mainrootId = m_gif_list->AddRoot("root");
wxTreeItemId rootId = m_gif_list->AppendItem(mainrootId, "0 - VSync");
for (auto& element : m_dump_packets)
for (auto& element : m_thread->m_dump_file->GetPackets())
{
wxString s, t;
element.id == GSType::Transfer ? t.Printf(" - %s", GetName(element.path)) : t.Printf("");
s.Printf("%d - %s%s - %d byte", i, GetName(element.id), t, element.length);
element.id == GSType::Transfer ? t.Printf(" - %s", GSDumpTypes::GetName(element.path)) : t.Printf("");
s.Printf("%d - %s%s - %d byte", i, GSDumpTypes::GetName(element.id), t, element.length);
if (element.id == GSType::VSync)
{
m_gif_list->SetItemText(rootId, s);
@@ -506,7 +425,7 @@ void Dialogs::GSDumpDialog::GenPacketList()
m_gif_list->SelectItem(m_gif_items[0]);
}
void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
void Dialogs::GSDumpDialog::GenPacketInfo(const GSDumpFile::GSData& dump)
{
m_gif_packet->DeleteAllItems();
wxTreeItemId rootId = m_gif_packet->AddRoot("root");
@@ -514,12 +433,12 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
{
case GSType::Transfer:
{
char* data = dump.data.get();
const u8* data = dump.data;
u32 remaining = dump.length;
int idx = 0;
while (remaining >= 16)
{
wxTreeItemId trootId = m_gif_packet->AppendItem(rootId, wxString::Format("Transfer Path %s Packet %u", GetName(dump.path), idx));
wxTreeItemId trootId = m_gif_packet->AppendItem(rootId, wxString::Format("Transfer Path %s Packet %u", GSDumpTypes::GetName(dump.path), idx));
ParseTransfer(trootId, data);
m_gif_packet->Expand(trootId);
u32 size = ReadPacketSize(data);
@@ -532,7 +451,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
case GSType::VSync:
{
wxString s;
s.Printf("Field = %u", *(u8*)(dump.data.get()));
s.Printf("Field = %u", dump.data[0]);
m_gif_packet->AppendItem(rootId, s);
break;
}
@@ -549,7 +468,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
}
}
void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, char* data)
void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, const u8* data)
{
u64 tag = *(u64*)data;
u64 regs = *(u64*)(data + 8);
@@ -567,7 +486,7 @@ void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, char* data)
m_gif_packet->AppendItem(trootId, wxString::Format("nloop = %u", nloop));
m_gif_packet->AppendItem(trootId, wxString::Format("eop = %u", eop));
m_gif_packet->AppendItem(trootId, wxString::Format("flg = %s", GetName(flg)));
m_gif_packet->AppendItem(trootId, wxString::Format("flg = %s", GSDumpTypes::GetName(flg)));
m_gif_packet->AppendItem(trootId, wxString::Format("pre = %u", pre));
if (pre)
{
@@ -619,12 +538,12 @@ void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, char* data)
void Dialogs::GSDumpDialog::ParsePacket(wxTreeEvent& event)
{
GenPacketInfo(m_dump_packets[wxAtoi(m_gif_list->GetItemText(event.GetItem()).BeforeFirst('-'))]);
GenPacketInfo(m_thread->m_dump_file->GetPackets()[wxAtoi(m_gif_list->GetItemText(event.GetItem()).BeforeFirst('-'))]);
}
void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed)
{
wxTreeItemId rootId = m_gif_packet->AppendItem(id, wxString(GetName(reg)));
wxTreeItemId rootId = m_gif_packet->AppendItem(id, wxString(GSDumpTypes::GetName(reg)));
switch (reg)
{
case GIFReg::PRIM:
@@ -652,7 +571,7 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
q = BitCast<float>(data._u32[1]);
}
m_gif_packet->SetItemText(rootId, wxString::Format("%s - #%02x%02x%02x%02x, %g", GetName(reg), r, g, b, a, q));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - #%02x%02x%02x%02x, %g", GSDumpTypes::GetName(reg), r, g, b, a, q));
m_gif_packet->AppendItem(rootId, wxString::Format("R = %u", r));
m_gif_packet->AppendItem(rootId, wxString::Format("G = %u", g));
m_gif_packet->AppendItem(rootId, wxString::Format("B = %u", b));
@@ -670,11 +589,11 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
{
m_stored_q = BitCast<float>(data._u32[2]);
m_gif_packet->AppendItem(rootId, wxString::Format("Q = %g", m_stored_q));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %g)", GetName(reg), s, t, m_stored_q));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g, %g)", GSDumpTypes::GetName(reg), s, t, m_stored_q));
}
else
{
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GetName(reg), s, t));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GSDumpTypes::GetName(reg), s, t));
}
break;
}
@@ -684,15 +603,15 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
float v = static_cast<float>(GetBits(data, packed ? 32 : 16, 14)) / 16.f;
m_gif_packet->AppendItem(rootId, wxString::Format("U = %g", u));
m_gif_packet->AppendItem(rootId, wxString::Format("V = %g", v));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GetName(reg), u, v));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - (%g, %g)", GSDumpTypes::GetName(reg), u, v));
break;
}
case GIFReg::XYZF2:
case GIFReg::XYZF3:
{
const char* name = GetName(reg);
const char* name = GSDumpTypes::GetName(reg);
if (packed && (reg == GIFReg::XYZF2) && GetBits(data, 111, 1))
name = GetName(GIFReg::XYZF3);
name = GSDumpTypes::GetName(GIFReg::XYZF3);
float x, y;
u32 z, f;
@@ -722,9 +641,9 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
case GIFReg::XYZ2:
case GIFReg::XYZ3:
{
const char* name = GetName(reg);
const char* name = GSDumpTypes::GetName(reg);
if (packed && (reg == GIFReg::XYZ2) && GetBits(data, 111, 1))
name = GetName(GIFReg::XYZ3);
name = GSDumpTypes::GetName(GIFReg::XYZ3);
float x, y;
u32 z;
@@ -755,7 +674,7 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
u32 tw = GetBits(data, 26, 4);
u32 th = GetBits(data, 30, 4);
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %ux%u %s", GetName(reg), 1 << tw, 1 << th, GetNameTEXPSM(psm)));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %ux%u %s", GSDumpTypes::GetName(reg), 1 << tw, 1 << th, GetNameTEXPSM(psm)));
m_gif_packet->AppendItem(rootId, wxString::Format("TBP0 = %u", GetBits(data, 0, 14)));
m_gif_packet->AppendItem(rootId, wxString::Format("TBW = %u", GetBits(data, 14, 6)));
m_gif_packet->AppendItem(rootId, wxString::Format("PSM = %s", GetNameTEXPSM(psm)));
@@ -774,7 +693,7 @@ void Dialogs::GSDumpDialog::ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data
{
u32 f = GetBits(data, packed ? 100 : 56, 8);
m_gif_packet->AppendItem(rootId, wxString::Format("F = %u", f));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %u", GetName(reg), f));
m_gif_packet->SetItemText(rootId, wxString::Format("%s - %u", GSDumpTypes::GetName(reg), f));
break;
}
case GIFReg::AD:
@@ -815,7 +734,7 @@ void Dialogs::GSDumpDialog::ParseTreePrim(wxTreeItemId& id, u32 prim)
m_gif_packet->Expand(id);
}
void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, char* regs)
void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSDumpFile::GSData& event, u8* regs)
{
switch (event.id)
{
@@ -827,18 +746,18 @@ void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, char* regs)
{
std::unique_ptr<char[]> data(new char[16384]);
int addr = 16384 - event.length;
memcpy(data.get(), event.data.get() + addr, event.length);
memcpy(data.get(), event.data + addr, event.length);
GSgifTransfer1((u8*)data.get(), addr);
break;
}
case GSTransferPath::Path1New:
GSgifTransfer((u8*)event.data.get(), event.length / 16);
GSgifTransfer((u8*)event.data, event.length / 16);
break;
case GSTransferPath::Path2:
GSgifTransfer2((u8*)event.data.get(), event.length / 16);
GSgifTransfer2((u8*)event.data, event.length / 16);
break;
case GSTransferPath::Path3:
GSgifTransfer3((u8*)event.data.get(), event.length / 16);
GSgifTransfer3((u8*)event.data, event.length / 16);
break;
default:
break;
@@ -853,12 +772,12 @@ void Dialogs::GSDumpDialog::ProcessDumpEvent(const GSData& event, char* regs)
}
case GSType::ReadFIFO2:
{
std::unique_ptr<char[]> arr(new char[*((int*)event.data.get())]);
GSreadFIFO2((u8*)arr.get(), *((int*)event.data.get()));
std::unique_ptr<char[]> arr(new char[*((int*)event.data)]);
GSreadFIFO2((u8*)arr.get(), *((int*)event.data));
break;
}
case GSType::Registers:
memcpy(regs, event.data.get(), 8192);
memcpy(regs, event.data, 8192);
break;
}
}
@@ -893,8 +812,12 @@ void Dialogs::GSDumpDialog::GSThread::OnStop()
void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
{
GSDump::isRunning = true;
u32 crc = 0, ss = 0;
if (!m_dump_file->ReadFile())
{
OnStop();
return;
}
GSRendererType renderer = g_Conf->EmuOptions.GS.Renderer;
switch (m_renderer)
{
@@ -921,45 +844,6 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
default:
break;
}
char regs[8192];
m_dump_file->Read(&crc, 4);
m_dump_file->Read(&ss, 4);
std::unique_ptr<char[]> state_data(new char[ss]);
m_dump_file->Read(state_data.get(), ss);
m_dump_file->Read(&regs, 8192);
freezeData fd = {(int)ss, (u8*)state_data.get()};
m_root_window->m_dump_packets.clear();
while (!m_dump_file->IsEof())
{
GSType id;
GSTransferPath id_transfer = GSTransferPath::Dummy;
m_dump_file->Read(&id, 1);
s32 size = 0;
switch (id)
{
case GSType::Transfer:
m_dump_file->Read(&id_transfer, 1);
m_dump_file->Read(&size, 4);
break;
case GSType::VSync:
size = 1;
break;
case GSType::ReadFIFO2:
size = 4;
break;
case GSType::Registers:
size = 8192;
break;
}
std::unique_ptr<char[]> data(new char[size]);
m_dump_file->Read(data.get(), size);
m_root_window->m_dump_packets.push_back({id, std::move(data), size, id_transfer});
}
GSinit();
sApp.OpenGsPanel();
@@ -974,16 +858,38 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
g_FrameCount = 0;
}
if (!GSopen(g_Conf->EmuOptions.GS, renderer, (u8*)regs))
Pcsx2Config::GSOptions config(g_Conf->EmuOptions.GS);
config.MaskUserHacks();
config.MaskUpscalingHacks();
if (!m_dump_file->GetSerial().empty())
{
if (const GameDatabaseSchema::GameEntry* entry = GameDatabase::findGame(m_dump_file->GetSerial()); entry)
{
// apply hardware fixes to config before opening (e.g. tex in rt)
entry->applyGSHardwareFixes(config);
}
}
u8 regs[8192] = {};
if (!m_dump_file->GetRegsData().empty())
std::memcpy(regs, m_dump_file->GetRegsData().data(), std::min(m_dump_file->GetRegsData().size(), sizeof(regs)));
if (!GSopen(config, renderer, regs))
{
OnStop();
return;
}
GSsetGameCRC((int)crc, 0);
GSsetGameCRC((int)m_dump_file->GetCRC(), 0);
freezeData fd = {static_cast<int>(m_dump_file->GetStateData().size()),
const_cast<u8*>(m_dump_file->GetStateData().data())};
if (GSfreeze(FreezeAction::Load, &fd))
GSDump::isRunning = false;
{
OnStop();
return;
}
GSvsync(1, false);
GSreset();
GSfreeze(FreezeAction::Load, &fd);
@@ -992,6 +898,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
m_debug_index = 0;
size_t debug_idx = 0;
GSDump::isRunning = true;
while (GSDump::isRunning)
{
if (m_debug)
@@ -1001,7 +908,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
switch (m_root_window->m_button_events[0].btn)
{
case Step:
if (debug_idx >= m_root_window->m_dump_packets.size())
if (debug_idx >= m_dump_file->GetPackets().size())
debug_idx = 0;
m_debug_index = debug_idx;
break;
@@ -1011,13 +918,13 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
debug_idx = 0;
break;
case RunVSync:
if (debug_idx >= m_root_window->m_dump_packets.size())
if (debug_idx >= m_dump_file->GetPackets().size())
debug_idx = 1;
if ((debug_idx + 1) < m_root_window->m_dump_packets.size())
if ((debug_idx + 1) < m_dump_file->GetPackets().size())
{
auto it = std::find_if(m_root_window->m_dump_packets.begin() + debug_idx + 1, m_root_window->m_dump_packets.end(), [](const GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_root_window->m_dump_packets))
m_debug_index = std::distance(m_root_window->m_dump_packets.begin(), it);
auto it = std::find_if(m_dump_file->GetPackets().begin() + debug_idx + 1, m_dump_file->GetPackets().end(), [](const GSDumpFile::GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_dump_file->GetPackets()))
m_debug_index = std::distance(m_dump_file->GetPackets().begin(), it);
}
break;
}
@@ -1027,12 +934,12 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
{
while (debug_idx <= m_debug_index)
{
m_root_window->ProcessDumpEvent(m_root_window->m_dump_packets[debug_idx++], regs);
m_root_window->ProcessDumpEvent(m_dump_file->GetPackets()[debug_idx++], regs);
}
if ((debug_idx + 1) < m_root_window->m_dump_packets.size())
if ((debug_idx + 1) < m_dump_file->GetPackets().size())
{
auto it = std::find_if(m_root_window->m_dump_packets.begin() + debug_idx + 1, m_root_window->m_dump_packets.end(), [](const GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_root_window->m_dump_packets))
auto it = std::find_if(m_dump_file->GetPackets().begin() + debug_idx + 1, m_dump_file->GetPackets().end(), [](const GSDumpFile::GSData& gs) { return gs.id == GSType::Registers; });
if (it != std::end(m_dump_file->GetPackets()))
m_root_window->ProcessDumpEvent(*it, regs);
}
}
@@ -1041,11 +948,11 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
m_root_window->ProcessDumpEvent({GSType::VSync, 0, 0, GSTransferPath::Dummy}, regs);
}
}
else if (m_root_window->m_dump_packets.size())
else if (m_dump_file->GetPackets().size())
{
while (i < m_root_window->m_dump_packets.size())
while (i < m_dump_file->GetPackets().size())
{
GSData& packet = m_root_window->m_dump_packets[i++];
const GSDumpFile::GSData& packet = m_dump_file->GetPackets()[i++];
m_root_window->ProcessDumpEvent(packet, regs);
if (packet.id == GSType::VSync)
{
@@ -1065,7 +972,7 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
}
}
if (i >= m_root_window->m_dump_packets.size())
if (i >= m_dump_file->GetPackets().size())
i = 0;
}
if (window)
+4 -123
View File
@@ -25,28 +25,6 @@
#include <wx/treectrl.h>
#include <wx/fswatcher.h>
#define GEN_REG_ENUM_CLASS_CONTENT(ClassName, EntryName, Value) \
EntryName = Value,
#define GEN_REG_GETNAME_CONTENT(ClassName, EntryName, Value) \
case ClassName::EntryName: \
return #EntryName;
#define GEN_REG_ENUM_CLASS_AND_GETNAME(Macro, ClassName, Type, DefaultString) \
enum class ClassName : Type \
{ \
Macro(GEN_REG_ENUM_CLASS_CONTENT) \
}; \
static constexpr const char* GetName(ClassName reg) \
{ \
switch (reg) \
{ \
Macro(GEN_REG_GETNAME_CONTENT) \
default: \
return DefaultString; \
} \
}
class FirstTimeWizard : public wxWizard
{
typedef wxWizard _parent;
@@ -147,102 +125,6 @@ namespace Dialogs
ID_FRAMERATE,
};
// clang-format off
#define DEF_GSType(X) \
X(GSType, Transfer, 0) \
X(GSType, VSync, 1) \
X(GSType, ReadFIFO2, 2) \
X(GSType, Registers, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSType, GSType, u8, "UnknownType")
#undef DEF_GSType
#define DEF_GSTransferPath(X) \
X(GSTransferPath, Path1Old, 0) \
X(GSTransferPath, Path2, 1) \
X(GSTransferPath, Path3, 2) \
X(GSTransferPath, Path1New, 3) \
X(GSTransferPath, Dummy, 4)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GSTransferPath, GSTransferPath, u8, "UnknownPath")
#undef DEF_GSTransferPath
#define DEF_GIFFlag(X) \
X(GIFFlag, PACKED, 0) \
X(GIFFlag, REGLIST, 1) \
X(GIFFlag, IMAGE, 2) \
X(GIFFlag, IMAGE2, 3)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFFlag, GIFFlag, u8, "UnknownFlag")
#undef DEF_GifFlag
#define DEF_GIFReg(X) \
X(GIFReg, PRIM, 0x00) \
X(GIFReg, RGBAQ, 0x01) \
X(GIFReg, ST, 0x02) \
X(GIFReg, UV, 0x03) \
X(GIFReg, XYZF2, 0x04) \
X(GIFReg, XYZ2, 0x05) \
X(GIFReg, TEX0_1, 0x06) \
X(GIFReg, TEX0_2, 0x07) \
X(GIFReg, CLAMP_1, 0x08) \
X(GIFReg, CLAMP_2, 0x09) \
X(GIFReg, FOG, 0x0a) \
X(GIFReg, XYZF3, 0x0c) \
X(GIFReg, XYZ3, 0x0d) \
X(GIFReg, AD, 0x0e) \
X(GIFReg, NOP, 0x0f) \
X(GIFReg, TEX1_1, 0x14) \
X(GIFReg, TEX1_2, 0x15) \
X(GIFReg, TEX2_1, 0x16) \
X(GIFReg, TEX2_2, 0x17) \
X(GIFReg, XYOFFSET_1, 0x18) \
X(GIFReg, XYOFFSET_2, 0x19) \
X(GIFReg, PRMODECONT, 0x1a) \
X(GIFReg, PRMODE, 0x1b) \
X(GIFReg, TEXCLUT, 0x1c) \
X(GIFReg, SCANMSK, 0x22) \
X(GIFReg, MIPTBP1_1, 0x34) \
X(GIFReg, MIPTBP1_2, 0x35) \
X(GIFReg, MIPTBP2_1, 0x36) \
X(GIFReg, MIPTBP2_2, 0x37) \
X(GIFReg, TEXA, 0x3b) \
X(GIFReg, FOGCOL, 0x3d) \
X(GIFReg, TEXFLUSH, 0x3f) \
X(GIFReg, SCISSOR_1, 0x40) \
X(GIFReg, SCISSOR_2, 0x41) \
X(GIFReg, ALPHA_1, 0x42) \
X(GIFReg, ALPHA_2, 0x43) \
X(GIFReg, DIMX, 0x44) \
X(GIFReg, DTHE, 0x45) \
X(GIFReg, COLCLAMP, 0x46) \
X(GIFReg, TEST_1, 0x47) \
X(GIFReg, TEST_2, 0x48) \
X(GIFReg, PABE, 0x49) \
X(GIFReg, FBA_1, 0x4a) \
X(GIFReg, FBA_2, 0x4b) \
X(GIFReg, FRAME_1, 0x4c) \
X(GIFReg, FRAME_2, 0x4d) \
X(GIFReg, ZBUF_1, 0x4e) \
X(GIFReg, ZBUF_2, 0x4f) \
X(GIFReg, BITBLTBUF, 0x50) \
X(GIFReg, TRXPOS, 0x51) \
X(GIFReg, TRXREG, 0x52) \
X(GIFReg, TRXDIR, 0x53) \
X(GIFReg, HWREG, 0x54) \
X(GIFReg, SIGNAL, 0x60) \
X(GIFReg, FINISH, 0x61) \
X(GIFReg, LABEL, 0x62)
GEN_REG_ENUM_CLASS_AND_GETNAME(DEF_GIFReg, GIFReg, u8, "UnknownReg")
#undef DEF_GIFReg
// clang-format on
struct GSData
{
GSType id;
std::unique_ptr<char[]> data;
int length;
GSTransferPath path;
};
enum ButtonState
{
Step,
@@ -256,16 +138,15 @@ namespace Dialogs
int index;
};
std::vector<GSEvent> m_button_events;
std::vector<GSData> m_dump_packets;
std::vector<wxTreeItemId> m_gif_items;
float m_stored_q = 1.0;
void ProcessDumpEvent(const GSData& event, char* regs);
void ProcessDumpEvent(const GSDumpFile::GSData& event, u8* regs);
u32 ReadPacketSize(const void* packet);
void GenPacketList();
void GenPacketInfo(GSData& dump);
void ParseTransfer(wxTreeItemId& id, char* data);
void ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed);
void GenPacketInfo(const GSDumpFile::GSData& dump);
void ParseTransfer(wxTreeItemId& id, const u8* data);
void ParseTreeReg(wxTreeItemId& id, GSDumpTypes::GIFReg reg, u128 data, bool packed);
void ParseTreePrim(wxTreeItemId& id, u32 prim);
void CloseDump(wxCommandEvent& event);
class GSThread : public pxThread
+2
View File
@@ -186,6 +186,7 @@
<ClCompile Include="GameDatabase.cpp" />
<ClCompile Include="Gif_Logger.cpp" />
<ClCompile Include="Gif_Unit.cpp" />
<ClCompile Include="GSDumpReplayer.cpp" />
<ClCompile Include="GS\Renderers\DX11\D3D.cpp" />
<ClCompile Include="GS\Renderers\HW\GSTextureReplacementLoaders.cpp" />
<ClCompile Include="GS\Renderers\HW\GSTextureReplacements.cpp" />
@@ -488,6 +489,7 @@
<ClInclude Include="Frontend\XInputSource.h" />
<ClInclude Include="GameDatabase.h" />
<ClInclude Include="Gif_Unit.h" />
<ClInclude Include="GSDumpReplayer.h" />
<ClInclude Include="GS\Renderers\DX11\D3D.h" />
<ClInclude Include="GS\Renderers\HW\GSTextureReplacements.h" />
<ClInclude Include="GS\Window\GSwxDialog.h" />
+2
View File
@@ -1202,6 +1202,7 @@
<ClCompile Include="x86\iR5900Analysis.cpp">
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
</ClCompile>
<ClCompile Include="GSDumpReplayer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@@ -1985,6 +1986,7 @@
<ClInclude Include="x86\iR5900Analysis.h">
<Filter>System\Ps2\EmotionEngine\EE\Dynarec</Filter>
</ClInclude>
<ClInclude Include="GSDumpReplayer.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GS\GS.rc">
+13 -2
View File
@@ -327,19 +327,28 @@ __fi bool dmacWrite32( u32 mem, mem32_t& value )
{
if ((psHu32(mem & ~0xff) & 0x100) && dmacRegs.ctrl.DMAE && !psHu8(DMAC_ENABLER + 2))
{
DevCon.Warning("Gamefix: Write to DMA addr %x while STR is busy!", mem);
//DevCon.Warning("Gamefix: Write to DMA addr %x while STR is busy!", mem);
while (psHu32(mem & ~0xff) & 0x100)
{
switch ((mem >> 8) & 0xFF)
{
case 0x80: // VIF0
vif0Interrupt();
cpuRegs.interrupt &= ~(1 << DMAC_VIF0);
break;
case 0x90: // VIF1
vif1Interrupt();
if (vif1Regs.stat.VEW)
{
vu1Finish(false);
vif1VUFinish();
}
else
vif1Interrupt();
cpuRegs.interrupt &= ~(1 << DMAC_VIF1);
break;
case 0xA0: // GIF
gifInterrupt();
cpuRegs.interrupt &= ~(1 << DMAC_GIF);
break;
case 0xB0: // IPUFROM
[[fallthrough]];
@@ -351,9 +360,11 @@ __fi bool dmacWrite32( u32 mem, mem32_t& value )
break;
case 0xD0: // SPRFROM
SPRFROMinterrupt();
cpuRegs.interrupt &= ~(1 << DMAC_FROM_SPR);
break;
case 0xD4: // SPRTO
SPRTOinterrupt();
cpuRegs.interrupt &= ~(1 << DMAC_TO_SPR);
break;
default:
return false;
+53 -12
View File
@@ -127,21 +127,24 @@ void mVUDTendProgram(mV, microFlagCycles* mFC, int isEbit)
xMOV(ptr32[&mVU.regs().nextBlockCycles], 0);
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (isEbit) // Clear 'is busy' Flags
{
if (!mVU.index || !THREAD_VU1)
{
xAND(ptr32[&VU0.VI[REG_VPU_STAT].UL], (isVU1 ? ~0x100 : ~0x001)); // VBS0/VBS1 flag
}
else
xFastCall((void*)mVUTBit);
}
if (isEbit != 2) // Save PC, and Jump to Exit Point
{
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUTBit);
xJMP(mVU.exitFunct);
}
memcpy(&mVUregs, &stateBackup, sizeof(mVUregs)); //Restore the state for the rest of the recompile
}
@@ -244,6 +247,7 @@ void mVUendProgram(mV, microFlagCycles* mFC, int isEbit)
xMOVAPS(ptr128[&mVU.regs().micro_statusflags], xmmT1);
}
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if ((isEbit && isEbit != 3)) // Clear 'is busy' Flags
{
@@ -252,8 +256,6 @@ void mVUendProgram(mV, microFlagCycles* mFC, int isEbit)
{
xAND(ptr32[&VU0.VI[REG_VPU_STAT].UL], (isVU1 ? ~0x100 : ~0x001)); // VBS0/VBS1 flag
}
else
xFastCall((void*)mVUEBit);
}
else if(isEbit)
{
@@ -262,7 +264,8 @@ void mVUendProgram(mV, microFlagCycles* mFC, int isEbit)
if (isEbit != 2 && isEbit != 3) // Save PC, and Jump to Exit Point
{
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
}
memcpy(&mVUregs, &stateBackup, sizeof(mVUregs)); //Restore the state for the rest of the recompile
@@ -321,6 +324,8 @@ void normJumpCompile(mV, microFlagCycles& mFC, bool isEvilJump)
//So if it is taken, you need to end the program, else you get infinite loops.
mVUendProgram(mVU, &mFC, 2);
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], arg1regd);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
}
@@ -340,7 +345,10 @@ void normBranch(mV, microFlagCycles& mFC)
if (mVUup.dBit && doDBitHandling)
{
u32 tempPC = iPC;
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x400 : 0x4));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
xForwardJump32 eJMP(Jcc_Zero);
if (!mVU.index || !THREAD_VU1)
{
@@ -355,7 +363,10 @@ void normBranch(mV, microFlagCycles& mFC)
if (mVUup.tBit)
{
u32 tempPC = iPC;
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x800 : 0x8));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
xForwardJump32 eJMP(Jcc_Zero);
if (!mVU.index || !THREAD_VU1)
{
@@ -381,6 +392,8 @@ void normBranch(mV, microFlagCycles& mFC)
mVUendProgram(mVU, &mFC, 3);
iPC = branchAddr(mVU) / 4;
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
iPC = tempPC;
}
@@ -407,7 +420,10 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc)
{
DevCon.Warning("T-Bit on branch, please report if broken");
u32 tempPC = iPC;
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x800 : 0x8));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
xForwardJump32 eJMP(Jcc_Zero);
if (!mVU.index || !THREAD_VU1)
{
@@ -419,11 +435,15 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc)
xForwardJump32 tJMP(xInvertCond((JccComparisonType)JMPcc));
incPC(4); // Set PC to First instruction of Non-Taken Side
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUTBit);
xJMP(mVU.exitFunct);
tJMP.SetTarget();
incPC(-4); // Go Back to Branch Opcode to get branchAddr
iPC = branchAddr(mVU) / 4;
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUTBit);
xJMP(mVU.exitFunct);
eJMP.SetTarget();
iPC = tempPC;
@@ -431,7 +451,10 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc)
if (mVUup.dBit && doDBitHandling)
{
u32 tempPC = iPC;
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x400 : 0x4));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
xForwardJump32 eJMP(Jcc_Zero);
if (!mVU.index || !THREAD_VU1)
{
@@ -466,11 +489,15 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc)
xForwardJump32 dJMP((JccComparisonType)JMPcc);
incPC(4); // Set PC to First instruction of Non-Taken Side
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
dJMP.SetTarget();
incPC(-4); // Go Back to Branch Opcode to get branchAddr
iPC = branchAddr(mVU) / 4;
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
iPC = tempPC;
}
@@ -486,12 +513,16 @@ void condBranch(mV, microFlagCycles& mFC, int JMPcc)
xForwardJump32 eJMP(((JccComparisonType)JMPcc));
incPC(1); // Set PC to First instruction of Non-Taken Side
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
eJMP.SetTarget();
incPC(-4); // Go Back to Branch Opcode to get branchAddr
iPC = branchAddr(mVU) / 4;
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], xPC);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
return;
}
@@ -550,7 +581,10 @@ void normJump(mV, microFlagCycles& mFC)
}
if (mVUup.dBit && doDBitHandling)
{
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
if (THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x400 : 0x4));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
xForwardJump32 eJMP(Jcc_Zero);
if (!mVU.index || !THREAD_VU1)
{
@@ -565,7 +599,10 @@ void normJump(mV, microFlagCycles& mFC)
}
if (mVUup.tBit)
{
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x800 : 0x8));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
xForwardJump32 eJMP(Jcc_Zero);
if (!mVU.index || !THREAD_VU1)
{
@@ -575,6 +612,8 @@ void normJump(mV, microFlagCycles& mFC)
mVUDTendProgram(mVU, &mFC, 2);
xMOV(gprT1, ptr32[&mVU.branch]);
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], gprT1);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUTBit);
xJMP(mVU.exitFunct);
eJMP.SetTarget();
}
@@ -583,6 +622,8 @@ void normJump(mV, microFlagCycles& mFC)
mVUendProgram(mVU, &mFC, 2);
xMOV(gprT1, ptr32[&mVU.branch]);
xMOV(ptr32[&mVU.regs().VI[REG_TPC].UL], gprT1);
if (mVU.index && THREAD_VU1)
xFastCall((void*)mVUEBit);
xJMP(mVU.exitFunct);
}
else
+8 -2
View File
@@ -549,7 +549,10 @@ __fi void mVUinitFirstPass(microVU& mVU, uptr pState, u8* thisPtr)
void mVUDoDBit(microVU& mVU, microFlagCycles* mFC)
{
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x400 : 0x4));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x400 : 0x4));
xForwardJump32 eJMP(Jcc_Zero);
if (!isVU1 || !THREAD_VU1)
{
@@ -564,7 +567,10 @@ void mVUDoDBit(microVU& mVU, microFlagCycles* mFC)
void mVUDoTBit(microVU& mVU, microFlagCycles* mFC)
{
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
if (mVU.index && THREAD_VU1)
xTEST(ptr32[&vu1Thread.vuFBRST], (isVU1 ? 0x800 : 0x8));
else
xTEST(ptr32[&VU0.VI[REG_FBRST].UL], (isVU1 ? 0x800 : 0x8));
xForwardJump32 eJMP(Jcc_Zero);
if (!isVU1 || !THREAD_VU1)
{
-1
View File
@@ -14,7 +14,6 @@
*/
#pragma once
extern void _vu0WaitMicro();
extern void _vu0FinishMicro();
@@ -113,6 +113,7 @@ TEST(CodegenTests, MathTest)
CODEGEN_TEST_64(xADD(r8, r9), "4d 01 c8");
CODEGEN_TEST_64(xADD(r8, 0x12), "49 83 c0 12");
CODEGEN_TEST_64(xADD(rax, 0x1234), "48 05 34 12 00 00");
CODEGEN_TEST_64(xADD(ptr8[base], 1), "80 05 f9 ff ff ff 01");
CODEGEN_TEST_64(xADD(ptr32[base], -0x60), "83 05 f9 ff ff ff a0");
CODEGEN_TEST_64(xADD(ptr32[base], 0x1234), "81 05 f6 ff ff ff 34 12 00 00");
CODEGEN_TEST_BOTH(xADD(eax, ebx), "01 d8");