GS: Improve target size calcs and remove conservative framebuffer

This commit is contained in:
Connor McLaughlin 2022-07-23 17:41:56 +10:00 committed by refractionpcsx2
parent f218e11d78
commit 6a144f86cf
15 changed files with 261 additions and 248 deletions

View File

@ -186,7 +186,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic)); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.conservativeBufferAllocation, "EmuCore/GS", "conservative_framebuffer", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.texturePreloading, "EmuCore/GS", "texture_preloading", SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.texturePreloading, "EmuCore/GS", "texture_preloading",
static_cast<int>(TexturePreloadingLevel::Off)); static_cast<int>(TexturePreloadingLevel::Off));
@ -355,10 +354,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Implement a more accurate algorithm to compute GS destination alpha testing. " tr("Implement a more accurate algorithm to compute GS destination alpha testing. "
"It improves shadow and transparency rendering.")); "It improves shadow and transparency rendering."));
dialog->registerWidgetHelp(m_ui.conservativeBufferAllocation, tr("Conservative Buffer Allocation"), tr("Checked"),
tr("Disabled: Reserves a larger framebuffer to prevent FMV flickers. Increases GPU/memory requirements. "
"Disabling this can amplify stuttering due to low RAM/VRAM."));
dialog->registerWidgetHelp(m_ui.gpuPaletteConversion, tr("GPU Palette Conversion"), tr("Unchecked"), dialog->registerWidgetHelp(m_ui.gpuPaletteConversion, tr("GPU Palette Conversion"), tr("Unchecked"),
tr("When enabled GPU converts colormap-textures, otherwise the CPU will. " tr("When enabled GPU converts colormap-textures, otherwise the CPU will. "
"It is a trade-off between GPU and CPU.")); "It is a trade-off between GPU and CPU."));

View File

@ -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>1</number>
</property> </property>
<property name="documentMode"> <property name="documentMode">
<bool>true</bool> <bool>true</bool>
@ -642,38 +642,6 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="9" column="0" colspan="2">
<layout class="QGridLayout" name="basicCheckboxGridLayout">
<item row="1" column="0">
<widget class="QCheckBox" name="gpuPaletteConversion">
<property name="text">
<string>GPU Palette Conversion</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="conservativeBufferAllocation">
<property name="text">
<string>Conservative Buffer Allocation</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="accurateDATE">
<property name="text">
<string>Accurate Destination Alpha Test</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_20"> <widget class="QLabel" name="label_20">
<property name="text"> <property name="text">
@ -700,6 +668,31 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="9" column="0" colspan="2">
<layout class="QGridLayout" name="basicCheckboxGridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="accurateDATE">
<property name="text">
<string>Accurate Destination Alpha Test</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="gpuPaletteConversion">
<property name="text">
<string>GPU Palette Conversion</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QGroupBox" name="hardwareFixesTab"> <widget class="QGroupBox" name="hardwareFixesTab">

View File

@ -461,7 +461,6 @@ struct Pcsx2Config
HWDisableReadbacks : 1, HWDisableReadbacks : 1,
AccurateDATE : 1, AccurateDATE : 1,
GPUPaletteConversion : 1, GPUPaletteConversion : 1,
ConservativeFramebuffer : 1,
AutoFlushSW : 1, AutoFlushSW : 1,
PreloadFrameWithGSData : 1, PreloadFrameWithGSData : 1,
WrapGSMem : 1, WrapGSMem : 1,

View File

@ -829,7 +829,6 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// reload texture cache when trilinear filtering or TC options change // reload texture cache when trilinear filtering or TC options change
if ( if (
(GSConfig.UseHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) || (GSConfig.UseHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) ||
GSConfig.ConservativeFramebuffer != old_config.ConservativeFramebuffer ||
GSConfig.TexturePreloading != old_config.TexturePreloading || GSConfig.TexturePreloading != old_config.TexturePreloading ||
GSConfig.UserHacks_TriFilter != old_config.UserHacks_TriFilter || GSConfig.UserHacks_TriFilter != old_config.UserHacks_TriFilter ||
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion || GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
@ -1456,7 +1455,6 @@ void GSApp::Init()
m_default_configuration["pcrtc_overscan"] = "0"; m_default_configuration["pcrtc_overscan"] = "0";
m_default_configuration["IntegerScaling"] = "0"; m_default_configuration["IntegerScaling"] = "0";
m_default_configuration["deinterlace"] = "7"; m_default_configuration["deinterlace"] = "7";
m_default_configuration["conservative_framebuffer"] = "1";
m_default_configuration["linear_present"] = "1"; m_default_configuration["linear_present"] = "1";
m_default_configuration["LoadTextureReplacements"] = "0"; m_default_configuration["LoadTextureReplacements"] = "0";
m_default_configuration["LoadTextureReplacementsAsync"] = "1"; m_default_configuration["LoadTextureReplacementsAsync"] = "1";

View File

@ -76,12 +76,6 @@ __forceinline bool BitEqual(const T& a, const T& b)
return eqb; return eqb;
} }
#ifdef ENABLE_ACCURATE_BUFFER_EMULATION
static const GSVector2i default_rt_size(2048, 2048);
#else
static const GSVector2i default_rt_size(0, 0);
#endif
extern Pcsx2Config::GSOptions GSConfig; extern Pcsx2Config::GSOptions GSConfig;
// Maximum texture size to skip preload/hash path. // Maximum texture size to skip preload/hash path.

View File

