mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
GS-HW: SW Render CLUT draws
This commit is contained in:
parent
614c535f5e
commit
bc2f576b30
@ -62,6 +62,7 @@ allowed_gs_hw_fixes = [
|
|||||||
"texturePreloading",
|
"texturePreloading",
|
||||||
"deinterlace",
|
"deinterlace",
|
||||||
"cpuSpriteRenderBW",
|
"cpuSpriteRenderBW",
|
||||||
|
"cpuCLUTRender",
|
||||||
"gpuPaletteConversion",
|
"gpuPaletteConversion",
|
||||||
]
|
]
|
||||||
gs_hw_fix_ranges = {
|
gs_hw_fix_ranges = {
|
||||||
@ -73,6 +74,7 @@ gs_hw_fix_ranges = {
|
|||||||
"roundSprite": (0, 2),
|
"roundSprite": (0, 2),
|
||||||
"deinterlace": (0, 7),
|
"deinterlace": (0, 7),
|
||||||
"cpuSpriteRenderBW": (1, 10),
|
"cpuSpriteRenderBW": (1, 10),
|
||||||
|
"cpuCLUTRender": (1, 2),
|
||||||
"gpuPaletteConversion": (0, 2),
|
"gpuPaletteConversion": (0, 2),
|
||||||
}
|
}
|
||||||
allowed_speed_hacks = ["mvuFlagSpeedHack", "InstantVU1SpeedHack", "MTVUSpeedHack"]
|
allowed_speed_hacks = ["mvuFlagSpeedHack", "InstantVU1SpeedHack", "MTVUSpeedHack"]
|
||||||
|
|||||||
@ -16837,6 +16837,8 @@ SLES-53556:
|
|||||||
region: "PAL-M3"
|
region: "PAL-M3"
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLES-53557:
|
SLES-53557:
|
||||||
name: "Need for Speed - Most Wanted"
|
name: "Need for Speed - Most Wanted"
|
||||||
region: "PAL-E"
|
region: "PAL-E"
|
||||||
@ -18021,6 +18023,8 @@ SLES-54027:
|
|||||||
region: "PAL-M3"
|
region: "PAL-M3"
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLES-54030:
|
SLES-54030:
|
||||||
name: "Black"
|
name: "Black"
|
||||||
region: "PAL-E"
|
region: "PAL-E"
|
||||||
@ -23037,6 +23041,8 @@ SLKA-25341:
|
|||||||
region: "NTSC-K"
|
region: "NTSC-K"
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLKA-25342:
|
SLKA-25342:
|
||||||
name: "Ryu ga Gotoku"
|
name: "Ryu ga Gotoku"
|
||||||
region: "NTSC-K"
|
region: "NTSC-K"
|
||||||
@ -31535,6 +31541,8 @@ SLPM-66567:
|
|||||||
region: "NTSC-J"
|
region: "NTSC-J"
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLPM-66568:
|
SLPM-66568:
|
||||||
name: "Brothers In Arms - Road to Hill 30 [Ubisoft Best]"
|
name: "Brothers In Arms - Road to Hill 30 [Ubisoft Best]"
|
||||||
region: "NTSC-J"
|
region: "NTSC-J"
|
||||||
@ -44695,6 +44703,8 @@ SLUS-21271:
|
|||||||
compat: 5
|
compat: 5
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLUS-21272:
|
SLUS-21272:
|
||||||
name: "Super Monkey Ball Adventure"
|
name: "Super Monkey Ball Adventure"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
@ -45419,6 +45429,8 @@ SLUS-21399:
|
|||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLUS-21400:
|
SLUS-21400:
|
||||||
name: "Monster House"
|
name: "Monster House"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
@ -48537,6 +48549,8 @@ SLUS-29185:
|
|||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
gameFixes:
|
gameFixes:
|
||||||
- BlitInternalFPSHack # Fixes internal FPS detection.
|
- BlitInternalFPSHack # Fixes internal FPS detection.
|
||||||
|
gsHWFixes:
|
||||||
|
cpuSpriteRenderBW: 2 # Fixes some bad textures.
|
||||||
SLUS-29188:
|
SLUS-29188:
|
||||||
name: "Steambot Chronicles [Regular Demo]"
|
name: "Steambot Chronicles [Regular Demo]"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
|
|||||||
@ -236,6 +236,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
|||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderBW, "EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderBW, "EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
|
||||||
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuCLUTRender, "EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawStart, "EmuCore/GS", "UserHacks_SkipDraw_Start", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawStart, "EmuCore/GS", "UserHacks_SkipDraw_Start", 0);
|
||||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawEnd, "EmuCore/GS", "UserHacks_SkipDraw_End", 0);
|
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawEnd, "EmuCore/GS", "UserHacks_SkipDraw_End", 0);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
|
||||||
@ -335,6 +336,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
|||||||
{
|
{
|
||||||
m_ui.upscalingFixesLayout->removeRow(2);
|
m_ui.upscalingFixesLayout->removeRow(2);
|
||||||
m_ui.hardwareFixesLayout->removeRow(2);
|
m_ui.hardwareFixesLayout->removeRow(2);
|
||||||
|
m_ui.hardwareFixesLayout->removeRow(1);
|
||||||
m_ui.skipDrawStart = nullptr;
|
m_ui.skipDrawStart = nullptr;
|
||||||
m_ui.skipDrawEnd = nullptr;
|
m_ui.skipDrawEnd = nullptr;
|
||||||
m_ui.textureOffsetX = nullptr;
|
m_ui.textureOffsetX = nullptr;
|
||||||
|
|||||||
@ -58,7 +58,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="hardwareRendererGroup">
|
<widget class="QTabWidget" name="hardwareRendererGroup">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="documentMode">
|
<property name="documentMode">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -388,7 +388,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="upscaleMultiplier" />
|
<widget class="QComboBox" name="upscaleMultiplier"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
@ -692,91 +692,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_12">
|
|
||||||
<property name="text">
|
|
||||||
<string>Skipdraw Range:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QSpinBox" name="skipDrawStart">
|
|
||||||
<property name="maximum">
|
|
||||||
<number>10000</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QSpinBox" name="skipDrawEnd">
|
|
||||||
<property name="maximum">
|
|
||||||
<number>10000</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="2">
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="hwAutoFlush">
|
|
||||||
<property name="text">
|
|
||||||
<string>Auto Flush</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="frameBufferConversion">
|
|
||||||
<property name="text">
|
|
||||||
<string>Frame Buffer Conversion</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="disableDepthEmulation">
|
|
||||||
<property name="text">
|
|
||||||
<string>Disable Depth Emulation</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QCheckBox" name="memoryWrapping">
|
|
||||||
<property name="text">
|
|
||||||
<string>Memory Wrapping</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QCheckBox" name="disableSafeFeatures">
|
|
||||||
<property name="text">
|
|
||||||
<string>Disable Safe Features</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QCheckBox" name="preloadFrameData">
|
|
||||||
<property name="text">
|
|
||||||
<string>Preload Frame Data</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QCheckBox" name="disablePartialInvalidation">
|
|
||||||
<property name="text">
|
|
||||||
<string>Disable Partial Invalidation</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QCheckBox" name="textureInsideRt">
|
|
||||||
<property name="text">
|
|
||||||
<string>Texture Inside RT</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_36">
|
<widget class="QLabel" name="label_36">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -843,6 +758,123 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="text">
|
||||||
|
<string>Skipdraw Range:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="skipDrawStart">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="skipDrawEnd">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>10000</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="hwAutoFlush">
|
||||||
|
<property name="text">
|
||||||
|
<string>Auto Flush</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="frameBufferConversion">
|
||||||
|
<property name="text">
|
||||||
|
<string>Frame Buffer Conversion</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="disableDepthEmulation">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable Depth Emulation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="memoryWrapping">
|
||||||
|
<property name="text">
|
||||||
|
<string>Memory Wrapping</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="disableSafeFeatures">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable Safe Features</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="preloadFrameData">
|
||||||
|
<property name="text">
|
||||||
|
<string>Preload Frame Data</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="disablePartialInvalidation">
|
||||||
|
<property name="text">
|
||||||
|
<string>Disable Partial Invalidation</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="textureInsideRt">
|
||||||
|
<property name="text">
|
||||||
|
<string>Texture Inside RT</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="cpuCLUTRender">
|
||||||
|
<property name="currentText">
|
||||||
|
<string extracomment="0 (Disabled)">0 (Disabled)</string>
|
||||||
|
</property>
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>0 (Disabled)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>1 (Normal)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>2 (Aggressive)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="text">
|
||||||
|
<string>Software CLUT Render</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QGroupBox" name="upscalingFixesTab">
|
<widget class="QGroupBox" name="upscalingFixesTab">
|
||||||
@ -1537,6 +1569,13 @@
|
|||||||
<string>Rendering</string>
|
<string>Rendering</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QFormLayout" name="formLayout_3">
|
<layout class="QFormLayout" name="formLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_37">
|
||||||
|
<property name="text">
|
||||||
|
<string>Texture Filtering:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QComboBox" name="swTextureFiltering">
|
<widget class="QComboBox" name="swTextureFiltering">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
@ -1573,10 +1612,10 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_37">
|
<widget class="QLabel" name="label_9">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Texture Filtering:</string>
|
<string>Extra Rendering Threads:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -1587,13 +1626,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_9">
|
|
||||||
<property name="text">
|
|
||||||
<string>Extra Rendering Threads:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="2" column="0" colspan="2">
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
|
|||||||
@ -585,6 +585,7 @@ struct Pcsx2Config
|
|||||||
int UserHacks_TCOffsetX{0};
|
int UserHacks_TCOffsetX{0};
|
||||||
int UserHacks_TCOffsetY{0};
|
int UserHacks_TCOffsetY{0};
|
||||||
int UserHacks_CPUSpriteRenderBW{0};
|
int UserHacks_CPUSpriteRenderBW{0};
|
||||||
|
int UserHacks_CPUCLUTRender{ 0 };
|
||||||
TriFiltering TriFilter{TriFiltering::Automatic};
|
TriFiltering TriFilter{TriFiltering::Automatic};
|
||||||
int OverrideTextureBarriers{-1};
|
int OverrideTextureBarriers{-1};
|
||||||
int OverrideGeometryShaders{-1};
|
int OverrideGeometryShaders{-1};
|
||||||
|
|||||||
@ -2683,6 +2683,7 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
|||||||
static constexpr const char* s_cpu_sprite_render_bw_options[] = {"0 (Disabled)", "1 (64 Max Width)", "2 (128 Max Width)",
|
static constexpr const char* s_cpu_sprite_render_bw_options[] = {"0 (Disabled)", "1 (64 Max Width)", "2 (128 Max Width)",
|
||||||
"3 (192 Max Width)", "4 (256 Max Width)", "5 (320 Max Width)", "6 (384 Max Width)", "7 (448 Max Width)",
|
"3 (192 Max Width)", "4 (256 Max Width)", "5 (320 Max Width)", "6 (384 Max Width)", "7 (448 Max Width)",
|
||||||
"8 (512 Max Width)", "9 (576 Max Width)", "10 (640 Max Width)"};
|
"8 (512 Max Width)", "9 (576 Max Width)", "10 (640 Max Width)"};
|
||||||
|
static constexpr const char* s_cpu_clut_render_options[] = { "0 (Disabled)", "1 (Normal)", "2 (Aggressive)" };
|
||||||
static constexpr const char* s_half_pixel_offset_options[] = {
|
static constexpr const char* s_half_pixel_offset_options[] = {
|
||||||
"Off (Default)", "Normal (Vertex)", "Special (Texture)", "Special (Texture - Aggressive)"};
|
"Off (Default)", "Normal (Vertex)", "Special (Texture)", "Special (Texture - Aggressive)"};
|
||||||
static constexpr const char* s_round_sprite_options[] = {"Off (Default)", "Half", "Full"};
|
static constexpr const char* s_round_sprite_options[] = {"Off (Default)", "Half", "Full"};
|
||||||
@ -2691,6 +2692,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
|||||||
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
|
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
|
||||||
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses sofware renderer to draw texture decompression-like sprites.",
|
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses sofware renderer to draw texture decompression-like sprites.",
|
||||||
"EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0, s_cpu_sprite_render_bw_options, std::size(s_cpu_sprite_render_bw_options));
|
"EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0, s_cpu_sprite_render_bw_options, std::size(s_cpu_sprite_render_bw_options));
|
||||||
|
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses sofware renderer to draw texture decompression-like sprites.",
|
||||||
|
"EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0, s_cpu_clut_render_options, std::size(s_cpu_clut_render_options));
|
||||||
DrawIntRangeSetting(
|
DrawIntRangeSetting(
|
||||||
bsi, "Skip Draw Start", "Object range to skip drawing.", "EmuCore/GS", "UserHacks_SkipDraw_Start", 0, 0, 5000);
|
bsi, "Skip Draw Start", "Object range to skip drawing.", "EmuCore/GS", "UserHacks_SkipDraw_Start", 0, 0, 5000);
|
||||||
DrawIntRangeSetting(bsi, "Skip Draw End", "Object range to skip drawing.", "EmuCore/GS", "UserHacks_SkipDraw_End", 0, 0, 5000);
|
DrawIntRangeSetting(bsi, "Skip Draw End", "Object range to skip drawing.", "EmuCore/GS", "UserHacks_SkipDraw_End", 0, 0, 5000);
|
||||||
|
|||||||
@ -309,6 +309,8 @@ void ImGuiManager::DrawSettingsOverlay()
|
|||||||
APPEND("TCO={}/{} ", GSConfig.UserHacks_TCOffsetX, GSConfig.UserHacks_TCOffsetY);
|
APPEND("TCO={}/{} ", GSConfig.UserHacks_TCOffsetX, GSConfig.UserHacks_TCOffsetY);
|
||||||
if (GSConfig.UserHacks_CPUSpriteRenderBW != 0)
|
if (GSConfig.UserHacks_CPUSpriteRenderBW != 0)
|
||||||
APPEND("CSBW={} ", GSConfig.UserHacks_CPUSpriteRenderBW);
|
APPEND("CSBW={} ", GSConfig.UserHacks_CPUSpriteRenderBW);
|
||||||
|
if (GSConfig.UserHacks_CPUCLUTRender != 0)
|
||||||
|
APPEND("CCD={} ", GSConfig.UserHacks_CPUCLUTRender);
|
||||||
if (GSConfig.SkipDrawStart != 0 || GSConfig.SkipDrawEnd != 0)
|
if (GSConfig.SkipDrawStart != 0 || GSConfig.SkipDrawEnd != 0)
|
||||||
APPEND("SD={}/{} ", GSConfig.SkipDrawStart, GSConfig.SkipDrawEnd);
|
APPEND("SD={}/{} ", GSConfig.SkipDrawStart, GSConfig.SkipDrawEnd);
|
||||||
if (GSConfig.UserHacks_TextureInsideRt)
|
if (GSConfig.UserHacks_TextureInsideRt)
|
||||||
|
|||||||
@ -834,7 +834,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
|||||||
GSConfig.UserHacks_DisableDepthSupport != old_config.UserHacks_DisableDepthSupport ||
|
GSConfig.UserHacks_DisableDepthSupport != old_config.UserHacks_DisableDepthSupport ||
|
||||||
GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation ||
|
GSConfig.UserHacks_DisablePartialInvalidation != old_config.UserHacks_DisablePartialInvalidation ||
|
||||||
GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt ||
|
GSConfig.UserHacks_TextureInsideRt != old_config.UserHacks_TextureInsideRt ||
|
||||||
GSConfig.UserHacks_CPUSpriteRenderBW != old_config.UserHacks_CPUSpriteRenderBW)
|
GSConfig.UserHacks_CPUSpriteRenderBW != old_config.UserHacks_CPUSpriteRenderBW ||
|
||||||
|
GSConfig.UserHacks_CPUSpriteRenderBW != old_config.UserHacks_CPUCLUTRender)
|
||||||
{
|
{
|
||||||
g_gs_renderer->PurgeTextureCache();
|
g_gs_renderer->PurgeTextureCache();
|
||||||
g_gs_renderer->PurgePool();
|
g_gs_renderer->PurgePool();
|
||||||
@ -1512,6 +1513,7 @@ void GSApp::Init()
|
|||||||
m_default_configuration["UserHacks_Disable_Safe_Features"] = "0";
|
m_default_configuration["UserHacks_Disable_Safe_Features"] = "0";
|
||||||
m_default_configuration["UserHacks_DisablePartialInvalidation"] = "0";
|
m_default_configuration["UserHacks_DisablePartialInvalidation"] = "0";
|
||||||
m_default_configuration["UserHacks_CPUSpriteRenderBW"] = "0";
|
m_default_configuration["UserHacks_CPUSpriteRenderBW"] = "0";
|
||||||
|
m_default_configuration["UserHacks_CPUCLUTRender"] = "0";
|
||||||
m_default_configuration["UserHacks_CPU_FB_Conversion"] = "0";
|
m_default_configuration["UserHacks_CPU_FB_Conversion"] = "0";
|
||||||
m_default_configuration["UserHacks_Half_Bottom_Override"] = "-1";
|
m_default_configuration["UserHacks_Half_Bottom_Override"] = "-1";
|
||||||
m_default_configuration["UserHacks_HalfPixelOffset"] = "0";
|
m_default_configuration["UserHacks_HalfPixelOffset"] = "0";
|
||||||
|
|||||||
@ -28,7 +28,7 @@ GSClut::GSClut(GSLocalMemory* mem)
|
|||||||
m_clut = (u16*)&p[0]; // 1k + 1k for mirrored area simulating wrapping memory
|
m_clut = (u16*)&p[0]; // 1k + 1k for mirrored area simulating wrapping memory
|
||||||
m_buff32 = (u32*)&p[2048]; // 1k
|
m_buff32 = (u32*)&p[2048]; // 1k
|
||||||
m_buff64 = (u64*)&p[4096]; // 2k
|
m_buff64 = (u64*)&p[4096]; // 2k
|
||||||
m_write.dirty = true;
|
m_write.dirty = 1;
|
||||||
m_read.dirty = true;
|
m_read.dirty = true;
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
@ -103,34 +103,35 @@ GSClut::~GSClut()
|
|||||||
vmfree(m_clut, CLUT_ALLOC_SIZE);
|
vmfree(m_clut, CLUT_ALLOC_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSClut::Invalidate()
|
u8 GSClut::IsInvalid()
|
||||||
{
|
{
|
||||||
m_write.dirty = true;
|
return m_write.dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSClut::InvalidateRange(u32 start_block, u32 end_block)
|
u32 GSClut::GetCLUTCBP()
|
||||||
{
|
{
|
||||||
u32 blocks = 4;
|
return m_write.TEX0.CBP;
|
||||||
|
|
||||||
if (GSLocalMemory::m_psm[m_write.TEX0.CPSM].bpp == 16)
|
|
||||||
blocks >>= 1;
|
|
||||||
|
|
||||||
if (GSLocalMemory::m_psm[m_write.TEX0.PSM].bpp == 4)
|
|
||||||
blocks >>= 1;
|
|
||||||
|
|
||||||
if ((m_write.TEX0.CBP + blocks) >= start_block && m_write.TEX0.CBP <= end_block)
|
|
||||||
{
|
|
||||||
m_write.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the whole page, if the CLUT is slightly offset from a page boundary it could miss it.
|
void GSClut::SetNextCLUTTEX0(u64 TEX0)
|
||||||
void GSClut::Invalidate(u32 block)
|
|
||||||
{
|
{
|
||||||
if (!((block ^ m_write.TEX0.CBP) & ~0x1F))
|
m_write.next_tex0 = TEX0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GSClut::InvalidateRange(u32 start_block, u32 end_block, bool is_draw)
|
||||||
|
{
|
||||||
|
if (m_write.dirty)
|
||||||
|
return m_write.dirty;
|
||||||
|
|
||||||
|
GIFRegTEX0 next_cbp;
|
||||||
|
next_cbp.U64 = m_write.next_tex0;
|
||||||
|
|
||||||
|
if ((next_cbp.CBP + 3) >= start_block && end_block >= next_cbp.CBP)
|
||||||
{
|
{
|
||||||
m_write.dirty = true;
|
m_write.dirty |= is_draw ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return m_write.dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GSClut::WriteTest(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT)
|
bool GSClut::WriteTest(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT)
|
||||||
@ -163,14 +164,14 @@ bool GSClut::WriteTest(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT)
|
|||||||
m_CBP[1] = TEX0.CBP;
|
m_CBP[1] = TEX0.CBP;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
return false; // ffx2 menu
|
return false; // ffx2 menu.
|
||||||
case 7:
|
case 7:
|
||||||
return false; // ford mustang racing // Bouken Jidai Katsugeki Goemon
|
return false; // ford mustang racing // Bouken Jidai Katsugeki Goemon.
|
||||||
default:
|
default:
|
||||||
__assume(0);
|
__assume(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLUT only reloads if PSM is a valid index type, avoid unnecessary flushes
|
// CLUT only reloads if PSM is a valid index type, avoid unnecessary flushes.
|
||||||
return m_write.IsDirty(TEX0, TEXCLUT);
|
return m_write.IsDirty(TEX0, TEXCLUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ void GSClut::Write(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT)
|
|||||||
m_write.TEX0 = TEX0;
|
m_write.TEX0 = TEX0;
|
||||||
m_write.TEXCLUT = TEXCLUT;
|
m_write.TEXCLUT = TEXCLUT;
|
||||||
m_read.dirty = true;
|
m_read.dirty = true;
|
||||||
m_write.dirty = false;
|
m_write.dirty = 0;
|
||||||
|
|
||||||
(this->*m_wc[TEX0.CSM][TEX0.CPSM][TEX0.PSM])(TEX0, TEXCLUT);
|
(this->*m_wc[TEX0.CSM][TEX0.CPSM][TEX0.PSM])(TEX0, TEXCLUT);
|
||||||
}
|
}
|
||||||
@ -775,7 +776,7 @@ bool GSClut::WriteState::IsDirty(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TE
|
|||||||
|
|
||||||
bool is_dirty = dirty;
|
bool is_dirty = dirty;
|
||||||
|
|
||||||
if (((this->TEX0.U64 ^ TEX0.U64) & mask) || (GSLocalMemory::m_psm[this->TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp))
|
if (((this->TEX0.U64 ^ TEX0.U64) & mask) || (GSLocalMemory::m_psm[this->TEX0.PSM].pal != GSLocalMemory::m_psm[TEX0.PSM].pal))
|
||||||
is_dirty |= true;
|
is_dirty |= true;
|
||||||
else if (TEX0.CSM == 1 && (TEXCLUT.U32[0] ^ this->TEXCLUT.U32[0]))
|
else if (TEX0.CSM == 1 && (TEXCLUT.U32[0] ^ this->TEXCLUT.U32[0]))
|
||||||
is_dirty |= true;
|
is_dirty |= true;
|
||||||
@ -795,7 +796,7 @@ bool GSClut::ReadState::IsDirty(const GIFRegTEX0& TEX0)
|
|||||||
|
|
||||||
bool is_dirty = dirty;
|
bool is_dirty = dirty;
|
||||||
|
|
||||||
if (((this->TEX0.U64 ^ TEX0.U64) & mask) || (GSLocalMemory::m_psm[this->TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp))
|
if (((this->TEX0.U64 ^ TEX0.U64) & mask) || (GSLocalMemory::m_psm[this->TEX0.PSM].pal != GSLocalMemory::m_psm[TEX0.PSM].pal))
|
||||||
is_dirty |= true;
|
is_dirty |= true;
|
||||||
|
|
||||||
if (!is_dirty)
|
if (!is_dirty)
|
||||||
@ -814,7 +815,7 @@ bool GSClut::ReadState::IsDirty(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA)
|
|||||||
|
|
||||||
bool is_dirty = dirty;
|
bool is_dirty = dirty;
|
||||||
|
|
||||||
if (((this->TEX0.U64 ^ TEX0.U64) & tex0_mask) || (GSLocalMemory::m_psm[this->TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp))
|
if (((this->TEX0.U64 ^ TEX0.U64) & tex0_mask) || (GSLocalMemory::m_psm[this->TEX0.PSM].pal != GSLocalMemory::m_psm[TEX0.PSM].pal))
|
||||||
is_dirty |= true;
|
is_dirty |= true;
|
||||||
else // Just to optimise the checks.
|
else // Just to optimise the checks.
|
||||||
{
|
{
|
||||||
|
|||||||
@ -39,7 +39,8 @@ class alignas(32) GSClut : public GSAlignedClass<32>
|
|||||||
{
|
{
|
||||||
GIFRegTEX0 TEX0;
|
GIFRegTEX0 TEX0;
|
||||||
GIFRegTEXCLUT TEXCLUT;
|
GIFRegTEXCLUT TEXCLUT;
|
||||||
bool dirty;
|
u8 dirty;
|
||||||
|
u64 next_tex0;
|
||||||
bool IsDirty(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
|
bool IsDirty(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
|
||||||
} m_write;
|
} m_write;
|
||||||
|
|
||||||
@ -100,9 +101,10 @@ public:
|
|||||||
GSClut(GSLocalMemory* mem);
|
GSClut(GSLocalMemory* mem);
|
||||||
virtual ~GSClut();
|
virtual ~GSClut();
|
||||||
|
|
||||||
void Invalidate();
|
bool InvalidateRange(u32 start_block, u32 end_block, bool is_draw = false);
|
||||||
void Invalidate(u32 block);
|
u8 IsInvalid();
|
||||||
void InvalidateRange(u32 start_block, u32 end_block);
|
u32 GetCLUTCBP();
|
||||||
|
void SetNextCLUTTEX0(u64 CBP);
|
||||||
bool WriteTest(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
|
bool WriteTest(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
|
||||||
void Write(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
|
void Write(const GIFRegTEX0& TEX0, const GIFRegTEXCLUT& TEXCLUT);
|
||||||
//void Read(const GIFRegTEX0& TEX0);
|
//void Read(const GIFRegTEX0& TEX0);
|
||||||
|
|||||||
@ -444,6 +444,7 @@ public:
|
|||||||
typedef u32 (GSLocalMemory::*readTexel)(int x, int y, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA) const;
|
typedef u32 (GSLocalMemory::*readTexel)(int x, int y, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA) const;
|
||||||
typedef void (GSLocalMemory::*writePixelAddr)(u32 addr, u32 c);
|
typedef void (GSLocalMemory::*writePixelAddr)(u32 addr, u32 c);
|
||||||
typedef void (GSLocalMemory::*writeFrameAddr)(u32 addr, u32 c);
|
typedef void (GSLocalMemory::*writeFrameAddr)(u32 addr, u32 c);
|
||||||
|
typedef u32(GSLocalMemory::*PixelAddr)(int x, int y, u32 bp, u32 bw) const;
|
||||||
typedef u32 (GSLocalMemory::*readPixelAddr)(u32 addr) const;
|
typedef u32 (GSLocalMemory::*readPixelAddr)(u32 addr) const;
|
||||||
typedef u32 (GSLocalMemory::*readTexelAddr)(u32 addr, const GIFRegTEXA& TEXA) const;
|
typedef u32 (GSLocalMemory::*readTexelAddr)(u32 addr, const GIFRegTEXA& TEXA) const;
|
||||||
typedef void (GSLocalMemory::*writeImage)(int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
|
typedef void (GSLocalMemory::*writeImage)(int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG);
|
||||||
@ -531,17 +532,6 @@ public:
|
|||||||
GSPixelOffset4* GetPixelOffset4(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
|
GSPixelOffset4* GetPixelOffset4(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
|
||||||
std::vector<GSVector2i>* GetPage2TileMap(const GIFRegTEX0& TEX0);
|
std::vector<GSVector2i>* GetPage2TileMap(const GIFRegTEX0& TEX0);
|
||||||
|
|
||||||
static u32 GetEndBlock(int bp, int bw, int w, int h, int psm)
|
|
||||||
{
|
|
||||||
const GSLocalMemory::psm_t& dpsm = GSLocalMemory::m_psm[psm];
|
|
||||||
const int page_width = std::max(1, w / dpsm.pgs.x);
|
|
||||||
const int page_height = std::max(1, h / dpsm.pgs.y);
|
|
||||||
const int pitch = (std::max(1, bw) * 64) / dpsm.pgs.x;
|
|
||||||
const u32 end_bp = bp + ((((page_height % dpsm.pgs.y) != 0) ? (page_width << 5) : 0) + ((page_height * pitch) << 5));
|
|
||||||
|
|
||||||
return end_bp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// address
|
// address
|
||||||
|
|
||||||
static u32 BlockNumber32(int x, int y, u32 bp, u32 bw)
|
static u32 BlockNumber32(int x, int y, u32 bp, u32 bw)
|
||||||
|
|||||||
@ -777,16 +777,8 @@ __inline void GSState::CheckFlushes()
|
|||||||
if (m_dirty_gs_regs && m_index.tail > 0)
|
if (m_dirty_gs_regs && m_index.tail > 0)
|
||||||
{
|
{
|
||||||
if (TestDrawChanged())
|
if (TestDrawChanged())
|
||||||
{
|
|
||||||
Flush(GSFlushReason::CONTEXTCHANGE);
|
Flush(GSFlushReason::CONTEXTCHANGE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ((m_context->FRAME.FBMSK & GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk) != GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk)
|
|
||||||
m_mem.m_clut.Invalidate(m_context->FRAME.Block());
|
|
||||||
|
|
||||||
// Hey, why not check? I mean devs have done crazier things..
|
|
||||||
if(!m_context->ZBUF.ZMSK)
|
|
||||||
m_mem.m_clut.Invalidate(m_context->ZBUF.Block());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSState::GIFPackedRegHandlerNull(const GIFPackedReg* RESTRICT r)
|
void GSState::GIFPackedRegHandlerNull(const GIFPackedReg* RESTRICT r)
|
||||||
@ -1073,13 +1065,14 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
|||||||
|
|
||||||
GL_REG("Apply TEX0_%d = 0x%x_%x", i, TEX0.U32[1], TEX0.U32[0]);
|
GL_REG("Apply TEX0_%d = 0x%x_%x", i, TEX0.U32[1], TEX0.U32[0]);
|
||||||
|
|
||||||
// even if TEX0 did not change, a new palette may have been uploaded and will overwrite the currently queued for drawing
|
// Even if TEX0 did not change, a new palette may have been uploaded and will overwrite the currently queued for drawing.
|
||||||
const bool wt = m_mem.m_clut.WriteTest(TEX0, m_env.TEXCLUT);
|
const bool wt = m_mem.m_clut.WriteTest(TEX0, m_env.TEXCLUT);
|
||||||
|
|
||||||
// clut loading already covered with WriteTest, for drawing only have to check CPSM and CSA (MGS3 intro skybox would be drawn piece by piece without this)
|
|
||||||
|
|
||||||
if (wt)
|
if (wt)
|
||||||
|
{
|
||||||
|
m_mem.m_clut.SetNextCLUTTEX0(TEX0.U64);
|
||||||
Flush(GSFlushReason::CLUTCHANGE);
|
Flush(GSFlushReason::CLUTCHANGE);
|
||||||
|
}
|
||||||
|
|
||||||
TEX0.CPSM &= 0xa; // 1010b
|
TEX0.CPSM &= 0xa; // 1010b
|
||||||
|
|
||||||
@ -1097,7 +1090,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
|||||||
{
|
{
|
||||||
BITBLTBUF.SBP = TEX0.CBP;
|
BITBLTBUF.SBP = TEX0.CBP;
|
||||||
BITBLTBUF.SBW = 1;
|
BITBLTBUF.SBW = 1;
|
||||||
BITBLTBUF.SPSM = TEX0.CSM;
|
BITBLTBUF.SPSM = TEX0.CPSM;
|
||||||
|
|
||||||
r.left = 0;
|
r.left = 0;
|
||||||
r.top = 0;
|
r.top = 0;
|
||||||
@ -1106,12 +1099,13 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
|||||||
|
|
||||||
int blocks = 4;
|
int blocks = 4;
|
||||||
|
|
||||||
if (GSLocalMemory::m_psm[TEX0.CPSM].bpp == 16)
|
if (GSLocalMemory::m_psm[TEX0.CPSM].trbpp == 16)
|
||||||
blocks >>= 1;
|
blocks >>= 1;
|
||||||
|
|
||||||
if (GSLocalMemory::m_psm[TEX0.PSM].bpp == 4)
|
if (GSLocalMemory::m_psm[TEX0.PSM].trbpp == 4)
|
||||||
blocks >>= 1;
|
blocks >>= 1;
|
||||||
|
|
||||||
|
// Invalidating videomem is slow, so *only* do it when it's definitely a CLUT draw in HW mode.
|
||||||
for (int j = 0; j < blocks; j++, BITBLTBUF.SBP++)
|
for (int j = 0; j < blocks; j++, BITBLTBUF.SBP++)
|
||||||
InvalidateLocalMem(BITBLTBUF, r, true);
|
InvalidateLocalMem(BITBLTBUF, r, true);
|
||||||
}
|
}
|
||||||
@ -1119,7 +1113,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
|||||||
{
|
{
|
||||||
BITBLTBUF.SBP = TEX0.CBP;
|
BITBLTBUF.SBP = TEX0.CBP;
|
||||||
BITBLTBUF.SBW = m_env.TEXCLUT.CBW;
|
BITBLTBUF.SBW = m_env.TEXCLUT.CBW;
|
||||||
BITBLTBUF.SPSM = TEX0.CSM;
|
BITBLTBUF.SPSM = TEX0.CPSM;
|
||||||
|
|
||||||
r.left = m_env.TEXCLUT.COU;
|
r.left = m_env.TEXCLUT.COU;
|
||||||
r.top = m_env.TEXCLUT.COV;
|
r.top = m_env.TEXCLUT.COV;
|
||||||
@ -1148,8 +1142,6 @@ void GSState::GIFRegHandlerTEX0(const GIFReg* RESTRICT r)
|
|||||||
GL_REG("TEX0_%d = 0x%x_%x", i, r->U32[1], r->U32[0]);
|
GL_REG("TEX0_%d = 0x%x_%x", i, r->U32[1], r->U32[0]);
|
||||||
|
|
||||||
GIFRegTEX0 TEX0 = r->TEX0;
|
GIFRegTEX0 TEX0 = r->TEX0;
|
||||||
GIFRegMIPTBP1 temp_MIPTBP1;
|
|
||||||
bool MTBAReloaded = false;
|
|
||||||
// Max allowed MTBA size for 32bit swizzled textures (including 8H 4HL etc) is 512, 16bit and normal 8/4bit formats can be 1024
|
// Max allowed MTBA size for 32bit swizzled textures (including 8H 4HL etc) is 512, 16bit and normal 8/4bit formats can be 1024
|
||||||
const u32 maxTex = (GSLocalMemory::m_psm[TEX0.PSM].bpp < 32) ? 10 : 9;
|
const u32 maxTex = (GSLocalMemory::m_psm[TEX0.PSM].bpp < 32) ? 10 : 9;
|
||||||
|
|
||||||
@ -1174,6 +1166,7 @@ void GSState::GIFRegHandlerTEX0(const GIFReg* RESTRICT r)
|
|||||||
// Format must be a color, Z formats do not trigger MTBA (but are valid for Mipmapping)
|
// Format must be a color, Z formats do not trigger MTBA (but are valid for Mipmapping)
|
||||||
if (m_env.CTXT[i].TEX1.MTBA && TEX0.TW >= 5 && TEX0.TW <= maxTex && (TEX0.PSM & 0x30) != 0x30)
|
if (m_env.CTXT[i].TEX1.MTBA && TEX0.TW >= 5 && TEX0.TW <= maxTex && (TEX0.PSM & 0x30) != 0x30)
|
||||||
{
|
{
|
||||||
|
GIFRegMIPTBP1& mip_tbp1 = m_env.CTXT[i].MIPTBP1;
|
||||||
// NOTE 1: TEX1.MXL must not be automatically set to 3 here and it has no effect on MTBA.
|
// NOTE 1: TEX1.MXL must not be automatically set to 3 here and it has no effect on MTBA.
|
||||||
// NOTE 2: Mipmap levels are packed with a minimum distance between them of 1 block, even down at 4bit textures under 16x16.
|
// NOTE 2: Mipmap levels are packed with a minimum distance between them of 1 block, even down at 4bit textures under 16x16.
|
||||||
// NOTE 3: Everything is derrived from the width of the texture, TBW and TH are completely ignored (useful for handling non-rectangular ones)
|
// NOTE 3: Everything is derrived from the width of the texture, TBW and TH are completely ignored (useful for handling non-rectangular ones)
|
||||||
@ -1190,39 +1183,32 @@ void GSState::GIFRegHandlerTEX0(const GIFReg* RESTRICT r)
|
|||||||
bw = std::max<u32>(bw >> 1, 1);
|
bw = std::max<u32>(bw >> 1, 1);
|
||||||
tex_size = std::max<u32>(tex_size >> 2, 1);
|
tex_size = std::max<u32>(tex_size >> 2, 1);
|
||||||
|
|
||||||
temp_MIPTBP1.TBP1 = bp;
|
mip_tbp1.TBP1 = bp;
|
||||||
temp_MIPTBP1.TBW1 = bw;
|
mip_tbp1.TBW1 = bw;
|
||||||
|
|
||||||
bp += tex_size;
|
bp += tex_size;
|
||||||
bw = std::max<u32>(bw >> 1, 1);
|
bw = std::max<u32>(bw >> 1, 1);
|
||||||
tex_size = std::max<u32>(tex_size >> 2, 1);
|
tex_size = std::max<u32>(tex_size >> 2, 1);
|
||||||
|
|
||||||
temp_MIPTBP1.TBP2 = bp;
|
mip_tbp1.TBP2 = bp;
|
||||||
temp_MIPTBP1.TBW2 = bw;
|
mip_tbp1.TBW2 = bw;
|
||||||
|
|
||||||
bp += tex_size;
|
bp += tex_size;
|
||||||
bw = std::max<u32>(bw >> 1, 1);
|
bw = std::max<u32>(bw >> 1, 1);
|
||||||
|
|
||||||
temp_MIPTBP1.TBP3 = bp;
|
mip_tbp1.TBP3 = bp;
|
||||||
temp_MIPTBP1.TBW3 = bw;
|
mip_tbp1.TBW3 = bw;
|
||||||
|
|
||||||
MTBAReloaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplyTEX0<i>(TEX0);
|
|
||||||
|
|
||||||
if (MTBAReloaded)
|
|
||||||
{
|
|
||||||
m_env.CTXT[i].MIPTBP1 = temp_MIPTBP1;
|
|
||||||
|
|
||||||
if (i == m_prev_env.PRIM.CTXT)
|
if (i == m_prev_env.PRIM.CTXT)
|
||||||
{
|
{
|
||||||
if (m_prev_env.CTXT[i].MIPTBP1.U64 ^ m_env.CTXT[i].MIPTBP1.U64)
|
if (m_prev_env.CTXT[i].MIPTBP1.U64 ^ mip_tbp1.U64)
|
||||||
m_dirty_gs_regs |= (1 << DIRTY_REG_MIPTBP1);
|
m_dirty_gs_regs |= (1 << DIRTY_REG_MIPTBP1);
|
||||||
else
|
else
|
||||||
m_dirty_gs_regs &= ~(1 << DIRTY_REG_MIPTBP1);
|
m_dirty_gs_regs &= ~(1 << DIRTY_REG_MIPTBP1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApplyTEX0<i>(TEX0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int i>
|
template <int i>
|
||||||
@ -2009,16 +1995,19 @@ void GSState::Write(const u8* mem, int len)
|
|||||||
|
|
||||||
GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
|
GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
|
||||||
|
|
||||||
const u32 write_end_bp = GSLocalMemory::GetEndBlock(blit.DBP, blit.DBW, w + static_cast<int>(m_env.TRXPOS.DSAX), h + static_cast<int>(m_env.TRXPOS.DSAY), blit.DPSM);
|
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[prev_tex0.PSM];
|
||||||
const u32 tex_end_bp = GSLocalMemory::GetEndBlock(prev_tex0.TBP0, prev_tex0.TBW, 1 << prev_tex0.TW, 1 << prev_tex0.TH, prev_tex0.PSM);
|
|
||||||
|
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW); // (m_mem.*psm.pa)(static_cast<int>(m_env.TRXPOS.DSAX), static_cast<int>(m_env.TRXPOS.DSAY), blit.DBP, blit.DBW) >> 6;
|
||||||
|
const u32 write_end_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, blit.DBP, blit.DBW); // (m_mem.*psm.pa)(w + static_cast<int>(m_env.TRXPOS.DSAX) - 1, h + static_cast<int>(m_env.TRXPOS.DSAY) - 1, blit.DBP, blit.DBW) >> 6;
|
||||||
|
const u32 tex_end_bp = m_mem.m_psm[prev_tex0.PSM].info.bn((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW); // (m_mem.*psm.pa)((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW) >> 6;
|
||||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||||
if (m_tr.end == 0 && m_index.tail > 0 && m_prev_env.PRIM.TME && write_end_bp >= prev_tex0.TBP0 && blit.DBP <= tex_end_bp)
|
if (m_tr.end == 0 && m_index.tail > 0 && m_prev_env.PRIM.TME && write_end_bp > prev_tex0.TBP0 && write_start_bp <= tex_end_bp)
|
||||||
{
|
{
|
||||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||||
}
|
}
|
||||||
// Invalid the CLUT if it crosses paths.
|
// Invalid the CLUT if it crosses paths.
|
||||||
m_mem.m_clut.InvalidateRange(blit.DBP, write_end_bp);
|
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||||
|
|
||||||
GL_CACHE("Write! ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)",
|
GL_CACHE("Write! ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)",
|
||||||
blit.DBP, blit.DBW, psm_str(blit.DPSM),
|
blit.DBP, blit.DBW, psm_str(blit.DPSM),
|
||||||
@ -2156,17 +2145,20 @@ void GSState::Move()
|
|||||||
|
|
||||||
GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
|
GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
|
||||||
|
|
||||||
const u32 end_bp = GSLocalMemory::GetEndBlock(dbp, dbw, w + static_cast<int>(m_env.TRXPOS.DSAX), h + static_cast<int>(m_env.TRXPOS.DSAY), m_env.BITBLTBUF.DPSM);
|
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[prev_tex0.PSM];
|
||||||
const u32 tex_end_bp = GSLocalMemory::GetEndBlock(prev_tex0.TBP0, prev_tex0.TBW, 1 << prev_tex0.TW, 1 << prev_tex0.TH, prev_tex0.PSM);
|
|
||||||
|
|
||||||
|
const u32 write_start_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, dbp, dbw); // (m_mem.*dpsm.pa)(static_cast<int>(m_env.TRXPOS.DSAX), static_cast<int>(m_env.TRXPOS.DSAY), dbp, dbw) >> 6;
|
||||||
|
const u32 write_end_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, dbp, dbw); // (m_mem.*dpsm.pa)(w + static_cast<int>(m_env.TRXPOS.DSAX) - 1, h + static_cast<int>(m_env.TRXPOS.DSAY) - 1, dbp, dbw) >> 6;
|
||||||
|
const u32 tex_end_bp = m_mem.m_psm[prev_tex0.PSM].info.bn((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW); // (m_mem.*dpsm.pa)((1 << prev_tex0.TW) - 1, (1 << prev_tex0.TH) - 1, prev_tex0.TBP0, prev_tex0.TBW) >> 6;
|
||||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||||
// Unknown if games use this one, but best to be safe.
|
// Unknown if games use this one, but best to be safe.
|
||||||
if (m_index.tail > 0 && m_prev_env.PRIM.TME && end_bp >= prev_tex0.TBP0 && dbp <= static_cast<int>(tex_end_bp))
|
|
||||||
|
if (m_index.tail > 0 && m_prev_env.PRIM.TME && write_end_bp >= prev_tex0.TBP0 && write_start_bp <= tex_end_bp)
|
||||||
{
|
{
|
||||||
Flush(GSFlushReason::LOCALTOLOCALMOVE);
|
Flush(GSFlushReason::LOCALTOLOCALMOVE);
|
||||||
}
|
}
|
||||||
// Invalid the CLUT if it crosses paths.
|
// Invalid the CLUT if it crosses paths.
|
||||||
m_mem.m_clut.InvalidateRange(dbp, end_bp);
|
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||||
|
|
||||||
auto genericCopy = [=](const GSOffset& dpo, const GSOffset& spo, auto&& getPAHelper, auto&& pxCopyFn)
|
auto genericCopy = [=](const GSOffset& dpo, const GSOffset& spo, auto&& getPAHelper, auto&& pxCopyFn)
|
||||||
{
|
{
|
||||||
@ -2957,19 +2949,70 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
|
|||||||
|
|
||||||
__forceinline bool GSState::IsAutoFlushDraw()
|
__forceinline bool GSState::IsAutoFlushDraw()
|
||||||
{
|
{
|
||||||
|
if (!PRIM->TME)
|
||||||
|
return false;
|
||||||
|
|
||||||
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
|
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
|
||||||
const bool frame_hit = (m_context->FRAME.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
|
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
|
||||||
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
|
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
|
||||||
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
|
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
|
||||||
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
|
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
|
||||||
const u32 frame_z_bp = frame_hit ? m_context->FRAME.Block() : m_context->ZBUF.Block();
|
const u32 frame_z_bp = frame_hit ? m_context->FRAME.Block() : m_context->ZBUF.Block();
|
||||||
|
|
||||||
if (PRIM->TME && (frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM))
|
if ((frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__forceinline void GSState::CLUTAutoFlush()
|
||||||
|
{
|
||||||
|
if (m_mem.m_clut.IsInvalid() & 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int n = 1;
|
||||||
|
|
||||||
|
switch (PRIM->PRIM)
|
||||||
|
{
|
||||||
|
case GS_POINTLIST:
|
||||||
|
n = 1;
|
||||||
|
break;
|
||||||
|
case GS_LINELIST:
|
||||||
|
case GS_LINESTRIP:
|
||||||
|
case GS_SPRITE:
|
||||||
|
n = 2;
|
||||||
|
break;
|
||||||
|
case GS_TRIANGLELIST:
|
||||||
|
case GS_TRIANGLESTRIP:
|
||||||
|
n = 3;
|
||||||
|
break;
|
||||||
|
case GS_TRIANGLEFAN:
|
||||||
|
n = 3;
|
||||||
|
break;
|
||||||
|
case GS_INVALID:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m_index.tail > 0 || (m_vertex.tail == n-1)) && (GSLocalMemory::m_psm[m_context->TEX0.PSM].pal == 0 || !PRIM->TME))
|
||||||
|
{
|
||||||
|
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||||
|
|
||||||
|
if ((m_context->FRAME.FBMSK & psm.fmsk) != psm.fmsk)
|
||||||
|
{
|
||||||
|
const u32 startbp = psm.info.bn(temp_draw_rect.x, temp_draw_rect.y, m_context->FRAME.Block(), m_context->FRAME.FBW);
|
||||||
|
|
||||||
|
// If it's a point, then we only have one coord, so the address for start and end will be the same, which is bad for the following check.
|
||||||
|
u32 endbp = startbp;
|
||||||
|
// otherwise calculate the end.
|
||||||
|
if (PRIM->PRIM != GS_POINTLIST || (m_index.tail > 1))
|
||||||
|
endbp = psm.info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, m_context->FRAME.Block(), m_context->FRAME.FBW);
|
||||||
|
|
||||||
|
m_mem.m_clut.InvalidateRange(startbp, endbp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
__forceinline void GSState::HandleAutoFlush()
|
__forceinline void GSState::HandleAutoFlush()
|
||||||
{
|
{
|
||||||
// Kind of a cheat, making the assumption that 2 consecutive fan/strip triangles won't overlap each other (*should* be safe)
|
// Kind of a cheat, making the assumption that 2 consecutive fan/strip triangles won't overlap each other (*should* be safe)
|
||||||
@ -3190,7 +3233,7 @@ __forceinline void GSState::VertexKick(u32 skip)
|
|||||||
|
|
||||||
ASSERT(m_vertex.tail < m_vertex.maxcount + 3);
|
ASSERT(m_vertex.tail < m_vertex.maxcount + 3);
|
||||||
|
|
||||||
if (auto_flush && m_index.tail > 0 && ((m_vertex.tail + 1) - m_vertex.head) >= n)
|
if (auto_flush && skip == 0 && m_index.tail > 0 && ((m_vertex.tail + 1) - m_vertex.head) >= n)
|
||||||
{
|
{
|
||||||
HandleAutoFlush();
|
HandleAutoFlush();
|
||||||
}
|
}
|
||||||
@ -3403,6 +3446,39 @@ __forceinline void GSState::VertexKick(u32 skip)
|
|||||||
default:
|
default:
|
||||||
__assume(0);
|
__assume(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GSVector4i draw_coord;
|
||||||
|
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
const GSVertex* v = &m_vertex.buff[m_index.buff[(m_index.tail - n) + i]];
|
||||||
|
draw_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
|
||||||
|
draw_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
|
||||||
|
|
||||||
|
if (m_vertex.tail == n && i == 0)
|
||||||
|
{
|
||||||
|
const GSVector4i scissor = GSVector4i(m_context->scissor.in);
|
||||||
|
|
||||||
|
temp_draw_rect.x = draw_coord.x;
|
||||||
|
temp_draw_rect.y = draw_coord.y;
|
||||||
|
temp_draw_rect = temp_draw_rect.xyxy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_draw_rect.x = std::min(draw_coord.x, temp_draw_rect.x);
|
||||||
|
temp_draw_rect.y = std::min(draw_coord.y, temp_draw_rect.y);
|
||||||
|
temp_draw_rect.z = std::max(draw_coord.x, temp_draw_rect.z);
|
||||||
|
temp_draw_rect.w = std::max(draw_coord.y, temp_draw_rect.w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GSVector4i scissor = GSVector4i(m_context->scissor.in);
|
||||||
|
temp_draw_rect.rintersect(scissor);
|
||||||
|
|
||||||
|
CLUTAutoFlush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if region repeat is used (applying it does something to at least one of the values in min...max)
|
/// Checks if region repeat is used (applying it does something to at least one of the values in min...max)
|
||||||
|
|||||||
@ -186,6 +186,7 @@ protected:
|
|||||||
void GrowVertexBuffer();
|
void GrowVertexBuffer();
|
||||||
bool IsAutoFlushDraw();
|
bool IsAutoFlushDraw();
|
||||||
void HandleAutoFlush();
|
void HandleAutoFlush();
|
||||||
|
void CLUTAutoFlush();
|
||||||
|
|
||||||
template <u32 prim, bool auto_flush, bool index_swap>
|
template <u32 prim, bool auto_flush, bool index_swap>
|
||||||
void VertexKick(u32 skip);
|
void VertexKick(u32 skip);
|
||||||
@ -228,6 +229,7 @@ public:
|
|||||||
GSDrawingEnvironment m_env;
|
GSDrawingEnvironment m_env;
|
||||||
GSDrawingEnvironment m_backup_env;
|
GSDrawingEnvironment m_backup_env;
|
||||||
GSDrawingEnvironment m_prev_env;
|
GSDrawingEnvironment m_prev_env;
|
||||||
|
GSVector4i temp_draw_rect;
|
||||||
GSDrawingContext* m_context;
|
GSDrawingContext* m_context;
|
||||||
u32 m_crc;
|
u32 m_crc;
|
||||||
CRC::Game m_game;
|
CRC::Game m_game;
|
||||||
|
|||||||
@ -1275,6 +1275,7 @@ void GSRendererHW::Draw()
|
|||||||
s = StringUtil::StdStringFromFormat("%05d_vertex.txt", s_n);
|
s = StringUtil::StdStringFromFormat("%05d_vertex.txt", s_n);
|
||||||
DumpVertices(m_dump_root + s);
|
DumpVertices(m_dump_root + s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsBadFrame())
|
if (IsBadFrame())
|
||||||
{
|
{
|
||||||
GL_INS("Warning skipping a draw call (%d)", s_n);
|
GL_INS("Warning skipping a draw call (%d)", s_n);
|
||||||
@ -1390,6 +1391,20 @@ void GSRendererHW::Draw()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SW CLUT Render enable.
|
||||||
|
if (GSConfig.UserHacks_CPUCLUTRender > 0)
|
||||||
|
{
|
||||||
|
bool result = (GSConfig.UserHacks_CPUCLUTRender == 1) ? PossibleCLUTDraw() : PossibleCLUTDrawAggressive();
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
if (SwPrimRender())
|
||||||
|
{
|
||||||
|
GL_CACHE("Possible clut draw, drawn with SwPrimRender()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (m_channel_shuffle)
|
if (m_channel_shuffle)
|
||||||
{
|
{
|
||||||
m_channel_shuffle = draw_sprite_tex && (m_context->TEX0.PSM == PSM_PSMT8) && single_page;
|
m_channel_shuffle = draw_sprite_tex && (m_context->TEX0.PSM == PSM_PSMT8) && single_page;
|
||||||
@ -3879,6 +3894,113 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
|||||||
g_gs_device->RenderHW(m_conf);
|
g_gs_device->RenderHW(m_conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GSRendererHW::PossibleCLUTDraw()
|
||||||
|
{
|
||||||
|
if (m_channel_shuffle || m_texture_shuffle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Keep the draws simple, no alpha testing, blending, mipmapping, Z writes, and make sure it's flat.
|
||||||
|
const bool fb_only = m_context->TEST.ATE && m_context->TEST.AFAIL == 1 && m_context->TEST.ATST == ATST_NEVER;
|
||||||
|
|
||||||
|
if (!m_context->ZBUF.ZMSK && !fb_only)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_vt.m_eq.z != 0x1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_context->TEX1.MXL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_vt.m_min.p.x < 0 || m_vt.m_min.p.y < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Writing to the framebuffer for output. We're not interested. - Note: This stops NFS HP2 Busted screens working, but they're glitchy anyway
|
||||||
|
// what NFS HP2 really needs is a kind of shuffle with mask, 32bit target is interpreted as 16bit and masked.
|
||||||
|
if ((m_regs->DISP[0].DISPFB.Block() == m_context->FRAME.Block()) || (m_regs->DISP[1].DISPFB.Block() == m_context->FRAME.Block()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Hopefully no games draw a CLUT with a CLUT, that would be evil, most likely a channel shuffle.
|
||||||
|
if (PRIM->TME && GSLocalMemory::m_psm[m_context->TEX0.PSM].pal > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||||
|
|
||||||
|
// Max size for a CLUT/Current page size.
|
||||||
|
constexpr float clut_width = 16.0f;
|
||||||
|
constexpr float clut_height = 16.0f;
|
||||||
|
constexpr float min_clut_width = 7.0f;
|
||||||
|
constexpr float min_clut_height = 1.0f;
|
||||||
|
const float page_width = static_cast<float>(psm.pgs.x);
|
||||||
|
const float page_height = static_cast<float>(psm.pgs.y);
|
||||||
|
|
||||||
|
// Make sure it's kinda CLUT sized, at least. Be wary, it can draw a line at a time (Guitar Hero - Metallica)
|
||||||
|
const float draw_width = (m_vt.m_max.p.x - m_vt.m_min.p.x);
|
||||||
|
const float draw_height = (m_vt.m_max.p.y - m_vt.m_min.p.y);
|
||||||
|
const bool valid_size =((draw_width >= min_clut_width || draw_height >= min_clut_height) &&
|
||||||
|
m_vt.m_max.p.x <= page_width && m_vt.m_max.p.y <= page_height);
|
||||||
|
|
||||||
|
// Klonoa draws a clut with a full page of triangles instead of a sprite, but we need to make sure it doesn't intefere with normal triangle draws.
|
||||||
|
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
|
||||||
|
{
|
||||||
|
if (draw_width != page_width || draw_height != page_height)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the draw hits the next CLUT and it's marked as invalid (kind of a sanity check).
|
||||||
|
// We can also allow draws which are of a sensible size within the page, as they could also be CLUT draws (or gradients for the CLUT).
|
||||||
|
if (!(valid_size || (m_mem.m_clut.IsInvalid() & 2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (PRIM->TME)
|
||||||
|
{
|
||||||
|
// If we're using a texture to draw our CLUT/whatever, we need the GPU to write back dirty data we need.
|
||||||
|
const GSVector4i r = GetTextureMinMax(m_context->TEX0, m_context->CLAMP, m_vt.IsLinear()).coverage;
|
||||||
|
|
||||||
|
GIFRegBITBLTBUF BITBLTBUF;
|
||||||
|
BITBLTBUF.SBP = m_context->TEX0.TBP0;
|
||||||
|
BITBLTBUF.SBW = m_context->TEX0.TBW;
|
||||||
|
BITBLTBUF.SPSM = m_context->TEX0.PSM;
|
||||||
|
|
||||||
|
InvalidateLocalMem(BITBLTBUF, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slight more aggressive version that kinda YOLO's it if the draw is anywhere near the CLUT or is point/line (providing it's not too wide of a draw and a few other parameters.
|
||||||
|
// This is pretty much tuned for the Sega Model 2 games, which draw a huge gradient, then pick lines out of it to make up CLUT's for about 4000 draws...
|
||||||
|
bool GSRendererHW::PossibleCLUTDrawAggressive()
|
||||||
|
{
|
||||||
|
// Avoid any shuffles.
|
||||||
|
if (m_channel_shuffle || m_texture_shuffle)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Keep the draws simple, no alpha testing, blending, mipmapping, Z writes, and make sure it's flat.
|
||||||
|
if (m_context->TEST.ATE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (PRIM->ABE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_context->TEX1.MXL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_context->FRAME.FBW != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_context->ZBUF.ZMSK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_vt.m_eq.z != 0x1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!((m_vt.m_primclass == GS_POINT_CLASS || m_vt.m_primclass == GS_LINE_CLASS) || ((m_mem.m_clut.GetCLUTCBP() >> 5) >= m_context->FRAME.FBP && (m_context->FRAME.FBP + 1) >= (m_mem.m_clut.GetCLUTCBP() >> 5) && m_vt.m_primclass == GS_SPRITE_CLASS)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Avoid invalidating anything here, we just want to avoid the thing being drawn on the GPU.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex)
|
bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex)
|
||||||
{
|
{
|
||||||
// Master enable.
|
// Master enable.
|
||||||
|
|||||||
@ -127,6 +127,8 @@ private:
|
|||||||
void SwSpriteRender();
|
void SwSpriteRender();
|
||||||
bool CanUseSwSpriteRender();
|
bool CanUseSwSpriteRender();
|
||||||
|
|
||||||
|
bool PossibleCLUTDraw();
|
||||||
|
bool PossibleCLUTDrawAggressive();
|
||||||
bool CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex);
|
bool CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex);
|
||||||
bool SwPrimRender();
|
bool SwPrimRender();
|
||||||
|
|
||||||
|
|||||||
@ -290,6 +290,7 @@ static const char* s_gs_hw_fix_names[] = {
|
|||||||
"texturePreloading",
|
"texturePreloading",
|
||||||
"deinterlace",
|
"deinterlace",
|
||||||
"cpuSpriteRenderBW",
|
"cpuSpriteRenderBW",
|
||||||
|
"cpuCLUTRender",
|
||||||
"gpuPaletteConversion",
|
"gpuPaletteConversion",
|
||||||
};
|
};
|
||||||
static_assert(std::size(s_gs_hw_fix_names) == static_cast<u32>(GameDatabaseSchema::GSHWFixId::Count), "HW fix name lookup is correct size");
|
static_assert(std::size(s_gs_hw_fix_names) == static_cast<u32>(GameDatabaseSchema::GSHWFixId::Count), "HW fix name lookup is correct size");
|
||||||
@ -499,6 +500,9 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
|
|||||||
case GSHWFixId::CPUSpriteRenderBW:
|
case GSHWFixId::CPUSpriteRenderBW:
|
||||||
return (config.UserHacks_CPUSpriteRenderBW == value);
|
return (config.UserHacks_CPUSpriteRenderBW == value);
|
||||||
|
|
||||||
|
case GSHWFixId::CPUCLUTRender:
|
||||||
|
return (config.UserHacks_CPUCLUTRender == value);
|
||||||
|
|
||||||
case GSHWFixId::GPUPaletteConversion:
|
case GSHWFixId::GPUPaletteConversion:
|
||||||
return (config.GPUPaletteConversion == ((value > 1) ? (config.TexturePreloading == TexturePreloadingLevel::Full) : (value != 0)));
|
return (config.GPUPaletteConversion == ((value > 1) ? (config.TexturePreloading == TexturePreloadingLevel::Full) : (value != 0)));
|
||||||
|
|
||||||
@ -642,6 +646,9 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
|
|||||||
config.UserHacks_CPUSpriteRenderBW = value;
|
config.UserHacks_CPUSpriteRenderBW = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GSHWFixId::CPUCLUTRender:
|
||||||
|
config.UserHacks_CPUCLUTRender = value;
|
||||||
|
break;
|
||||||
|
|
||||||
case GSHWFixId::GPUPaletteConversion:
|
case GSHWFixId::GPUPaletteConversion:
|
||||||
{
|
{
|
||||||
|
|||||||
@ -82,6 +82,7 @@ namespace GameDatabaseSchema
|
|||||||
TexturePreloading,
|
TexturePreloading,
|
||||||
Deinterlace,
|
Deinterlace,
|
||||||
CPUSpriteRenderBW,
|
CPUSpriteRenderBW,
|
||||||
|
CPUCLUTRender,
|
||||||
GPUPaletteConversion,
|
GPUPaletteConversion,
|
||||||
|
|
||||||
Count
|
Count
|
||||||
|
|||||||
@ -423,6 +423,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
|
|||||||
OpEqu(UserHacks_TCOffsetX) &&
|
OpEqu(UserHacks_TCOffsetX) &&
|
||||||
OpEqu(UserHacks_TCOffsetY) &&
|
OpEqu(UserHacks_TCOffsetY) &&
|
||||||
OpEqu(UserHacks_CPUSpriteRenderBW) &&
|
OpEqu(UserHacks_CPUSpriteRenderBW) &&
|
||||||
|
OpEqu(UserHacks_CPUCLUTRender) &&
|
||||||
OpEqu(OverrideTextureBarriers) &&
|
OpEqu(OverrideTextureBarriers) &&
|
||||||
OpEqu(OverrideGeometryShaders) &&
|
OpEqu(OverrideGeometryShaders) &&
|
||||||
|
|
||||||
@ -616,6 +617,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
|
|||||||
GSSettingIntEx(UserHacks_TCOffsetX, "UserHacks_TCOffsetX");
|
GSSettingIntEx(UserHacks_TCOffsetX, "UserHacks_TCOffsetX");
|
||||||
GSSettingIntEx(UserHacks_TCOffsetY, "UserHacks_TCOffsetY");
|
GSSettingIntEx(UserHacks_TCOffsetY, "UserHacks_TCOffsetY");
|
||||||
GSSettingIntEx(UserHacks_CPUSpriteRenderBW, "UserHacks_CPUSpriteRenderBW");
|
GSSettingIntEx(UserHacks_CPUSpriteRenderBW, "UserHacks_CPUSpriteRenderBW");
|
||||||
|
GSSettingIntEx(UserHacks_CPUCLUTRender, "UserHacks_CPUCLUTRender");
|
||||||
GSSettingIntEnumEx(TriFilter, "TriFilter");
|
GSSettingIntEnumEx(TriFilter, "TriFilter");
|
||||||
GSSettingIntEx(OverrideTextureBarriers, "OverrideTextureBarriers");
|
GSSettingIntEx(OverrideTextureBarriers, "OverrideTextureBarriers");
|
||||||
GSSettingIntEx(OverrideGeometryShaders, "OverrideGeometryShaders");
|
GSSettingIntEx(OverrideGeometryShaders, "OverrideGeometryShaders");
|
||||||
@ -663,6 +665,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
|||||||
UserHacks_TCOffsetX = 0;
|
UserHacks_TCOffsetX = 0;
|
||||||
UserHacks_TCOffsetY = 0;
|
UserHacks_TCOffsetY = 0;
|
||||||
UserHacks_CPUSpriteRenderBW = 0;
|
UserHacks_CPUSpriteRenderBW = 0;
|
||||||
|
UserHacks_CPUCLUTRender = 0;
|
||||||
SkipDrawStart = 0;
|
SkipDrawStart = 0;
|
||||||
SkipDrawEnd = 0;
|
SkipDrawEnd = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user