mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Add "Merge Targets" texture-in-RT mode
Can take several targets from the cache, and create a combined/merged source from them. Fixes shadow maps in Destroy All Humans.
This commit is contained in:
parent
75957c84e3
commit
64b38e5a4a
|
@ -16718,12 +16718,16 @@ SLES-53196:
|
||||||
name: "Destroy All Humans!"
|
name: "Destroy All Humans!"
|
||||||
region: "PAL-M4"
|
region: "PAL-M4"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLES-53197:
|
SLES-53197:
|
||||||
name: "Destroy All Humans!"
|
name: "Destroy All Humans!"
|
||||||
region: "PAL-G"
|
region: "PAL-G"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLES-53199:
|
SLES-53199:
|
||||||
name: "25 to Life"
|
name: "25 to Life"
|
||||||
region: "PAL-E"
|
region: "PAL-E"
|
||||||
|
@ -17886,7 +17890,9 @@ SLES-53641:
|
||||||
name: "Destroy All Humans!"
|
name: "Destroy All Humans!"
|
||||||
region: "PAL-R"
|
region: "PAL-R"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLES-53644:
|
SLES-53644:
|
||||||
name: "Gene Troopers"
|
name: "Gene Troopers"
|
||||||
region: "PAL-M5"
|
region: "PAL-M5"
|
||||||
|
@ -19797,7 +19803,9 @@ SLES-54384:
|
||||||
name: "Destroy All Humans 2 - Make War not Love"
|
name: "Destroy All Humans 2 - Make War not Love"
|
||||||
region: "PAL-M5"
|
region: "PAL-M5"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLES-54385:
|
SLES-54385:
|
||||||
name: "Atelier Iris 2 - The Azoth of Destiny"
|
name: "Atelier Iris 2 - The Azoth of Destiny"
|
||||||
region: "PAL-E"
|
region: "PAL-E"
|
||||||
|
@ -20855,6 +20863,8 @@ SLES-54786:
|
||||||
SLES-54788:
|
SLES-54788:
|
||||||
name: "Aqua Teen Hunger Force - Zombie Ninja Pro-Am"
|
name: "Aqua Teen Hunger Force - Zombie Ninja Pro-Am"
|
||||||
region: "PAL-E"
|
region: "PAL-E"
|
||||||
|
gsHWFixes:
|
||||||
|
textureInsideRT: 2 # Fixes shadows.
|
||||||
SLES-54789:
|
SLES-54789:
|
||||||
name: "Beta Bloc"
|
name: "Beta Bloc"
|
||||||
region: "PAL-E"
|
region: "PAL-E"
|
||||||
|
@ -32693,7 +32703,9 @@ SLPM-66430:
|
||||||
name: "Destroy All Humans!"
|
name: "Destroy All Humans!"
|
||||||
region: "NTSC-J"
|
region: "NTSC-J"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLPM-66431:
|
SLPM-66431:
|
||||||
name: "WinBack 2 - Project Poseidon"
|
name: "WinBack 2 - Project Poseidon"
|
||||||
region: "NTSC-J"
|
region: "NTSC-J"
|
||||||
|
@ -45111,7 +45123,9 @@ SLUS-20945:
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
compat: 5
|
compat: 5
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLUS-20946:
|
SLUS-20946:
|
||||||
name: "Grand Theft Auto - San Andreas"
|
name: "Grand Theft Auto - San Andreas"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
|
@ -47879,7 +47893,9 @@ SLUS-21439:
|
||||||
clampModes:
|
clampModes:
|
||||||
eeClampMode: 3 # Fixes material stretching across screen that appears for a split second.
|
eeClampMode: 3 # Fixes material stretching across screen that appears for a split second.
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLUS-21440:
|
SLUS-21440:
|
||||||
name: "Big Idea's VeggieTales - LarryBoy and the Bad Apple"
|
name: "Big Idea's VeggieTales - LarryBoy and the Bad Apple"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
|
@ -48728,6 +48744,8 @@ SLUS-21633:
|
||||||
name: "Aqua Teen Hunger Force - Zombie Ninja Pro-Am"
|
name: "Aqua Teen Hunger Force - Zombie Ninja Pro-Am"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
compat: 5
|
compat: 5
|
||||||
|
gsHWFixes:
|
||||||
|
textureInsideRT: 2 # Fixes shadows.
|
||||||
SLUS-21634:
|
SLUS-21634:
|
||||||
name: "Crazy Frog Racer 2"
|
name: "Crazy Frog Racer 2"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
|
@ -50790,7 +50808,9 @@ SLUS-29150:
|
||||||
name: "Destroy All Humans! [Demo]"
|
name: "Destroy All Humans! [Demo]"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLUS-29152:
|
SLUS-29152:
|
||||||
name: "Battlefield 2 - Modern Combat [Regular Demo]"
|
name: "Battlefield 2 - Modern Combat [Regular Demo]"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
|
@ -50980,7 +51000,9 @@ SLUS-29196:
|
||||||
name: "Destroy All Humans! 2 [Demo]"
|
name: "Destroy All Humans! 2 [Demo]"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
gsHWFixes:
|
gsHWFixes:
|
||||||
mipmap: 1
|
mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer.
|
||||||
|
trilinearFiltering: 1
|
||||||
|
textureInsideRT: 2 # Fixes shadow maps.
|
||||||
SLUS-29197:
|
SLUS-29197:
|
||||||
name: "Flushed Away [Demo]"
|
name: "Flushed Away [Demo]"
|
||||||
region: "NTSC-U"
|
region: "NTSC-U"
|
||||||
|
|
|
@ -178,11 +178,11 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||||
&GraphicsSettingsWidget::onTrilinearFilteringChanged);
|
&GraphicsSettingsWidget::onTrilinearFilteringChanged);
|
||||||
connect(m_ui.gpuPaletteConversion, QOverload<int>::of(&QCheckBox::stateChanged), this,
|
connect(m_ui.gpuPaletteConversion, QOverload<int>::of(&QCheckBox::stateChanged), this,
|
||||||
&GraphicsSettingsWidget::onGpuPaletteConversionChanged);
|
&GraphicsSettingsWidget::onGpuPaletteConversionChanged);
|
||||||
connect(m_ui.textureInsideRt, QOverload<int>::of(&QCheckBox::stateChanged), this,
|
connect(m_ui.textureInsideRt, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||||
&GraphicsSettingsWidget::onTextureInsideRtChanged);
|
&GraphicsSettingsWidget::onTextureInsideRtChanged);
|
||||||
onTrilinearFilteringChanged();
|
onTrilinearFilteringChanged();
|
||||||
onGpuPaletteConversionChanged(m_ui.gpuPaletteConversion->checkState());
|
onGpuPaletteConversionChanged(m_ui.gpuPaletteConversion->checkState());
|
||||||
onTextureInsideRtChanged(m_ui.textureInsideRt->checkState());
|
onTextureInsideRtChanged();
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
// HW Renderer Fixes
|
// HW Renderer Fixes
|
||||||
|
@ -200,7 +200,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||||
sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
|
sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", false);
|
SettingWidgetBinder::BindWidgetToIntSetting(
|
||||||
|
sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
|
||||||
|
@ -540,7 +541,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||||
tr("Uploads GS data when rendering a new frame to reproduce some effects accurately. "
|
tr("Uploads GS data when rendering a new frame to reproduce some effects accurately. "
|
||||||
"Fixes black screen issues in games like Armored Core: Last Raven."));
|
"Fixes black screen issues in games like Armored Core: Last Raven."));
|
||||||
|
|
||||||
dialog->registerWidgetHelp(m_ui.textureInsideRt, tr("Texture Inside RT"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.textureInsideRt, tr("Texture Inside RT"), tr("Disabled"),
|
||||||
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer."));
|
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer."));
|
||||||
|
|
||||||
dialog->registerWidgetHelp(m_ui.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"),
|
dialog->registerWidgetHelp(m_ui.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"),
|
||||||
|
@ -861,11 +862,10 @@ void GraphicsSettingsWidget::onGpuPaletteConversionChanged(int state)
|
||||||
m_ui.anisotropicFiltering->setDisabled(disabled);
|
m_ui.anisotropicFiltering->setDisabled(disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsSettingsWidget::onTextureInsideRtChanged(int state)
|
void GraphicsSettingsWidget::onTextureInsideRtChanged()
|
||||||
{
|
{
|
||||||
const bool disabled = state == Qt::CheckState::PartiallyChecked ?
|
const bool disabled = static_cast<GSTextureInRtMode>(m_dialog->getEffectiveIntValue("EmuCore/GS", "UserHacks_TextureInsideRt",
|
||||||
Host::GetBaseBoolSettingValue("EmuCore/GS", "UserHacks_TextureInsideRt", false) :
|
static_cast<int>(GSTextureInRtMode::Disabled))) >= GSTextureInRtMode::InsideTargets;
|
||||||
(state != 0);
|
|
||||||
|
|
||||||
m_ui.targetPartialInvalidation->setDisabled(disabled);
|
m_ui.targetPartialInvalidation->setDisabled(disabled);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ private Q_SLOTS:
|
||||||
void onAdapterChanged(int index);
|
void onAdapterChanged(int index);
|
||||||
void onTrilinearFilteringChanged();
|
void onTrilinearFilteringChanged();
|
||||||
void onGpuPaletteConversionChanged(int state);
|
void onGpuPaletteConversionChanged(int state);
|
||||||
void onTextureInsideRtChanged(int state);
|
void onTextureInsideRtChanged();
|
||||||
void onFullscreenModeChanged(int index);
|
void onFullscreenModeChanged(int index);
|
||||||
void onShadeBoostChanged();
|
void onShadeBoostChanged();
|
||||||
void onCaptureContainerChanged();
|
void onCaptureContainerChanged();
|
||||||
|
|
|
@ -978,14 +978,14 @@
|
||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_12">
|
<widget class="QLabel" name="label_12">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Skipdraw Range:</string>
|
<string>Skipdraw Range:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="5" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="skipDrawStart">
|
<widget class="QSpinBox" name="skipDrawStart">
|
||||||
|
@ -1003,7 +1003,7 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0" colspan="2">
|
<item row="6" column="0" colspan="2">
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="preloadFrameData">
|
<widget class="QCheckBox" name="preloadFrameData">
|
||||||
|
@ -1012,13 +1012,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QCheckBox" name="textureInsideRt">
|
|
||||||
<property name="text">
|
|
||||||
<string>Texture Inside RT</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="disableDepthEmulation">
|
<widget class="QCheckBox" name="disableDepthEmulation">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -1061,7 +1054,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="2" column="1">
|
||||||
<widget class="QCheckBox" name="readTCOnClose">
|
<widget class="QCheckBox" name="readTCOnClose">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Read Targets When Closing</string>
|
<string>Read Targets When Closing</string>
|
||||||
|
@ -1077,6 +1070,32 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_45">
|
||||||
|
<property name="text">
|
||||||
|
<string>Texture Inside RT:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QComboBox" name="textureInsideRt">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Disabled (Default)</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Inside Target</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Merge Targets</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QGroupBox" name="upscalingFixesTab">
|
<widget class="QGroupBox" name="upscalingFixesTab">
|
||||||
|
|
|
@ -357,6 +357,13 @@ enum class GSGPUTargetCLUTMode : u8
|
||||||
InsideTarget,
|
InsideTarget,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class GSTextureInRtMode : u8
|
||||||
|
{
|
||||||
|
Disabled,
|
||||||
|
InsideTargets,
|
||||||
|
MergeTargets,
|
||||||
|
};
|
||||||
|
|
||||||
// Template function for casting enumerations to their underlying type
|
// Template function for casting enumerations to their underlying type
|
||||||
template <typename Enumeration>
|
template <typename Enumeration>
|
||||||
typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
|
typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
|
||||||
|
@ -670,7 +677,6 @@ struct Pcsx2Config
|
||||||
UserHacks_DisableSafeFeatures : 1,
|
UserHacks_DisableSafeFeatures : 1,
|
||||||
UserHacks_MergePPSprite : 1,
|
UserHacks_MergePPSprite : 1,
|
||||||
UserHacks_WildHack : 1,
|
UserHacks_WildHack : 1,
|
||||||
UserHacks_TextureInsideRt : 1,
|
|
||||||
UserHacks_TargetPartialInvalidation : 1,
|
UserHacks_TargetPartialInvalidation : 1,
|
||||||
UserHacks_EstimateTextureRegion : 1,
|
UserHacks_EstimateTextureRegion : 1,
|
||||||
FXAA : 1,
|
FXAA : 1,
|
||||||
|
@ -748,6 +754,7 @@ struct Pcsx2Config
|
||||||
int UserHacks_CPUSpriteRenderBW{0};
|
int UserHacks_CPUSpriteRenderBW{0};
|
||||||
int UserHacks_CPUCLUTRender{ 0 };
|
int UserHacks_CPUCLUTRender{ 0 };
|
||||||
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode{GSGPUTargetCLUTMode::Disabled};
|
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode{GSGPUTargetCLUTMode::Disabled};
|
||||||
|
GSTextureInRtMode UserHacks_TextureInsideRt{GSTextureInRtMode::Disabled};
|
||||||
TriFiltering TriFilter{TriFiltering::Automatic};
|
TriFiltering TriFilter{TriFiltering::Automatic};
|
||||||
int OverrideTextureBarriers{-1};
|
int OverrideTextureBarriers{-1};
|
||||||
int OverrideGeometryShaders{-1};
|
int OverrideGeometryShaders{-1};
|
||||||
|
|
|
@ -159,7 +159,7 @@
|
||||||
"textureInsideRT": {
|
"textureInsideRT": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"maximum": 1
|
"maximum": 2
|
||||||
},
|
},
|
||||||
"alignSprite": {
|
"alignSprite": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|
|
@ -3166,6 +3166,7 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
||||||
"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_cpu_clut_render_options[] = {"0 (Disabled)", "1 (Normal)", "2 (Aggressive)"};
|
||||||
|
static constexpr const char* s_texture_inside_rt_options[] = {"Disabled", "Inside Target", "Merge Targets"};
|
||||||
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"};
|
||||||
|
@ -3193,9 +3194,9 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
||||||
DrawToggleSetting(bsi, "Disable Partial Invalidation",
|
DrawToggleSetting(bsi, "Disable Partial Invalidation",
|
||||||
"Removes texture cache entries when there is any intersection, rather than only the intersected areas.", "EmuCore/GS",
|
"Removes texture cache entries when there is any intersection, rather than only the intersected areas.", "EmuCore/GS",
|
||||||
"UserHacks_DisablePartialInvalidation", false, manual_hw_fixes);
|
"UserHacks_DisablePartialInvalidation", false, manual_hw_fixes);
|
||||||
DrawToggleSetting(bsi, "Texture Inside Render Target",
|
DrawIntListSetting(bsi, "Texture Inside Render Target",
|
||||||
"Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.", "EmuCore/GS",
|
"Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.", "EmuCore/GS",
|
||||||
"UserHacks_TextureInsideRt", false, manual_hw_fixes);
|
"UserHacks_TextureInsideRt", 0, s_texture_inside_rt_options, std::size(s_texture_inside_rt_options), 0, manual_hw_fixes);
|
||||||
DrawToggleSetting(bsi, "Target Partial Invalidation",
|
DrawToggleSetting(bsi, "Target Partial Invalidation",
|
||||||
"Allows partial invalidation of render targets, which can fix graphical errors in some games.", "EmuCore/GS",
|
"Allows partial invalidation of render targets, which can fix graphical errors in some games.", "EmuCore/GS",
|
||||||
"UserHacks_TargetPartialInvalidation", false,
|
"UserHacks_TargetPartialInvalidation", false,
|
||||||
|
|
|
@ -409,8 +409,8 @@ void ImGuiManager::DrawSettingsOverlay()
|
||||||
APPEND("CCD={} ", GSConfig.UserHacks_CPUCLUTRender);
|
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 != GSTextureInRtMode::Disabled)
|
||||||
APPEND("TexRT ");
|
APPEND("TexRT={} ", static_cast<int>(GSConfig.UserHacks_TextureInsideRt));
|
||||||
if (GSConfig.UserHacks_WildHack)
|
if (GSConfig.UserHacks_WildHack)
|
||||||
APPEND("WA ");
|
APPEND("WA ");
|
||||||
if (GSConfig.UserHacks_MergePPSprite)
|
if (GSConfig.UserHacks_MergePPSprite)
|
||||||
|
|
|
@ -25,6 +25,12 @@
|
||||||
#include "common/Align.h"
|
#include "common/Align.h"
|
||||||
#include "common/HashCombine.h"
|
#include "common/HashCombine.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <stdlib.h>
|
||||||
|
#else
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static u8* s_unswizzle_buffer;
|
static u8* s_unswizzle_buffer;
|
||||||
|
|
||||||
GSTextureCache::GSTextureCache()
|
GSTextureCache::GSTextureCache()
|
||||||
|
@ -377,6 +383,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||||
|
|
||||||
bool found_t = false;
|
bool found_t = false;
|
||||||
bool tex_in_rt = false;
|
bool tex_in_rt = false;
|
||||||
|
bool tex_merge_rt = false;
|
||||||
for (auto t : m_dst[RenderTarget])
|
for (auto t : m_dst[RenderTarget])
|
||||||
{
|
{
|
||||||
if (t->m_used)
|
if (t->m_used)
|
||||||
|
@ -428,6 +435,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||||
|
|
||||||
found_t = true;
|
found_t = true;
|
||||||
tex_in_rt = false;
|
tex_in_rt = false;
|
||||||
|
tex_merge_rt = false;
|
||||||
x_offset = 0;
|
x_offset = 0;
|
||||||
y_offset = 0;
|
y_offset = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -442,40 +450,57 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||||
dst = t;
|
dst = t;
|
||||||
found_t = true;
|
found_t = true;
|
||||||
tex_in_rt = false;
|
tex_in_rt = false;
|
||||||
|
tex_merge_rt = false;
|
||||||
x_offset = 0;
|
x_offset = 0;
|
||||||
y_offset = 0;
|
y_offset = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
|
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
|
||||||
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
|
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
|
||||||
else if (GSConfig.UserHacks_TextureInsideRt && psm >= PSM_PSMCT32 && psm <= PSM_PSMCT16S && t->m_TEX0.PSM == psm &&
|
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && psm >= PSM_PSMCT32 &&
|
||||||
(t->Overlaps(bp, bw, psm, r) || t_wraps) && t->m_age <= 1 && !found_t && bp >= t->m_TEX0.TBP0)
|
psm <= PSM_PSMCT16S && t->m_TEX0.PSM == psm && (t->Overlaps(bp, bw, psm, r) || t_wraps) &&
|
||||||
|
t->m_age <= 1 && !found_t)
|
||||||
{
|
{
|
||||||
// Only PSMCT32 to limit false hits.
|
|
||||||
// PSM equality needed because CreateSource does not handle PSM conversion.
|
// PSM equality needed because CreateSource does not handle PSM conversion.
|
||||||
// Only inclusive hit to limit false hits.
|
// Only inclusive hit to limit false hits.
|
||||||
|
|
||||||
// Check if it is possible to hit with valid <x,y> offset on the given Target.
|
if (bp >= t->m_TEX0.TBP0)
|
||||||
// Fixes Jak eyes rendering.
|
|
||||||
// Fixes Xenosaga 3 last dungeon graphic bug.
|
|
||||||
// Fixes Pause menu in The Getaway.
|
|
||||||
|
|
||||||
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, r, t);
|
|
||||||
if (!so.is_valid && t_wraps)
|
|
||||||
{
|
{
|
||||||
// Improves Beyond Good & Evil shadow.
|
// Check if it is possible to hit with valid <x,y> offset on the given Target.
|
||||||
const u32 bp_unwrap = bp + GSTextureCache::MAX_BP + 0x1;
|
// Fixes Jak eyes rendering.
|
||||||
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, r, t);
|
// Fixes Xenosaga 3 last dungeon graphic bug.
|
||||||
|
// Fixes Pause menu in The Getaway.
|
||||||
|
|
||||||
|
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, r, t);
|
||||||
|
if (!so.is_valid && t_wraps)
|
||||||
|
{
|
||||||
|
// Improves Beyond Good & Evil shadow.
|
||||||
|
const u32 bp_unwrap = bp + GSTextureCache::MAX_BP + 0x1;
|
||||||
|
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, r, t);
|
||||||
|
}
|
||||||
|
if (so.is_valid)
|
||||||
|
{
|
||||||
|
dst = t;
|
||||||
|
// Offset from Target to Source in Target coords.
|
||||||
|
x_offset = so.b2a_offset.x;
|
||||||
|
y_offset = so.b2a_offset.y;
|
||||||
|
tex_in_rt = true;
|
||||||
|
tex_merge_rt = false;
|
||||||
|
found_t = true;
|
||||||
|
// Keep looking, just in case there is an exact match (Situation: Target frame drawn inside target frame, current makes a separate texture)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (so.is_valid)
|
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::MergeTargets && !tex_merge_rt)
|
||||||
{
|
{
|
||||||
dst = t;
|
dst = t;
|
||||||
// Offset from Target to Source in Target coords.
|
x_offset = 0;
|
||||||
x_offset = so.b2a_offset.x;
|
y_offset = 0;
|
||||||
y_offset = so.b2a_offset.y;
|
tex_in_rt = false;
|
||||||
tex_in_rt = true;
|
tex_merge_rt = true;
|
||||||
found_t = true;
|
|
||||||
// Keep looking, just in case there is an exact match (Situation: Target frame drawn inside target frame, current makes a separate texture)
|
// Prefer a target inside over a target outside.
|
||||||
|
found_t = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -530,7 +555,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||||
//
|
//
|
||||||
// Sigh... They don't help us.
|
// Sigh... They don't help us.
|
||||||
|
|
||||||
if (!found_t && !GSConfig.UserHacks_DisableDepthSupport)
|
if (!found_t && !dst && !GSConfig.UserHacks_DisableDepthSupport)
|
||||||
{
|
{
|
||||||
// Let's try a trick to avoid to use wrongly a depth buffer
|
// Let's try a trick to avoid to use wrongly a depth buffer
|
||||||
// Unfortunately, I don't have any Arc the Lad testcase
|
// Unfortunately, I don't have any Arc the Lad testcase
|
||||||
|
@ -557,6 +582,9 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tex_merge_rt)
|
||||||
|
src = CreateMergedSource(TEX0, TEXA, region, dst->m_texture->GetScale());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!src)
|
if (!src)
|
||||||
|
@ -1261,7 +1289,8 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
|
||||||
const u32 end_bp = off.bnNoWrap(rect.z - 1, rect.w - 1);
|
const u32 end_bp = off.bnNoWrap(rect.z - 1, rect.w - 1);
|
||||||
|
|
||||||
// Ideally in the future we can turn this on unconditionally, but for now it breaks too much.
|
// Ideally in the future we can turn this on unconditionally, but for now it breaks too much.
|
||||||
const bool check_inside_target = (GSConfig.UserHacks_TargetPartialInvalidation || GSConfig.UserHacks_TextureInsideRt);
|
const bool check_inside_target = (GSConfig.UserHacks_TargetPartialInvalidation ||
|
||||||
|
GSConfig.UserHacks_TextureInsideRt != GSTextureInRtMode::Disabled);
|
||||||
RGBAMask rgba;
|
RGBAMask rgba;
|
||||||
rgba._u32 = GSUtil::GetChannelMask(psm);
|
rgba._u32 = GSUtil::GetChannelMask(psm);
|
||||||
|
|
||||||
|
@ -2543,6 +2572,289 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GSTextureCache::Source* GSTextureCache::CreateMergedSource(GIFRegTEX0 TEX0, GIFRegTEXA TEXA, SourceRegion region, const GSVector2& scale)
|
||||||
|
{
|
||||||
|
// We *should* be able to use the TBW here as an indicator of size... except Destroy All Humans 2 sets
|
||||||
|
// TBW to 10, and samples from 64 through 703... which means it'd be grabbing the next row at the end.
|
||||||
|
const int tex_width = std::max<int>(64 * TEX0.TBW, region.GetMaxX());
|
||||||
|
const int tex_height = region.HasY() ? region.GetHeight() : (1 << TEX0.TH);
|
||||||
|
const int scaled_width = static_cast<int>(static_cast<float>(tex_width) * scale.x);
|
||||||
|
const int scaled_height = static_cast<int>(static_cast<float>(tex_height) * scale.y);
|
||||||
|
|
||||||
|
// Compute new end block based on size.
|
||||||
|
const u32 end_block = GSLocalMemory::m_psm[TEX0.PSM].info.bn(tex_width - 1, tex_height - 1, TEX0.TBP0, TEX0.TBW);
|
||||||
|
GL_PUSH("Merging targets from %x through %x", TEX0.TBP0, end_block);
|
||||||
|
|
||||||
|
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||||
|
const int page_width = psm.pgs.x;
|
||||||
|
const int page_height = psm.pgs.y;
|
||||||
|
constexpr int page_blocks = 32;
|
||||||
|
|
||||||
|
// Number of pages to increment by for each row. This may be smaller than tex_width.
|
||||||
|
const int row_page_increment = TEX0.TBW;
|
||||||
|
const int page_block_increment = row_page_increment * page_blocks;
|
||||||
|
const int width_in_pages = (tex_width + (page_width - 1)) / page_width;
|
||||||
|
const int height_in_pages = (tex_height + (page_height - 1)) / page_height;
|
||||||
|
const int num_pages = width_in_pages * height_in_pages;
|
||||||
|
const GSVector4i page_rect(GSVector4i(psm.pgs).zwxy());
|
||||||
|
|
||||||
|
// Temporary texture for preloading local memory.
|
||||||
|
const GSOffset lm_off(psm.info, TEX0.TBP0, TEX0.TBW, TEX0.PSM);
|
||||||
|
GSTexture* lmtex = nullptr;
|
||||||
|
GSTexture::GSMap lmtex_map;
|
||||||
|
bool lmtex_mapped = false;
|
||||||
|
|
||||||
|
u8* pages_done = static_cast<u8*>(alloca((num_pages + 7) / 8));
|
||||||
|
std::memset(pages_done, 0, (num_pages + 7) / 8);
|
||||||
|
|
||||||
|
// Queue of rectangles to copy, we try to batch as many at once as possible.
|
||||||
|
// Multiply by 2 in case we need to preload.
|
||||||
|
GSDevice::MultiStretchRect* copy_queue =
|
||||||
|
static_cast<GSDevice::MultiStretchRect*>(alloca(sizeof(GSDevice::MultiStretchRect) * num_pages * 2));
|
||||||
|
u32 copy_count = 0;
|
||||||
|
|
||||||
|
// Page counters.
|
||||||
|
u32 start_TBP0 = TEX0.TBP0;
|
||||||
|
u32 current_TBP0 = start_TBP0;
|
||||||
|
int page_x = 0;
|
||||||
|
int page_y = 0;
|
||||||
|
|
||||||
|
// Helper to preload a page.
|
||||||
|
auto preload_page = [&](int dst_x, int dst_y) {
|
||||||
|
if (!lmtex)
|
||||||
|
{
|
||||||
|
lmtex = g_gs_device->CreateTexture(tex_width, tex_height, 1, GSTexture::Format::Color, false);
|
||||||
|
lmtex_mapped = lmtex->Map(lmtex_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GSVector4i rect(
|
||||||
|
dst_x, dst_y, std::min(dst_x + page_width, tex_width), std::min(dst_y + page_height, tex_height));
|
||||||
|
|
||||||
|
if (lmtex_mapped)
|
||||||
|
{
|
||||||
|
psm.rtx(g_gs_renderer->m_mem, lm_off, rect, lmtex_map.bits + dst_y * lmtex_map.pitch + dst_x * sizeof(u32),
|
||||||
|
lmtex_map.pitch, TEXA);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Slow for DX11... page_width * 4 should still be 32 byte aligned for AVX.
|
||||||
|
const int pitch = page_width * sizeof(u32);
|
||||||
|
psm.rtx(g_gs_renderer->m_mem, lm_off, rect, s_unswizzle_buffer, pitch, TEXA);
|
||||||
|
lmtex->Update(rect, s_unswizzle_buffer, pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload texture -> render target.
|
||||||
|
const bool linear = (scale.x != 1.0f);
|
||||||
|
copy_queue[copy_count++] = {lmtex, GSVector4(rect) / GSVector4(lmtex->GetSize()).xyxy(),
|
||||||
|
GSVector4(rect) * GSVector4(scale).xyxy(), linear};
|
||||||
|
};
|
||||||
|
|
||||||
|
// The idea: loop through pages that this texture covers, find targets which overlap, and copy them in.
|
||||||
|
// For pages which don't have any targets covering, if preloading is enabled, load that page from local memory.
|
||||||
|
// Try to batch as much as possible, ideally this shouldn't be more than a handful of draw calls.
|
||||||
|
for (int page_num = 0; page_num < num_pages; page_num++)
|
||||||
|
{
|
||||||
|
const u32 this_start_block = current_TBP0;
|
||||||
|
const u32 this_end_block = (current_TBP0 + page_blocks) - 1;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
if (pages_done[page_num / 8] & (1u << (page_num % 8)))
|
||||||
|
goto next_page;
|
||||||
|
|
||||||
|
GL_INS("Searching for block range %x - %x for (%u,%u)", this_start_block, this_end_block, page_x * page_width,
|
||||||
|
page_y * page_height);
|
||||||
|
|
||||||
|
for (auto i = m_dst[RenderTarget].begin(); i != m_dst[RenderTarget].end(); ++i)
|
||||||
|
{
|
||||||
|
Target* const t = *i;
|
||||||
|
if (this_start_block >= t->m_TEX0.TBP0 && this_end_block <= t->m_end_block && t->m_TEX0.PSM == TEX0.PSM)
|
||||||
|
{
|
||||||
|
GL_INS(" Candidate at BP %x BW %d PSM %d", t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM);
|
||||||
|
|
||||||
|
// Can't copy multiple pages when we're past the TBW.. only grab one page at a time then.
|
||||||
|
GSVector4i src_rect(page_rect);
|
||||||
|
bool copy_multiple_pages = (t->m_TEX0.TBW == TEX0.TBW && page_x < row_page_increment);
|
||||||
|
int available_pages_x = 1;
|
||||||
|
int available_pages_y = 1;
|
||||||
|
if (copy_multiple_pages)
|
||||||
|
{
|
||||||
|
// TBW matches, we can copy multiple pages.
|
||||||
|
available_pages_x = (row_page_increment - page_x);
|
||||||
|
available_pages_y = (height_in_pages - page_y);
|
||||||
|
src_rect.z = available_pages_x * page_width;
|
||||||
|
src_rect.w = available_pages_y * page_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Why do we do this? Things go really haywire when we try to get surface offsets when
|
||||||
|
// the valid rect isn't starting at 0. In most cases, the target has been cleared anyway,
|
||||||
|
// so we really should be setting this to 0, not the draw...
|
||||||
|
//
|
||||||
|
// Guitar Hero needs it, the player meshes don't necessarily touch the corner, and so
|
||||||
|
// does Aqua Teen Hunger Force.
|
||||||
|
//
|
||||||
|
const GSVector4i t_rect(t->m_valid.insert64<0>(0));
|
||||||
|
|
||||||
|
SurfaceOffset so;
|
||||||
|
if (this_start_block > t->m_TEX0.TBP0)
|
||||||
|
{
|
||||||
|
SurfaceOffsetKey sok;
|
||||||
|
sok.elems[0].bp = this_start_block;
|
||||||
|
sok.elems[0].bw = TEX0.TBW;
|
||||||
|
sok.elems[0].psm = TEX0.PSM;
|
||||||
|
sok.elems[0].rect = src_rect;
|
||||||
|
sok.elems[1].bp = t->m_TEX0.TBP0;
|
||||||
|
sok.elems[1].bw = t->m_TEX0.TBW;
|
||||||
|
sok.elems[1].psm = t->m_TEX0.PSM;
|
||||||
|
sok.elems[1].rect = t_rect;
|
||||||
|
so = ComputeSurfaceOffset(sok);
|
||||||
|
if (!so.is_valid)
|
||||||
|
goto next_page;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
so.is_valid = true;
|
||||||
|
so.b2a_offset = src_rect.rintersect(t_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust to what the target actually has.
|
||||||
|
if (copy_multiple_pages)
|
||||||
|
{
|
||||||
|
// Min here because we don't want to go off the end of the target.
|
||||||
|
available_pages_x = std::min(available_pages_x, (so.b2a_offset.width() + (psm.pgs.x - 1)) / psm.pgs.x);
|
||||||
|
available_pages_y = std::min(available_pages_y, (so.b2a_offset.height() + (psm.pgs.y - 1)) / psm.pgs.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We might not even have a full page valid..
|
||||||
|
const bool linear = (scale != t->m_texture->GetScale());
|
||||||
|
const int src_x_end = so.b2a_offset.z;
|
||||||
|
const int src_y_end = so.b2a_offset.w;
|
||||||
|
int src_y = so.b2a_offset.y;
|
||||||
|
int dst_y = page_y * page_height;
|
||||||
|
int current_copy_page = page_num;
|
||||||
|
|
||||||
|
for (int copy_page_y = 0; copy_page_y < available_pages_y; copy_page_y++)
|
||||||
|
{
|
||||||
|
if (src_y >= src_y_end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
const int wanted_height = std::min(tex_height - dst_y, page_height);
|
||||||
|
const int copy_height = std::min(src_y_end - src_y, wanted_height);
|
||||||
|
int src_x = so.b2a_offset.x;
|
||||||
|
int dst_x = page_x * page_width;
|
||||||
|
int row_page = current_copy_page;
|
||||||
|
pxAssert(dst_y < tex_height && copy_height > 0);
|
||||||
|
|
||||||
|
for (int copy_page_x = 0; copy_page_x < available_pages_x; copy_page_x++)
|
||||||
|
{
|
||||||
|
if (src_x >= src_x_end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((pages_done[row_page / 8] & (1u << (row_page % 8))) == 0)
|
||||||
|
{
|
||||||
|
pages_done[row_page / 8] |= (1u << (row_page % 8));
|
||||||
|
|
||||||
|
// In case a whole page isn't valid.
|
||||||
|
const int wanted_width = std::min(tex_width - dst_x, page_width);
|
||||||
|
const int copy_width = std::min(src_x_end - src_x, wanted_width);
|
||||||
|
pxAssert(dst_x < tex_width && copy_width > 0);
|
||||||
|
|
||||||
|
// Preload any missing parts. This will happen when the valid rect isn't page aligned.
|
||||||
|
if (GSConfig.PreloadFrameWithGSData &&
|
||||||
|
(copy_width < wanted_width || copy_height < wanted_height))
|
||||||
|
{
|
||||||
|
preload_page(dst_x, dst_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
GL_INS(" Copy from %d,%d -> %d,%d (%dx%d)", src_x, src_y, dst_x, dst_y, copy_width, copy_height);
|
||||||
|
copy_queue[copy_count++] = {t->m_texture,
|
||||||
|
(GSVector4(src_x, src_y, src_x + copy_width, src_y + copy_height) *
|
||||||
|
GSVector4(t->m_texture->GetScale()).xyxy()) /
|
||||||
|
GSVector4(t->m_texture->GetSize()).xyxy(),
|
||||||
|
GSVector4(dst_x, dst_y, dst_x + copy_width, dst_y + copy_height) *
|
||||||
|
GSVector4(scale).xyxy(),
|
||||||
|
linear};
|
||||||
|
}
|
||||||
|
|
||||||
|
row_page++;
|
||||||
|
src_x += page_width;
|
||||||
|
dst_x += page_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_copy_page += width_in_pages;
|
||||||
|
src_y += page_height;
|
||||||
|
dst_y += page_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
if (GSConfig.PreloadFrameWithGSData)
|
||||||
|
{
|
||||||
|
pages_done[page_num / 8] |= (1u << (page_num % 8));
|
||||||
|
|
||||||
|
GL_INS(" *** NOT FOUND, preloading from local memory");
|
||||||
|
const int dst_x = page_x * page_width;
|
||||||
|
const int dst_y = page_y * page_height;
|
||||||
|
preload_page(dst_x, dst_y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GL_INS(" *** NOT FOUND");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next_page:
|
||||||
|
current_TBP0 += page_blocks;
|
||||||
|
page_x++;
|
||||||
|
if (page_x == width_in_pages)
|
||||||
|
{
|
||||||
|
start_TBP0 += row_page_increment * page_blocks;
|
||||||
|
current_TBP0 = start_TBP0;
|
||||||
|
page_x = 0;
|
||||||
|
page_y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find anything, abort.
|
||||||
|
if (copy_count == 0)
|
||||||
|
{
|
||||||
|
GL_INS("No sources found.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually do the drawing.
|
||||||
|
if (lmtex_mapped)
|
||||||
|
lmtex->Unmap();
|
||||||
|
|
||||||
|
// Allocate our render target for drawing everything to.
|
||||||
|
GSTexture* dtex = g_gs_device->CreateRenderTarget(scaled_width, scaled_height, GSTexture::Format::Color, true);
|
||||||
|
dtex->SetScale(scale);
|
||||||
|
m_source_memory_usage += dtex->GetMemUsage();
|
||||||
|
|
||||||
|
// Sort rect list by the texture, we want to batch as many as possible together.
|
||||||
|
g_gs_device->SortMultiStretchRects(copy_queue, copy_count);
|
||||||
|
g_gs_device->DrawMultiStretchRects(copy_queue, copy_count, dtex, ShaderConvert::COPY);
|
||||||
|
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||||
|
|
||||||
|
if (lmtex)
|
||||||
|
g_gs_device->Recycle(lmtex);
|
||||||
|
|
||||||
|
Source* src = new Source(TEX0, TEXA);
|
||||||
|
src->m_texture = dtex;
|
||||||
|
src->m_end_block = end_block;
|
||||||
|
src->m_target = true;
|
||||||
|
|
||||||
|
// Can't use the normal SetPages() here, it'll try to use TW/TH, which might be bad.
|
||||||
|
src->m_pages = g_gs_renderer->m_context->offset.tex.pageLooperForRect(GSVector4i(0, 0, tex_width, tex_height));
|
||||||
|
|
||||||
|
m_src.Add(src, TEX0, g_gs_renderer->m_context->offset.tex);
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
// This really needs a better home...
|
// This really needs a better home...
|
||||||
extern bool FMVstarted;
|
extern bool FMVstarted;
|
||||||
|
|
||||||
|
|
|
@ -394,6 +394,8 @@ protected:
|
||||||
// TODO: virtual void Write(Source* s, const GSVector4i& r) = 0;
|
// TODO: virtual void Write(Source* s, const GSVector4i& r) = 0;
|
||||||
// TODO: virtual void Write(Target* t, const GSVector4i& r) = 0;
|
// TODO: virtual void Write(Target* t, const GSVector4i& r) = 0;
|
||||||
|
|
||||||
|
Source* CreateMergedSource(GIFRegTEX0 TEX0, GIFRegTEXA TEXA, SourceRegion region, const GSVector2& scale);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSTextureCache();
|
GSTextureCache();
|
||||||
~GSTextureCache();
|
~GSTextureCache();
|
||||||
|
|
|
@ -587,7 +587,7 @@ void GSDeviceVK::DrawMultiStretchRects(
|
||||||
last_tex = rects[i].src;
|
last_tex = rects[i].src;
|
||||||
last_linear = rects[i].linear;
|
last_linear = rects[i].linear;
|
||||||
first += count;
|
first += count;
|
||||||
count = 0;
|
count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DoMultiStretchRects(rects + first, count, static_cast<GSTextureVK*>(dTex), shader);
|
DoMultiStretchRects(rects + first, count, static_cast<GSTextureVK*>(dTex), shader);
|
||||||
|
|
|
@ -691,8 +691,11 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GSHWFixId::TextureInsideRT:
|
case GSHWFixId::TextureInsideRT:
|
||||||
config.UserHacks_TextureInsideRt = (value > 0);
|
{
|
||||||
break;
|
if (value >= 0 && value <= static_cast<int>(GSTextureInRtMode::MergeTargets))
|
||||||
|
config.UserHacks_TextureInsideRt = static_cast<GSTextureInRtMode>(value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case GSHWFixId::AlignSprite:
|
case GSHWFixId::AlignSprite:
|
||||||
config.UserHacks_AlignSpriteX = (value > 0);
|
config.UserHacks_AlignSpriteX = (value > 0);
|
||||||
|
|
|
@ -517,6 +517,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
|
||||||
OpEqu(UserHacks_CPUSpriteRenderBW) &&
|
OpEqu(UserHacks_CPUSpriteRenderBW) &&
|
||||||
OpEqu(UserHacks_CPUCLUTRender) &&
|
OpEqu(UserHacks_CPUCLUTRender) &&
|
||||||
OpEqu(UserHacks_GPUTargetCLUTMode) &&
|
OpEqu(UserHacks_GPUTargetCLUTMode) &&
|
||||||
|
OpEqu(UserHacks_TextureInsideRt) &&
|
||||||
OpEqu(OverrideTextureBarriers) &&
|
OpEqu(OverrideTextureBarriers) &&
|
||||||
OpEqu(OverrideGeometryShaders) &&
|
OpEqu(OverrideGeometryShaders) &&
|
||||||
|
|
||||||
|
@ -647,7 +648,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
GSSettingBoolEx(UserHacks_DisableSafeFeatures, "UserHacks_Disable_Safe_Features");
|
GSSettingBoolEx(UserHacks_DisableSafeFeatures, "UserHacks_Disable_Safe_Features");
|
||||||
GSSettingBoolEx(UserHacks_MergePPSprite, "UserHacks_merge_pp_sprite");
|
GSSettingBoolEx(UserHacks_MergePPSprite, "UserHacks_merge_pp_sprite");
|
||||||
GSSettingBoolEx(UserHacks_WildHack, "UserHacks_WildHack");
|
GSSettingBoolEx(UserHacks_WildHack, "UserHacks_WildHack");
|
||||||
GSSettingBoolEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt");
|
GSSettingIntEnumEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt");
|
||||||
GSSettingBoolEx(UserHacks_TargetPartialInvalidation, "UserHacks_TargetPartialInvalidation");
|
GSSettingBoolEx(UserHacks_TargetPartialInvalidation, "UserHacks_TargetPartialInvalidation");
|
||||||
GSSettingBoolEx(UserHacks_EstimateTextureRegion, "UserHacks_EstimateTextureRegion");
|
GSSettingBoolEx(UserHacks_EstimateTextureRegion, "UserHacks_EstimateTextureRegion");
|
||||||
GSSettingBoolEx(FXAA, "fxaa");
|
GSSettingBoolEx(FXAA, "fxaa");
|
||||||
|
@ -775,7 +776,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||||
UserHacks_DisableDepthSupport = false;
|
UserHacks_DisableDepthSupport = false;
|
||||||
UserHacks_CPUFBConversion = false;
|
UserHacks_CPUFBConversion = false;
|
||||||
UserHacks_ReadTCOnClose = false;
|
UserHacks_ReadTCOnClose = false;
|
||||||
UserHacks_TextureInsideRt = false;
|
UserHacks_TextureInsideRt = GSTextureInRtMode::Disabled;
|
||||||
UserHacks_TargetPartialInvalidation = false;
|
UserHacks_TargetPartialInvalidation = false;
|
||||||
UserHacks_EstimateTextureRegion = false;
|
UserHacks_EstimateTextureRegion = false;
|
||||||
UserHacks_TCOffsetX = 0;
|
UserHacks_TCOffsetX = 0;
|
||||||
|
|
Loading…
Reference in New Issue