@ -20,12 +20,11 @@
#include "GS/Renderers/SW/GSTextureCacheSW.h" #include "GS/Renderers/SW/GSTextureCacheSW.h"
#include "GS/Renderers/SW/GSDrawScanline.h" #include "GS/Renderers/SW/GSDrawScanline.h"
#include "Host.h" #include "Host.h"
#include "common/Align.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
GSRendererHW::GSRendererHW() GSRendererHW::GSRendererHW()
: GSRenderer() : GSRenderer()
, m_width(default_rt_size.x)
, m_height(default_rt_size.y)
, m_tc(new GSTextureCache()) , m_tc(new GSTextureCache())
, m_src(nullptr) , m_src(nullptr)
, m_reset(false) , m_reset(false)
@ -50,7 +49,7 @@ GSRendererHW::GSRendererHW()
ResetStates(); ResetStates();
} }
void GSRendererHW::SetScaling() GSVector2i GSRendererHW::GetOutputSize(int real_h)
{ {
GSVector2i crtc_size(GetResolution()); GSVector2i crtc_size(GetResolution());
@ -90,63 +89,10 @@ void GSRendererHW::SetScaling()
} }
} }
// Details of (potential) perf impact of a big framebuffer // Include negative display offsets in the height here.
// 1/ extra memory crtc_size.y = std::max(crtc_size.y, real_h);
// 2/ texture cache framebuffer rescaling/copy
// 3/ upload of framebuffer (preload hack)
// 4/ framebuffer clear (color/depth/stencil)
// 5/ read back of the frambuffer
//
// With the solution
// 1/ Nothing to do.Except the texture cache bug (channel shuffle effect)
// most of the market is 1GB of VRAM (and soon 2GB)
// 2/ limit rescaling/copy to the valid data of the framebuffer
// 3/ ??? no solution so far
// 4a/ stencil can be limited to valid data.
// 4b/ is it useful to clear color? depth? (in any case, it ought to be few operation)
// 5/ limit the read to the valid data
// Framebuffer width is always a multiple of 64 so at certain cases it can't cover some weird width values. return crtc_size * GSVector2i(static_cast<int>(GSConfig.UpscaleMultiplier), static_cast<int>(GSConfig.UpscaleMultiplier));
// 480P , 576P use width as 720 which is not referencable by FBW * 64. so it produces 704 ( the closest value multiple by 64).
// In such cases, let's just use the CRTC width.
const int fb_width = std::max({(int)m_context->FRAME.FBW * 64, crtc_size.x, 512});
// GS doesn't have a specific register for the FrameBuffer height. so we get the height
// from physical units of the display rectangle in case the game uses a heigher value of height.
//
// Gregory: the framebuffer must have enough room to draw
// * at least 2 frames such as FMV (see OI_BlitFMV)
// * high resolution game such as snowblind engine game
//
// Autodetection isn't a good idea because it will create flickering
// If memory consumption is an issue, there are 2 possibilities
// * 1/ Avoid to create hundreds of RT
// * 2/ Use sparse texture (requires recent HW)
//
// Avoid to alternate between 640x1280 and 1280x1024 on snow blind engine game
// int fb_height = (fb_width < 1024) ? 1280 : 1024;
//
// Until performance issue is properly fixed, let's keep an option to reduce the framebuffer size.
//
// m_large_framebuffer has been inverted to m_conservative_framebuffer, it isn't an option that benefits being enabled all the time for everyone.
int fb_height = MAX_FRAMEBUFFER_HEIGHT;
if (GSConfig.ConservativeFramebuffer)
{
fb_height = fb_width < 1024 ? std::max(512, crtc_size.y) : 1024;
}
const int upscaled_fb_w = fb_width * GSConfig.UpscaleMultiplier;
const int upscaled_fb_h = fb_height * GSConfig.UpscaleMultiplier;
const bool good_rt_size = m_width >= upscaled_fb_w && m_height >= upscaled_fb_h;
// No need to resize for native/custom resolutions as default size will be enough for native and we manually get RT Buffer size for custom.
// don't resize until the display rectangle and register states are stabilized.
if (good_rt_size)
return;
m_tc->RemovePartial();
m_width = upscaled_fb_w;
m_height = upscaled_fb_h;
printf("Frame buffer size set to %dx%d (%dx%d)\n", fb_width, fb_height, m_width, m_height);
} }
void GSRendererHW::SetTCOffset() void GSRendererHW::SetTCOffset()
@ -192,11 +138,6 @@ void GSRendererHW::SetGameCRC(u32 crc, int options)
bool GSRendererHW::CanUpscale() bool GSRendererHW::CanUpscale()
{ {
if (m_hacks.m_cu && !(this->*m_hacks.m_cu)())
{
return false;
}
return GSConfig.UpscaleMultiplier != 1; return GSConfig.UpscaleMultiplier != 1;
} }
@ -227,20 +168,12 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
if (m_reset) if (m_reset)
{ {
m_tc->RemoveAll(); m_tc->RemoveAll();
// Reset RT size.
m_width = default_rt_size.x;
m_height = default_rt_size.y;
m_reset = false; m_reset = false;
} }
if (GSConfig.LoadTextureReplacements) if (GSConfig.LoadTextureReplacements)
GSTextureReplacements::ProcessAsyncLoadedTextures(); GSTextureReplacements::ProcessAsyncLoadedTextures();
//Check if the frame buffer width or display width has changed
SetScaling();
GSRenderer::VSync(field, registers_written); GSRenderer::VSync(field, registers_written);
m_tc->IncAge(); m_tc->IncAge();
@ -286,7 +219,7 @@ GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
GSTexture* t = NULL; GSTexture* t = NULL;
if (GSTextureCache::Target* rt = m_tc->LookupTarget(TEX0, GetTargetSize(), fb_height)) if (GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, GetOutputSize(fb_height), fb_height))
{ {
t = rt->m_texture; t = rt->m_texture;
@ -321,7 +254,8 @@ GSTexture* GSRendererHW::GetFeedbackOutput()
TEX0.TBW = m_regs->EXTBUF.EXBW; TEX0.TBW = m_regs->EXTBUF.EXBW;
TEX0.PSM = m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPFB.PSM; TEX0.PSM = m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPFB.PSM;
GSTextureCache::Target* rt = m_tc->LookupTarget(TEX0, GetTargetSize(), /*GetFrameRect(i).bottom*/ m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPLAY.DH); const int fb_height = /*GetFrameRect(i).bottom*/ m_regs->DISP[m_regs->EXTBUF.FBIN & 1].DISPLAY.DH;
GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, GetOutputSize(fb_height), fb_height);
GSTexture* t = rt->m_texture; GSTexture* t = rt->m_texture;
@ -763,35 +697,41 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
} }
} }
GSVector2 GSRendererHW::GetTextureScaleFactor(const bool force_upscaling)
{
GSVector2 scale_factor{ 1.0f, 1.0f };
if (force_upscaling || CanUpscale())
{
const int multiplier = GetUpscaleMultiplier();
scale_factor.x = multiplier;
scale_factor.y = multiplier;
}
return scale_factor;
}
GSVector2 GSRendererHW::GetTextureScaleFactor() GSVector2 GSRendererHW::GetTextureScaleFactor()
{ {
return GetTextureScaleFactor(false); const float f_upscale = static_cast<float>(GetUpscaleMultiplier());
return GSVector2(f_upscale, f_upscale);
} }
GSVector2i GSRendererHW::GetTargetSize() GSVector2i GSRendererHW::GetTargetSize()
{ {
const GSVector2i t_size = { m_width, m_height }; // Don't blindly expand out to the scissor size if we're not drawing to it.
if (GetUpscaleMultiplier() == 1 || CanUpscale()) // e.g. Burnout 3, God of War II, etc.
return t_size; u32 min_height = std::min<u32>(m_context->scissor.in.w, m_r.w);
// Undo the upscaling for native resolution draws.
const GSVector2 up_s = GetTextureScaleFactor(true); // Another thing these games like to do, is draw a 512x896 shuffle, which would result in us
return { // expanding the target out to 896 height, but the extra area would all be black, with the
static_cast<int>(std::ceil(static_cast<float>(t_size.x) / up_s.x)), // draw effectively changing nothing for the new area. So, instead, lets try to detect these
static_cast<int>(std::ceil(static_cast<float>(t_size.y) / up_s.y)), // draws by double-checking we're not stretching the texture (gradient of <1).
}; if (PRIM->TME && m_vt.m_primclass == GS_SPRITE_CLASS && m_src && (m_src->m_target || m_src->m_from_target))
{
const float diff = std::abs((m_vt.m_max.p.y - m_vt.m_min.p.y) - (m_vt.m_max.t.y - m_vt.m_min.t.y));
if (diff <= 1.0f)
{
// Clamp to the texture size. We're working in unscaled coordinates here, so undo the upscaling.
min_height = std::min(min_height, static_cast<u32>(static_cast<float>(m_src->m_texture->GetHeight()) / m_src->m_texture->GetScale().y));
}
}
// Align to even lines, reduces the chance of tiny resizes.
min_height = Common::AlignUpPow2(min_height, 2);
const u32 width = m_context->FRAME.FBW * 64u;
const u32 height = m_tc->GetTargetHeight(m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, min_height);
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
return GSVector2i(static_cast<int>(width * GSConfig.UpscaleMultiplier), static_cast<int>(height * GSConfig.UpscaleMultiplier));
} }
void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r)
@ -1605,6 +1545,8 @@ void GSRendererHW::Draw()
} }
} }
// The rectangle of the draw
m_r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in));
const GSVector2i t_size = GetTargetSize(); const GSVector2i t_size = GetTargetSize();
TEX0.TBP0 = context->FRAME.Block(); TEX0.TBP0 = context->FRAME.Block();
@ -1612,24 +1554,16 @@ void GSRendererHW::Draw()
TEX0.PSM = context->FRAME.PSM; TEX0.PSM = context->FRAME.PSM;
GSTextureCache::Target* rt = nullptr; GSTextureCache::Target* rt = nullptr;
GSTexture* rt_tex = nullptr;
if (!no_rt) if (!no_rt)
{
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm); rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm);
rt_tex = rt->m_texture;
}
TEX0.TBP0 = context->ZBUF.Block(); TEX0.TBP0 = context->ZBUF.Block();
TEX0.TBW = context->FRAME.FBW; TEX0.TBW = context->FRAME.FBW;
TEX0.PSM = context->ZBUF.PSM; TEX0.PSM = context->ZBUF.PSM;
GSTextureCache::Target* ds = nullptr; GSTextureCache::Target* ds = nullptr;
GSTexture* ds_tex = nullptr;
if (!no_ds) if (!no_ds)
{
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite()); ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite());
ds_tex = ds->m_texture;
}
if (rt) if (rt)
{ {
@ -1639,38 +1573,24 @@ void GSRendererHW::Draw()
rt->m_32_bits_fmt = m_texture_shuffle || (GSLocalMemory::m_psm[context->FRAME.PSM].bpp != 16); rt->m_32_bits_fmt = m_texture_shuffle || (GSLocalMemory::m_psm[context->FRAME.PSM].bpp != 16);
} }
// The rectangle of the draw {
m_r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(context->scissor.in)); // We still need to make sure the dimensions of the targets match.
const GSVector2 up_s(GetTextureScaleFactor());
const int new_w = std::max(t_size.x, std::max(rt ? rt->m_texture->GetWidth() : 0, ds ? ds->m_texture->GetWidth() : 0));
const int new_h = std::max(t_size.y, std::max(rt ? rt->m_texture->GetHeight() : 0, ds ? ds->m_texture->GetHeight() : 0));
// Ensure draw rect is clamped to framebuffer size. Necessary for updating valid area.
m_r = m_r.rintersect(GSVector4i(0, 0, new_w, new_h));
if (rt)
{ {
const GSVector2 up_s = GetTextureScaleFactor(); pxAssert(rt->m_texture->GetScale() == up_s);
const int up_w = static_cast<int>(std::ceil(static_cast<float>(m_r.z) * up_s.x)); rt->ResizeTexture(new_w, new_h, up_s);
const int up_h = static_cast<int>(std::ceil(static_cast<float>(m_r.w) * up_s.y));
const int new_w = std::max(up_w, std::max(rt_tex ? rt_tex->GetWidth() : 0, ds_tex ? ds_tex->GetWidth() : 0));
const int new_h = std::max(up_h, std::max(rt_tex ? rt_tex->GetHeight() : 0, ds_tex ? ds_tex->GetHeight() : 0));
std::array<GSTextureCache::Target*, 2> ts{ rt, ds };
for (GSTextureCache::Target* t : ts)
{
if (t)
{
// Adjust texture size to fit current draw if necessary.
GSTexture* tex = t->m_texture;
assert(up_s == tex->GetScale());
const int w = tex->GetWidth();
const int h = tex->GetHeight();
if (w != new_w || h != new_h)
{
const bool is_rt = t == rt;
t->m_texture = is_rt ?
g_gs_device->CreateSparseRenderTarget(new_w, new_h, tex->GetFormat()) :
g_gs_device->CreateSparseDepthStencil(new_w, new_h, tex->GetFormat());
const GSVector4i r{ 0, 0, w, h };
g_gs_device->CopyRect(tex, t->m_texture, r, 0, 0);
g_gs_device->Recycle(tex);
t->m_texture->SetScale(up_s);
(is_rt ? rt_tex : ds_tex) = t->m_texture;
}
} }
if (ds)
{
pxAssert(ds->m_texture->GetScale() == up_s);
ds->ResizeTexture(new_w, new_h, up_s);
} }
} }
@ -1708,20 +1628,20 @@ void GSRendererHW::Draw()
{ {
s = StringUtil::StdStringFromFormat("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM)); s = StringUtil::StdStringFromFormat("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
if (rt_tex) if (rt->m_texture)
rt_tex->Save(m_dump_root + s); rt->m_texture->Save(m_dump_root + s);
} }
if (s_savez && s_n >= s_saven) if (s_savez && s_n >= s_saven)
{ {
s = StringUtil::StdStringFromFormat("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM)); s = StringUtil::StdStringFromFormat("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM));
if (ds_tex) if (ds->m_texture)
ds_tex->Save(m_dump_root + s); ds->m_texture->Save(m_dump_root + s);
} }
} }
if (m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt_tex, ds_tex, m_src)) if (m_hacks.m_oi && !(this->*m_hacks.m_oi)(rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, m_src))
{ {
GL_INS("Warning skipping a draw call (%d)", s_n); GL_INS("Warning skipping a draw call (%d)", s_n);
return; return;
@ -1746,7 +1666,7 @@ void GSRendererHW::Draw()
OI_GsMemClear(); OI_GsMemClear();
OI_DoubleHalfClear(rt_tex, ds_tex); OI_DoubleHalfClear(rt, ds);
} }
} }
@ -1801,7 +1721,7 @@ void GSRendererHW::Draw()
// //
DrawPrims(rt_tex, ds_tex, m_src); DrawPrims(rt ? rt->m_texture : nullptr, ds ? ds->m_texture : nullptr, m_src);
// //
@ -1853,16 +1773,16 @@ void GSRendererHW::Draw()
{ {
s = StringUtil::StdStringFromFormat("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM)); s = StringUtil::StdStringFromFormat("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
if (rt_tex) if (rt)
rt_tex->Save(m_dump_root + s); rt->m_texture->Save(m_dump_root + s);
} }
if (s_savez && s_n >= s_saven) if (s_savez && s_n >= s_saven)
{ {
s = StringUtil::StdStringFromFormat("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM)); s = StringUtil::StdStringFromFormat("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM));
if (ds_tex) if (ds)
ds_tex->Save(m_dump_root + s); rt->m_texture->Save(m_dump_root + s);
} }
if (s_savel > 0 && (s_n - s_saven) > s_savel) if (s_savel > 0 && (s_n - s_saven) > s_savel)
@ -3217,10 +3137,6 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
area_out.x, area_out.y, area_out.z, area_out.w); area_out.x, area_out.y, area_out.z, area_out.w);
#endif #endif
const GSVector2i& rtsize = ds ? ds->GetSize() : rt->GetSize();
const GSVector2& rtscale = ds ? ds->GetScale() : rt->GetScale();
const GSDevice::FeatureSupport features(g_gs_device->Features());
const bool DATE = m_context->TEST.DATE && m_context->FRAME.PSM != PSM_PSMCT24; const bool DATE = m_context->TEST.DATE && m_context->FRAME.PSM != PSM_PSMCT24;
bool DATE_PRIMID = false; bool DATE_PRIMID = false;
bool DATE_BARRIER = false; bool DATE_BARRIER = false;
@ -3250,6 +3166,7 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
// Upscaling hack to avoid various line/grid issues // Upscaling hack to avoid various line/grid issues
MergeSprite(tex); MergeSprite(tex);
const GSDevice::FeatureSupport features(g_gs_device->Features());
if (!features.framebuffer_fetch) if (!features.framebuffer_fetch)
m_prim_overlap = PrimitiveOverlap(); m_prim_overlap = PrimitiveOverlap();
else else
@ -3418,6 +3335,8 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
m_conf.vs.fst = PRIM->FST; m_conf.vs.fst = PRIM->FST;
// FIXME D3D11 and GL support half pixel center. Code could be easier!!! // FIXME D3D11 and GL support half pixel center. Code could be easier!!!
const GSVector2i rtsize(m_conf.ds ? m_conf.ds->GetSize() : m_conf.rt->GetSize());
const GSVector2 rtscale(m_conf.ds ? m_conf.ds->GetScale() : m_conf.rt->GetScale());
const float sx = 2.0f * rtscale.x / (rtsize.x << 4); const float sx = 2.0f * rtscale.x / (rtsize.x << 4);
const float sy = 2.0f * rtscale.y / (rtsize.y << 4); const float sy = 2.0f * rtscale.y / (rtsize.y << 4);
const float ox = (float)(int)m_context->XYOFFSET.OFX; const float ox = (float)(int)m_context->XYOFFSET.OFX;
@ -4177,10 +4096,8 @@ bool GSRendererHW::SwPrimRender()
GSRendererHW::Hacks::Hacks() GSRendererHW::Hacks::Hacks()
: m_oi_map(m_oi_list) : m_oi_map(m_oi_list)
, m_oo_map(m_oo_list) , m_oo_map(m_oo_list)
, m_cu_map(m_cu_list)
, m_oi(NULL) , m_oi(NULL)
, m_oo(NULL) , m_oo(NULL)
, m_cu(NULL)
{ {
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BigMuthaTruckers, CRC::RegionCount, &GSRendererHW::OI_BigMuthaTruckers)); m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::BigMuthaTruckers, CRC::RegionCount, &GSRendererHW::OI_BigMuthaTruckers));
m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::OI_DBZBTGames)); m_oi_list.push_back(HackEntry<OI_Ptr>(CRC::DBZBT2, CRC::RegionCount, &GSRendererHW::OI_DBZBTGames));
@ -4206,7 +4123,6 @@ void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
m_oi = m_oi_map[hash]; m_oi = m_oi_map[hash];
m_oo = m_oo_map[hash]; m_oo = m_oo_map[hash];
m_cu = m_cu_map[hash];
if (GSConfig.PointListPalette) if (GSConfig.PointListPalette)
{ {
@ -4220,7 +4136,7 @@ void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
// Trick to do a fast clear on the GS // Trick to do a fast clear on the GS
// Set frame buffer pointer on the start of the buffer. Set depth buffer pointer on the half buffer // Set frame buffer pointer on the start of the buffer. Set depth buffer pointer on the half buffer
// FB + depth write will fill the full buffer. // FB + depth write will fill the full buffer.
void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds) void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds)
{ {
// Note gs mem clear must be tested before calling this function // Note gs mem clear must be tested before calling this function
@ -4266,20 +4182,35 @@ void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
GL_INS("OI_DoubleHalfClear:%s: base %x half %x. w_pages %d h_pages %d fbw %d. Color %x", GL_INS("OI_DoubleHalfClear:%s: base %x half %x. w_pages %d h_pages %d fbw %d. Color %x",
clear_depth ? "depth" : "target", base << 5, half << 5, w_pages, h_pages, m_context->FRAME.FBW, color); clear_depth ? "depth" : "target", base << 5, half << 5, w_pages, h_pages, m_context->FRAME.FBW, color);
// Commit texture with a factor 2 on the height // Handle the case where the game stacks FBP and ZBP immediately after one another.
GSTexture* t = clear_depth ? ds : rt; // We incorrectly compute the height here, because both the scissor and draw rectangle will only be half
const GSVector4i commitRect = ComputeBoundingBox(t->GetScale(), t->GetSize()); // the height of what's effectively being cleared. Spider-Man 2's shadows are a good test case here: it
t->CommitRegion(GSVector2i(commitRect.z, 2 * commitRect.w)); // draws the shadow map to a 128x128 texture, but relies on a 1 pixel border around the edge to "cut off"
// the shadows. We cap it to a 256 height, because having a >=512 height framebuffer is very rare, and it
// stops us doubling actual framebuffers unintentionally (very common).
GSTextureCache::Target* t = clear_depth ? ds : rt;
const u32 unscaled_height = static_cast<u32>(static_cast<float>(t->m_texture->GetHeight()) / t->m_texture->GetScale().y);
if (unscaled_height == m_context->scissor.in.w && unscaled_height <= 256)
{
t->ResizeTexture(t->m_texture->GetWidth(), t->m_texture->GetHeight() * 2, t->m_texture->GetScale());
if (clear_depth)
rt = nullptr;
else
ds = nullptr;
// Feed it back into the height cache.
m_tc->GetTargetHeight(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, unscaled_height * 2);
}
if (clear_depth) if (clear_depth)
{ {
// Only pure clear are supported for depth // Only pure clear are supported for depth
ASSERT(color == 0); ASSERT(color == 0);
g_gs_device->ClearDepth(t); g_gs_device->ClearDepth(ds->m_texture);
} }
else else
{ {
g_gs_device->ClearRenderTarget(t, color); g_gs_device->ClearRenderTarget(rt->m_texture, color);
} }
} }
} }
@ -4295,10 +4226,10 @@ void GSRendererHW::OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds)
// If both buffers are side by side we can expect a fast clear in on-going // If both buffers are side by side we can expect a fast clear in on-going
const u32 color = v[1].RGBAQ.U32[0]; const u32 color = v[1].RGBAQ.U32[0];
const GSVector4i commitRect = ComputeBoundingBox(rt->GetScale(), rt->GetSize()); const GSVector4i commitRect = ComputeBoundingBox(rt->m_texture->GetScale(), rt->m_texture->GetSize());
rt->CommitRegion(GSVector2i(commitRect.z, commitRect.w)); rt->m_texture->CommitRegion(GSVector2i(commitRect.z, commitRect.w));
g_gs_device->ClearRenderTarget(rt, color); g_gs_device->ClearRenderTarget(rt->m_texture, color);
} }
} }
@ -4689,12 +4620,14 @@ bool GSRendererHW::OI_SonicUnleashed(GSTexture* rt, GSTexture* ds, GSTextureCach
GL_INS("OI_SonicUnleashed replace draw by a copy"); GL_INS("OI_SonicUnleashed replace draw by a copy");
GSTextureCache::Target* src = m_tc->LookupTarget(Texture, GetTargetSize(), GSTextureCache::RenderTarget, true); GSTextureCache::Target* src = m_tc->LookupTarget(Texture, GSVector2i(1, 1), GSTextureCache::RenderTarget, true);
const GSVector2i size = rt->GetSize(); const GSVector2i rt_size(rt->GetSize());
const GSVector2i src_size(src->m_texture->GetSize());
const GSVector2i copy_size(std::min(rt_size.x, src_size.x), std::min(rt_size.y, src_size.y));
const GSVector4 sRect(0, 0, 1, 1); const GSVector4 sRect(0.0f, 0.0f, static_cast<float>(copy_size.x) / static_cast<float>(src_size.x), static_cast<float>(copy_size.y) / static_cast<float>(src_size.y));
const GSVector4 dRect(0, 0, size.x, size.y); const GSVector4 dRect(0, 0, copy_size.x, copy_size.y);
g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false); g_gs_device->StretchRect(src->m_texture, sRect, rt, dRect, true, true, true, false);

