Compare commits

..
30 Commits
Author SHA1 Message Date
refractionpcsx2 34b7b07ff8 GameDB: Added a whole host of auto GS HW renderer fixes 2022-03-13 01:53:15 +00:00
Connor McLaughlin 6c33b73cdd GS: Make TC offset changable without recreating 2022-03-12 20:48:51 +00:00
Connor McLaughlin 1e86ba4120 GS: Append game serial/name to dump filename 2022-03-12 20:48:51 +00:00
Connor McLaughlin 89c79aa6c3 GSDump: Add embedded screenshot 2022-03-12 20:48:51 +00:00
Connor McLaughlin 4ed748fa30 GSDump: Add extensible header and serial
Serial is used to apply hw fixes.
2022-03-12 20:48:51 +00:00
Connor McLaughlin 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 McLaughlin 892eec79ed Workflows: Fix lint-gamedb for gsHWFixes 2022-03-12 20:48:51 +00:00
Connor McLaughlin b248b4a8af GameDB: Add HW fixes for GTASA/GOW/GOW2 2022-03-12 20:48:51 +00:00
Connor McLaughlin 39fe467b64 GS: Purge CRCs of unused titles 2022-03-12 20:48:51 +00:00
Connor McLaughlin 1c301ec889 GS: Move point list palette to gamedb 2022-03-12 20:48:51 +00:00
Connor McLaughlin de5690ddcb GS: Move texture-inside-rt flag to gamedb 2022-03-12 20:48:51 +00:00
Connor McLaughlin f376c8f7ae GS: Remove second source of truth for HWMipmap 2022-03-12 20:48:51 +00:00
Connor McLaughlin 2e199d47a8 GS: Move automatic mipmapping override to gamedb 2022-03-12 20:48:51 +00:00
Connor McLaughlin 96269db93e GameDatabase: Add ability to override GS fixes 2022-03-12 20:48:51 +00:00
Connor McLaughlin 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 McLaughlin 5961db6b9b GS/Vulkan: Elide render pass restarts on depth buffer toggle 2022-03-12 12:22:51 +00:00
Connor McLaughlin d0039c2920 Gif_Unit: Vectorize analyzeTag() 2022-03-12 12:10:32 +00:00
TellowKrinkle 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
TheLastRar 134242973b Config: Set a sane default value for HddSizeSectors 2022-03-09 10:07:54 +00:00
TheLastRar 92900d8dc8 Config: Fix manual subnet mask save/load 2022-03-09 10:07:54 +00:00
Ziemas 7a970e1d00 Filesystem: Properly convert stat return to bool.
Two of the overloads where wrong.
2022-03-09 09:33:51 +00:00
45 changed files with 2104 additions and 644 deletions
@@ -12,6 +12,7 @@ allowed_game_options = [
"roundModes",
"clampModes",
"gameFixes",
"gsHWFixes",
"speedHacks",
"memcardFilters",
"patches",
@@ -36,7 +37,37 @@ allowed_game_fixes = [
"IbitHack",
"VUOverflowHack",
]
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)
File diff suppressed because it is too large Load Diff
+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)
+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);
}
+2 -2
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);
+13 -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,10 @@
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawRangeStart"/>
<widget class="QSpinBox" name="skipDrawStart"/>
</item>
<item>
<widget class="QSpinBox" name="skipDrawRangeCount"/>
<widget class="QSpinBox" name="skipDrawEnd"/>
</item>
</layout>
</item>
@@ -809,7 +809,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 +823,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>
+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();
+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();
};
+121 -43
View File
@@ -23,13 +23,19 @@
int GSState::s_n = 0;
static __fi bool IsAutoFlushEnabled()
{
return (GSConfig.Renderer == GSRendererType::SW) ? GSConfig.AutoFlushSW : GSConfig.UserHacks_AutoFlush;
}
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)
, tex_flushed(true)
, m_vt(this)
, m_regs(NULL)
, m_crc(0)
@@ -39,18 +45,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 +128,6 @@ GSState::GSState()
PRIM = &m_env.PRIM;
//CSR->rREV = 0x20;
m_env.PRMODECONT.AC = 1;
tex_flushed = true;
Reset();
@@ -246,7 +241,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>;
@@ -257,7 +252,7 @@ void GSState::ResetHandlers()
// 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)
if (IsAutoFlushEnabled())
index_swap ? SetPrimHandlers<true, true>() : SetPrimHandlers<true, false>();
else
index_swap ? SetPrimHandlers<false, true>() : SetPrimHandlers<false, false>();
@@ -268,7 +263,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 +314,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;
@@ -1136,7 +1144,7 @@ void GSState::GIFRegHandlerTEXFLUSH(const GIFReg* RESTRICT r)
// 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 +1713,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 +2180,7 @@ int GSState::Defrost(const freezeData* fd)
u8* data = fd->data;
int version;
u32 version;
ReadState(&version, data);
@@ -2433,10 +2486,14 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
__forceinline void GSState::HandleAutoFlush()
{
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);
// 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;
// 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 (((frame_hit && ((m_context->TEX0.PSM ^ m_context->FRAME.PSM) & ~0x30) == 0) || (zbuf_hit && ((m_context->TEX0.PSM ^ m_context->ZBUF.PSM) & ~0x30) == 0)) && PRIM->TME && (m_context->FRAME.FBMSK != 0xFFFFFFFF))
{
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 +2570,45 @@ __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 (((frame_hit && (m_context->TEX0.PSM == m_context->FRAME.PSM)) || (zbuf_hit && (m_context->TEX0.PSM == m_context->ZBUF.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));
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())
{
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 +2622,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)
+6 -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();
+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);
+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;
}
}
}
+51 -103
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;
}
// Check if it's a full screen blit (or at least half screen), ignore small writes.
half_bottom = minvert == 0 && m_r.height() <= maxvert && (m_r.height()+1) >= 224;
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))
{
@@ -1424,7 +1371,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 +1396,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 +1451,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;
@@ -1711,7 +1658,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 +1684,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 +1710,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 +1835,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;
+7 -6
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)
@@ -544,7 +544,8 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
// 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)
if (((GSConfig.AccurateBlendingUnit >= AccBlendLevel::Basic) || (m_env.COLCLAMP.CLAMP == 0))
&& g_gs_device->Features().texture_barrier && blend_ad_alpha_masked)
ALPHA_C = 0;
else if (((GSConfig.AccurateBlendingUnit >= AccBlendLevel::Medium)
// Detect barrier aka fbmask on d3d11.
@@ -955,7 +956,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 +965,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"))
-8
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()
+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(); };
+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] Hardware fixes are enabled, not using automatic fixes.");
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);
+35 -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,7 +590,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
void Pcsx2Config::GSOptions::MaskUserHacks()
{
if (UserHacks)
if (ManualUserHacks)
return;
UserHacks_AlignSpriteX = false;
@@ -593,12 +600,15 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
UserHacks_HalfPixelOffset = 0;
UserHacks_RoundSprite = 0;
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 +616,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 +701,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);
+6
View File
@@ -218,6 +218,10 @@ 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();
if (HasValidVM())
ApplyGameFixes();
}
@@ -295,6 +299,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)
+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;
+159 -15
View File
@@ -22,8 +22,11 @@
#include "common/EmbeddedImage.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "gui/Resources/NoIcon.h"
#include "GS.h"
#include "GS/GSDump.h"
#include "HostDisplay.h"
#include "PathDefs.h"
@@ -31,6 +34,7 @@
#include "gui/GSFrame.h"
#include "Counters.h"
#include "PerformanceMetrics.h"
#include "GameDatabase.h"
#include <wx/mstream.h>
#include <wx/listctrl.h>
@@ -45,6 +49,7 @@
#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)
@@ -166,6 +171,94 @@ static constexpr const char* GetNameTEXCPSM(u8 psm)
}
}
static std::unique_ptr<GSDumpFile> GetDumpFile(const std::string& filename)
{
std::FILE* fp = FileSystem::OpenCFile(filename.c_str(), "rb");
if (!fp)
return nullptr;
if (StringUtil::EndsWith(filename, ".xz"))
return std::make_unique<GSDumpLzma>(fp, nullptr);
else
return std::make_unique<GSDumpRaw>(fp, nullptr);
}
static bool GetPreviewImageFromDump(const std::string& filename, u32* width, u32* height, std::vector<u32>* pixels)
{
try
{
std::unique_ptr<GSDumpFile> dump = GetDumpFile(filename);
if (!dump)
return false;
u32 crc;
dump->Read(&crc, sizeof(crc));
if (crc != 0xFFFFFFFFu)
{
// not new header dump, so no preview
return false;
}
u32 header_size;
dump->Read(&header_size, sizeof(header_size));
if (header_size < sizeof(GSDumpHeader))
{
// doesn't have the screenshot fields
return false;
}
std::unique_ptr<u8[]> header_bits = std::make_unique<u8[]>(header_size);
dump->Read(header_bits.get(), header_size);
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;
}
catch (...)
{
return false;
}
}
static std::optional<wxImage> GetPreviewImageFromDump(const std::string& filename)
{
std::vector<u32> pixels;
u32 width, height;
if (!GetPreviewImageFromDump(filename, &width, &height, &pixels))
return std::nullopt;
// 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++)
{
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
{
bool isRunning = false;
@@ -333,8 +426,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 +470,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 = GetDumpFile(StringUtil::wxStringToUTF8String(m_selected_dump));
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();
@@ -514,7 +614,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
{
case GSType::Transfer:
{
char* data = dump.data.get();
u8* data = dump.data.get();
u32 remaining = dump.length;
int idx = 0;
while (remaining >= 16)
@@ -532,7 +632,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 +649,7 @@ void Dialogs::GSDumpDialog::GenPacketInfo(GSData& dump)
}
}
void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, char* data)
void Dialogs::GSDumpDialog::ParseTransfer(wxTreeItemId& trootId, u8* data)
{
u64 tag = *(u64*)data;
u64 regs = *(u64*)(data + 8);
@@ -815,7 +915,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 GSData& event, u8* regs)
{
switch (event.id)
{
@@ -921,14 +1021,46 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
default:
break;
}
char regs[8192];
u8 regs[8192];
m_dump_file->Read(&crc, 4);
m_dump_file->Read(&ss, 4);
std::unique_ptr<char[]> state_data(new char[ss]);
std::unique_ptr<u8[]> state_data = std::make_unique<u8[]>(ss);
m_dump_file->Read(state_data.get(), ss);
// Pull serial out of new header, if present.
std::string serial;
if (crc == 0xFFFFFFFFu)
{
GSDumpHeader header;
if (ss < sizeof(header))
{
Console.Error("GSDump header is corrupted.");
GSDump::isRunning = false;
return;
}
std::memcpy(&header, state_data.get(), sizeof(header));
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.");
GSDump::isRunning = false;
return;
}
if (header.serial_size > 0)
serial.assign(reinterpret_cast<const char*>(state_data.get()) + header.serial_offset, header.serial_size);
}
// Read the real state data
ss = header.state_size;
state_data = std::make_unique<u8[]>(ss);
m_dump_file->Read(state_data.get(), ss);
}
m_dump_file->Read(&regs, 8192);
freezeData fd = {(int)ss, (u8*)state_data.get()};
@@ -956,7 +1088,9 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
size = 8192;
break;
}
std::unique_ptr<char[]> data(new char[size]);
// make_unique would zero the data out, which is pointless since we're reading it anyway,
// and this loop is executed a *ton* of times.
std::unique_ptr<u8[]> data(new u8[size]);
m_dump_file->Read(data.get(), size);
m_root_window->m_dump_packets.push_back({id, std::move(data), size, id_transfer});
}
@@ -974,7 +1108,17 @@ void Dialogs::GSDumpDialog::GSThread::ExecuteTaskInThread()
g_FrameCount = 0;
}
if (!GSopen(g_Conf->EmuOptions.GS, renderer, (u8*)regs))
Pcsx2Config::GSOptions config(g_Conf->EmuOptions.GS);
if (!serial.empty())
{
if (const GameDatabaseSchema::GameEntry* entry = GameDatabase::findGame(serial); entry)
{
// apply hardware fixes to config before opening (e.g. tex in rt)
entry->applyGSHardwareFixes(config);
}
}
if (!GSopen(config, renderer, regs))
{
OnStop();
return;
+3 -3
View File
@@ -239,7 +239,7 @@ namespace Dialogs
struct GSData
{
GSType id;
std::unique_ptr<char[]> data;
std::unique_ptr<u8[]> data;
int length;
GSTransferPath path;
};
@@ -260,11 +260,11 @@ namespace Dialogs
std::vector<wxTreeItemId> m_gif_items;
float m_stored_q = 1.0;
void ProcessDumpEvent(const GSData& event, char* regs);
void ProcessDumpEvent(const GSData& event, u8* regs);
u32 ReadPacketSize(const void* packet);
void GenPacketList();
void GenPacketInfo(GSData& dump);
void ParseTransfer(wxTreeItemId& id, char* data);
void ParseTransfer(wxTreeItemId& id, u8* data);
void ParseTreeReg(wxTreeItemId& id, GIFReg reg, u128 data, bool packed);
void ParseTreePrim(wxTreeItemId& id, u32 prim);
void CloseDump(wxCommandEvent& event);
+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");