View File

@ -29,9 +29,6 @@ public:
static constexpr int MAX_FRAMEBUFFER_HEIGHT = 1280; static constexpr int MAX_FRAMEBUFFER_HEIGHT = 1280;
private: private:
int m_width;
int m_height;
static constexpr float SSR_UV_TOLERANCE = 1.0f; static constexpr float SSR_UV_TOLERANCE = 1.0f;
#pragma region hacks #pragma region hacks
@ -43,7 +40,7 @@ private:
// Require special argument // Require special argument
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw); bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
void OI_GsMemClear(); // always on void OI_GsMemClear(); // always on
void OI_DoubleHalfClear(GSTexture* rt, GSTexture* ds); // always on void OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCache::Target*& ds); // always on
bool OI_BigMuthaTruckers(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); bool OI_BigMuthaTruckers(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
bool OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); bool OI_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
@ -108,16 +105,13 @@ private:
std::list<HackEntry<OI_Ptr>> m_oi_list; std::list<HackEntry<OI_Ptr>> m_oi_list;
std::list<HackEntry<OO_Ptr>> m_oo_list; std::list<HackEntry<OO_Ptr>> m_oo_list;
std::list<HackEntry<CU_Ptr>> m_cu_list;
FunctionMap<OI_Ptr> m_oi_map; FunctionMap<OI_Ptr> m_oi_map;
FunctionMap<OO_Ptr> m_oo_map; FunctionMap<OO_Ptr> m_oo_map;
FunctionMap<CU_Ptr> m_cu_map;
public: public:
OI_Ptr m_oi; OI_Ptr m_oi;
OO_Ptr m_oo; OO_Ptr m_oo;
CU_Ptr m_cu;
Hacks(); Hacks();
@ -183,15 +177,14 @@ public:
void SetGameCRC(u32 crc, int options) override; void SetGameCRC(u32 crc, int options) override;
bool CanUpscale() override; bool CanUpscale() override;
int GetUpscaleMultiplier() override; int GetUpscaleMultiplier() override;
void SetScaling();
void Lines2Sprites(); void Lines2Sprites();
void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2); void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2);
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba); void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba);
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex); GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize); GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
void MergeSprite(GSTextureCache::Source* tex); void MergeSprite(GSTextureCache::Source* tex);
GSVector2 GetTextureScaleFactor(const bool force_upscaling);
GSVector2 GetTextureScaleFactor() override; GSVector2 GetTextureScaleFactor() override;
GSVector2i GetOutputSize(int real_h);
GSVector2i GetTargetSize(); GSVector2i GetTargetSize();
void Reset(bool hardware_reset) override; void Reset(bool hardware_reset) override;

View File

@ -83,6 +83,7 @@ void GSTextureCache::RemoveAll()
m_hash_cache_memory_usage = 0; m_hash_cache_memory_usage = 0;
m_palette_map.Clear(); m_palette_map.Clear();
m_target_heights.clear();
} }
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette) GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette)
@ -416,7 +417,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_h) GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_h)
{ {
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM]; const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
const GSVector2& new_s = g_gs_renderer->GetTextureScaleFactor(); const GSVector2& new_s = static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor();
const u32 bp = TEX0.TBP0; const u32 bp = TEX0.TBP0;
GSVector2 res_size{ 0, 0 }; GSVector2 res_size{ 0, 0 };
GSVector2i new_size{ 0, 0 }; GSVector2i new_size{ 0, 0 };
@ -619,7 +620,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
return dst; return dst;
} }
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h) GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h)
{ {
return LookupTarget(TEX0, size, RenderTarget, true, 0, true, real_h); return LookupTarget(TEX0, size, RenderTarget, true, 0, true, real_h);
} }
@ -1187,6 +1188,20 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
const int scaled_w = static_cast<int>(w * scale.x); const int scaled_w = static_cast<int>(w * scale.x);
const int scaled_h = static_cast<int>(h * scale.y); const int scaled_h = static_cast<int>(h * scale.y);
// Expand the target when we used a more conservative size.
const int required_dh = scaled_dy + scaled_h;
if ((scaled_dx + scaled_w) <= dst->m_texture->GetWidth() && required_dh > dst->m_texture->GetHeight())
{
const int new_height = dy + h;
if (new_height > GSRendererHW::MAX_FRAMEBUFFER_HEIGHT)
return false;
const int scaled_new_height = static_cast<int>(static_cast<float>(new_height) * scale.y);
GL_INS("Resize %dx%d target to %dx%d for move", dst->m_texture->GetWidth(), dst->m_texture->GetHeight(), dst->m_texture->GetHeight(), scaled_new_height);
dst->ResizeTexture(dst->m_texture->GetWidth(), scaled_new_height);
GetTargetHeight(DBP, DBW, DPSM, new_height);
}
// Make sure the copy doesn't go out of bounds (it shouldn't). // Make sure the copy doesn't go out of bounds (it shouldn't).
if ((scaled_sx + scaled_w) > src->m_texture->GetWidth() || (scaled_sy + scaled_h) > src->m_texture->GetHeight() || if ((scaled_sx + scaled_w) > src->m_texture->GetWidth() || (scaled_sy + scaled_h) > src->m_texture->GetHeight() ||
(scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight()) (scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
@ -1230,6 +1245,36 @@ GSTextureCache::Target* GSTextureCache::GetTargetWithSharedBits(u32 BP, u32 PSM)
return nullptr; return nullptr;
} }
u32 GSTextureCache::GetTargetHeight(u32 fbp, u32 fbw, u32 psm, u32 min_height)
{
TargetHeightElem search = {};
search.fbp = fbp;
search.fbw = fbw;
search.psm = psm;
search.height = min_height;
for (auto it = m_target_heights.begin(); it != m_target_heights.end(); ++it)
{
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
if (elem.bits == search.bits)
{
if (elem.height < min_height)
{
DbgCon.WriteLn("Expand height at %x %u %u from %u to %u", fbp, fbw, psm, elem.height, min_height);
elem.height = min_height;
}
m_target_heights.MoveFront(it.Index());
elem.age = 0;
return elem.height;
}
}
DbgCon.WriteLn("New height at %x %u %u: %u", fbp, fbw, psm, min_height);
m_target_heights.push_front(search);
return min_height;
}
// Hack: remove Target that are strictly included in current rt. Typically uses for FMV // Hack: remove Target that are strictly included in current rt. Typically uses for FMV
// For example, game is rendered at 0x800->0x1000, fmv will be uploaded to 0x0->0x2800 // For example, game is rendered at 0x800->0x1000, fmv will be uploaded to 0x0->0x2800
// FIXME In theory, we ought to report the data from the sub rt to the main rt. But let's // FIXME In theory, we ought to report the data from the sub rt to the main rt. But let's
@ -1345,6 +1390,20 @@ void GSTextureCache::IncAge()
} }
} }
} }
for (auto it = m_target_heights.begin(); it != m_target_heights.end();)
{
TargetHeightElem& elem = const_cast<TargetHeightElem&>(*it);
if (elem.age >= max_rt_age)
{
it = m_target_heights.erase(it);
}
else
{
elem.age++;
++it;
}
}
} }
//Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work. //Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work.
@ -1822,7 +1881,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int
t->m_texture = g_gs_device->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil, clear); t->m_texture = g_gs_device->CreateSparseDepthStencil(w, h, GSTexture::Format::DepthStencil, clear);
} }
t->m_texture->SetScale(g_gs_renderer->GetTextureScaleFactor()); t->m_texture->SetScale(static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor());
m_dst[type].push_front(t); m_dst[type].push_front(t);
@ -1999,6 +2058,47 @@ bool GSTextureCache::Surface::Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i
return overlap; return overlap;
} }
void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height)
{
ResizeTexture(new_width, new_height, m_texture->GetScale());
}
void GSTextureCache::Surface::ResizeTexture(int new_width, int new_height, GSVector2 new_scale)
{
const int width = m_texture->GetWidth();
const int height = m_texture->GetHeight();
if (width == new_width && height == new_height)
return;
const bool clear = (new_width > width || new_height > height);
GSTexture* tex = m_texture->IsDepthStencil() ?
g_gs_device->CreateSparseDepthStencil(new_width, new_height, m_texture->GetFormat(), clear) :
g_gs_device->CreateSparseRenderTarget(new_width, new_height, m_texture->GetFormat(), clear);
if (!tex)
{
Console.Error("(GSTextureCache::Surface::ResizeTexture) Failed to allocate %dx%d texture", new_width, new_height);
return;
}
tex->SetScale(new_scale);
const GSVector4i rc(0, 0, std::min(width, new_width), std::min(height, new_height));
if (tex->IsDepthStencil())
{
// Can't do partial copies in DirectX for depth textures, and it's probably not ideal in other
// APIs either. So use a fullscreen quad setting depth instead.
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, false);
}
else
{
// Fast memcpy()-like path for color targets.
g_gs_device->CopyRect(m_texture, tex, rc, 0, 0);
}
g_gs_device->Recycle(m_texture);
m_texture = tex;
}
// GSTextureCache::Source // GSTextureCache::Source
GSTextureCache::Source::Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container) GSTextureCache::Source::Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container)
@ -2410,6 +2510,9 @@ void GSTextureCache::Target::UpdateIfDirtyIntersects(const GSVector4i& rc)
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect) void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
{ {
if (m_valid.eq(GSVector4i::zero()))
m_valid = rect;
else
m_valid = m_valid.runion(rect); m_valid = m_valid.runion(rect);
// Block of the bottom right texel of the validity rectangle, last valid block of the texture // Block of the bottom right texel of the validity rectangle, last valid block of the texture

View File

@ -92,6 +92,9 @@ public:
void UpdateAge(); void UpdateAge();
bool Inside(u32 bp, u32 bw, u32 psm, const GSVector4i& rect); bool Inside(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
bool Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect); bool Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
void ResizeTexture(int new_width, int new_height);
void ResizeTexture(int new_width, int new_height, GSVector2 new_scale);
}; };
struct PaletteKey struct PaletteKey
@ -243,6 +246,25 @@ public:
void RemoveAt(Source* s); void RemoveAt(Source* s);
}; };
struct TargetHeightElem
{
union
{
u32 bits;
struct
{
u32 fbp : 9;
u32 fbw : 6;
u32 psm : 6;
u32 pad : 11;
};
};
u32 height;
u32 age;
};
struct SurfaceOffsetKeyElem struct SurfaceOffsetKeyElem
{ {
u32 psm; u32 psm;
@ -278,6 +300,7 @@ protected:
std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache; std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache;
u64 m_hash_cache_memory_usage = 0; u64 m_hash_cache_memory_usage = 0;
FastList<Target*> m_dst[2]; FastList<Target*> m_dst[2];
FastList<TargetHeightElem> m_target_heights;
static u8* m_temp; static u8* m_temp;
constexpr static size_t S_SURFACE_OFFSET_CACHE_MAX_SIZE = std::numeric_limits<u16>::max(); constexpr static size_t S_SURFACE_OFFSET_CACHE_MAX_SIZE = std::numeric_limits<u16>::max();
std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache; std::unordered_map<SurfaceOffsetKey, SurfaceOffset, SurfaceOffsetKeyHash, SurfaceOffsetKeyEqual> m_surface_offset_cache;
@ -309,12 +332,14 @@ public:
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette = false); Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GSVector4i& r, bool palette = false);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_h = 0); Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_h = 0);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h); Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_h);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly. /// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.
Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const; Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const;
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const; Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;
u32 GetTargetHeight(u32 fbp, u32 fbw, u32 psm, u32 min_height);
void InvalidateVideoMemType(int type, u32 bp); void InvalidateVideoMemType(int type, u32 bp);
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt); void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true); void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);

View File

@ -191,12 +191,6 @@ const char* dialog_message(int ID, bool* updateText)
case IDC_DISABLE_PARTIAL_TC_INV: case IDC_DISABLE_PARTIAL_TC_INV:
return cvtString("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise." return cvtString("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise."
"\n\nThis hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load.\n\nIt helps snowblind engine games."); "\n\nThis hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load.\n\nIt helps snowblind engine games.");
case IDC_CONSERVATIVE_FB:
return cvtString("Disabled: Reserves a larger framebuffer to prevent FMV flickers.\n"
"Increases GPU/memory requirements.\n"
"Disabling this can amplify stuttering due to low RAM/VRAM.\n\n"
"Note: It should be enabled for Armored Core, Destroy All Humans, Gran Turismo and possibly others.\n"
"This option does not improve the graphics or the FPS.");
case IDC_DITHERING: case IDC_DITHERING:
return cvtString("In the PS2's case, it reduces banding between colors and improves the perceived color depth.\n" return cvtString("In the PS2's case, it reduces banding between colors and improves the perceived color depth.\n"
"In the PS1's case, it was used more aggressively due to 16-bit colour.\n" "In the PS1's case, it was used more aggressively due to 16-bit colour.\n"

View File

@ -50,7 +50,6 @@ enum
IDC_PRELOAD_TEXTURES, IDC_PRELOAD_TEXTURES,
IDC_ACCURATE_DATE, IDC_ACCURATE_DATE,
IDC_PALTEX, IDC_PALTEX,
IDC_CONSERVATIVE_FB,
IDC_AFCOMBO, IDC_AFCOMBO,
IDC_DITHERING, IDC_DITHERING,
IDC_MIPMAP_HW, IDC_MIPMAP_HW,

View File

@ -285,7 +285,6 @@ RendererTab::RendererTab(wxWindow* parent)
auto* hw_checks_box = new wxWrapSizer(wxHORIZONTAL); auto* hw_checks_box = new wxWrapSizer(wxHORIZONTAL);
m_ui.addCheckBox(hw_checks_box, "Accurate Destination Alpha Test", "accurate_date", IDC_ACCURATE_DATE, hw_prereq); m_ui.addCheckBox(hw_checks_box, "Accurate Destination Alpha Test", "accurate_date", IDC_ACCURATE_DATE, hw_prereq);
m_ui.addCheckBox(hw_checks_box, "Conservative Buffer Allocation", "conservative_framebuffer", IDC_CONSERVATIVE_FB, upscale_prereq);
auto* paltex_prereq = m_ui.addCheckBox(hw_checks_box, "GPU Palette Conversion", "paltex", IDC_PALTEX, hw_prereq); auto* paltex_prereq = m_ui.addCheckBox(hw_checks_box, "GPU Palette Conversion", "paltex", IDC_PALTEX, hw_prereq);
auto aniso_prereq = [this, paltex_prereq]{ return m_is_hardware && paltex_prereq->GetValue() == false; }; auto aniso_prereq = [this, paltex_prereq]{ return m_is_hardware && paltex_prereq->GetValue() == false; };

View File

@ -270,7 +270,6 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
static const char* s_gs_hw_fix_names[] = { static const char* s_gs_hw_fix_names[] = {
"autoFlush", "autoFlush",
"conservativeFramebuffer",
"cpuFramebufferConversion", "cpuFramebufferConversion",
"disableDepthSupport", "disableDepthSupport",
"wrapGSMem", "wrapGSMem",
@ -317,7 +316,6 @@ bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
case GSHWFixId::Deinterlace: case GSHWFixId::Deinterlace:
case GSHWFixId::Mipmap: case GSHWFixId::Mipmap:
case GSHWFixId::TexturePreloading: case GSHWFixId::TexturePreloading:
case GSHWFixId::ConservativeFramebuffer:
case GSHWFixId::PointListPalette: case GSHWFixId::PointListPalette:
return false; return false;
@ -446,9 +444,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::AutoFlush: case GSHWFixId::AutoFlush:
return (static_cast<int>(config.UserHacks_AutoFlush) == value); return (static_cast<int>(config.UserHacks_AutoFlush) == value);
case GSHWFixId::ConservativeFramebuffer:
return (static_cast<int>(config.ConservativeFramebuffer) == value);
case GSHWFixId::CPUFramebufferConversion: case GSHWFixId::CPUFramebufferConversion:
return (static_cast<int>(config.UserHacks_CPUFBConversion) == value); return (static_cast<int>(config.UserHacks_CPUFBConversion) == value);
@ -542,10 +537,6 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.UserHacks_AutoFlush = (value > 0); config.UserHacks_AutoFlush = (value > 0);
break; break;
case GSHWFixId::ConservativeFramebuffer:
config.ConservativeFramebuffer = (value > 0);
break;
case GSHWFixId::CPUFramebufferConversion: case GSHWFixId::CPUFramebufferConversion:
config.UserHacks_CPUFBConversion = (value > 0); config.UserHacks_CPUFBConversion = (value > 0);
break; break;

View File

@ -60,7 +60,6 @@ namespace GameDatabaseSchema
{ {
// boolean settings // boolean settings
AutoFlush, AutoFlush,
ConservativeFramebuffer,
CPUFramebufferConversion, CPUFramebufferConversion,
DisableDepthSupport, DisableDepthSupport,
WrapGSMem, WrapGSMem,

View File

@ -325,7 +325,6 @@ Pcsx2Config::GSOptions::GSOptions()
HWDisableReadbacks = false; HWDisableReadbacks = false;
AccurateDATE = true; AccurateDATE = true;
GPUPaletteConversion = false; GPUPaletteConversion = false;
ConservativeFramebuffer = true;
AutoFlushSW = true; AutoFlushSW = true;
PreloadFrameWithGSData = false; PreloadFrameWithGSData = false;
WrapGSMem = false; WrapGSMem = false;
@ -546,7 +545,6 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingBool(HWDisableReadbacks); GSSettingBool(HWDisableReadbacks);
GSSettingBoolEx(AccurateDATE, "accurate_date"); GSSettingBoolEx(AccurateDATE, "accurate_date");
GSSettingBoolEx(GPUPaletteConversion, "paltex"); GSSettingBoolEx(GPUPaletteConversion, "paltex");
GSSettingBoolEx(ConservativeFramebuffer, "conservative_framebuffer");
GSSettingBoolEx(AutoFlushSW, "autoflush_sw"); GSSettingBoolEx(AutoFlushSW, "autoflush_sw");
GSSettingBoolEx(PreloadFrameWithGSData, "preload_frame_with_gs_data"); GSSettingBoolEx(PreloadFrameWithGSData, "preload_frame_with_gs_data");
GSSettingBoolEx(WrapGSMem, "wrap_gs_mem"); GSSettingBoolEx(WrapGSMem, "wrap_gs_mem");