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.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.conservativeBufferAllocation, "EmuCore/GS", "conservative_framebuffer", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.texturePreloading, "EmuCore/GS", "texture_preloading",
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. "
"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"),
tr("When enabled GPU converts colormap-textures, otherwise the CPU will. "
"It is a trade-off between GPU and CPU."));

View File

@ -58,7 +58,7 @@
<item>
<widget class="QTabWidget" name="hardwareRendererGroup">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<property name="documentMode">
<bool>true</bool>
@ -642,38 +642,6 @@
</item>
</widget>
</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">
<widget class="QLabel" name="label_20">
<property name="text">
@ -700,6 +668,31 @@
</item>
</widget>
</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>
</widget>
<widget class="QGroupBox" name="hardwareFixesTab">

View File

@ -461,7 +461,6 @@ struct Pcsx2Config
HWDisableReadbacks : 1,
AccurateDATE : 1,
GPUPaletteConversion : 1,
ConservativeFramebuffer : 1,
AutoFlushSW : 1,
PreloadFrameWithGSData : 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
if (
(GSConfig.UseHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) ||
GSConfig.ConservativeFramebuffer != old_config.ConservativeFramebuffer ||
GSConfig.TexturePreloading != old_config.TexturePreloading ||
GSConfig.UserHacks_TriFilter != old_config.UserHacks_TriFilter ||
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
@ -1456,7 +1455,6 @@ void GSApp::Init()
m_default_configuration["pcrtc_overscan"] = "0";
m_default_configuration["IntegerScaling"] = "0";
m_default_configuration["deinterlace"] = "7";
m_default_configuration["conservative_framebuffer"] = "1";
m_default_configuration["linear_present"] = "1";
m_default_configuration["LoadTextureReplacements"] = "0";
m_default_configuration["LoadTextureReplacementsAsync"] = "1";

View File

@ -76,12 +76,6 @@ __forceinline bool BitEqual(const T& a, const T& b)
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;
// Maximum texture size to skip preload/hash path.

View File

@ -20,12 +20,11 @@
#include "GS/Renderers/SW/GSTextureCacheSW.h"
#include "GS/Renderers/SW/GSDrawScanline.h"
#include "Host.h"
#include "common/Align.h"
#include "common/StringUtil.h"
GSRendererHW::GSRendererHW()
: GSRenderer()
, m_width(default_rt_size.x)
, m_height(default_rt_size.y)
, m_tc(new GSTextureCache())
, m_src(nullptr)
, m_reset(false)
@ -50,7 +49,7 @@ GSRendererHW::GSRendererHW()
ResetStates();
}
void GSRendererHW::SetScaling()
GSVector2i GSRendererHW::GetOutputSize(int real_h)
{
GSVector2i crtc_size(GetResolution());
@ -90,63 +89,10 @@ void GSRendererHW::SetScaling()
}
}
// Details of (potential) perf impact of a big framebuffer
// 1/ extra memory
// 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
// Include negative display offsets in the height here.
crtc_size.y = std::max(crtc_size.y, real_h);
// Framebuffer width is always a multiple of 64 so at certain cases it can't cover some weird width values.
// 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);
return crtc_size * GSVector2i(static_cast<int>(GSConfig.UpscaleMultiplier), static_cast<int>(GSConfig.UpscaleMultiplier));
}
void GSRendererHW::SetTCOffset()
@ -192,11 +138,6 @@ void GSRendererHW::SetGameCRC(u32 crc, int options)
bool GSRendererHW::CanUpscale()
{
if (m_hacks.m_cu && !(this->*m_hacks.m_cu)())
{
return false;
}
return GSConfig.UpscaleMultiplier != 1;
}
@ -227,20 +168,12 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
if (m_reset)
{
m_tc->RemoveAll();
// Reset RT size.
m_width = default_rt_size.x;
m_height = default_rt_size.y;
m_reset = false;
}
if (GSConfig.LoadTextureReplacements)
GSTextureReplacements::ProcessAsyncLoadedTextures();
//Check if the frame buffer width or display width has changed
SetScaling();
GSRenderer::VSync(field, registers_written);
m_tc->IncAge();
@ -286,7 +219,7 @@ GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
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;
@ -321,7 +254,8 @@ GSTexture* GSRendererHW::GetFeedbackOutput()
TEX0.TBW = m_regs->EXTBUF.EXBW;
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;
@ -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()
{
return GetTextureScaleFactor(false);
const float f_upscale = static_cast<float>(GetUpscaleMultiplier());
return GSVector2(f_upscale, f_upscale);
}
GSVector2i GSRendererHW::GetTargetSize()
{
const GSVector2i t_size = { m_width, m_height };
if (GetUpscaleMultiplier() == 1 || CanUpscale())
return t_size;
// Undo the upscaling for native resolution draws.
const GSVector2 up_s = GetTextureScaleFactor(true);
return {
static_cast<int>(std::ceil(static_cast<float>(t_size.x) / up_s.x)),
static_cast<int>(std::ceil(static_cast<float>(t_size.y) / up_s.y)),
};
// Don't blindly expand out to the scissor size if we're not drawing to it.
// e.g. Burnout 3, God of War II, etc.
u32 min_height = std::min<u32>(m_context->scissor.in.w, m_r.w);
// Another thing these games like to do, is draw a 512x896 shuffle, which would result in us
// expanding the target out to 896 height, but the extra area would all be black, with the
// draw effectively changing nothing for the new area. So, instead, lets try to detect these
// 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)
@ -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();
TEX0.TBP0 = context->FRAME.Block();
@ -1612,24 +1554,16 @@ void GSRendererHW::Draw()
TEX0.PSM = context->FRAME.PSM;
GSTextureCache::Target* rt = nullptr;
GSTexture* rt_tex = nullptr;
if (!no_rt)
{
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm);
rt_tex = rt->m_texture;
}
TEX0.TBP0 = context->ZBUF.Block();
TEX0.TBW = context->FRAME.FBW;
TEX0.PSM = context->ZBUF.PSM;
GSTextureCache::Target* ds = nullptr;
GSTexture* ds_tex = nullptr;
if (!no_ds)
{
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite());
ds_tex = ds->m_texture;
}
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);
}
// 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 GSVector2 up_s = GetTextureScaleFactor();
const int up_w = static_cast<int>(std::ceil(static_cast<float>(m_r.z) * up_s.x));
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)
// 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)
{
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;
}
}
pxAssert(rt->m_texture->GetScale() == up_s);
rt->ResizeTexture(new_w, new_h, up_s);
}
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));
if (rt_tex)
rt_tex->Save(m_dump_root + s);
if (rt->m_texture)
rt->m_texture->Save(m_dump_root + s);
}
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));
if (ds_tex)
ds_tex->Save(m_dump_root + s);
if (ds->m_texture)
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);
return;
@ -1746,7 +1666,7 @@ void GSRendererHW::Draw()
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));
if (rt_tex)
rt_tex->Save(m_dump_root + s);
if (rt)
rt->m_texture->Save(m_dump_root + s);
}
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));
if (ds_tex)
ds_tex->Save(m_dump_root + s);
if (ds)
rt->m_texture->Save(m_dump_root + s);
}
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);
#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;
bool DATE_PRIMID = 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
MergeSprite(tex);
const GSDevice::FeatureSupport features(g_gs_device->Features());
if (!features.framebuffer_fetch)
m_prim_overlap = PrimitiveOverlap();
else
@ -3418,6 +3335,8 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
m_conf.vs.fst = PRIM->FST;
// 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 sy = 2.0f * rtscale.y / (rtsize.y << 4);
const float ox = (float)(int)m_context->XYOFFSET.OFX;
@ -4177,10 +4096,8 @@ bool GSRendererHW::SwPrimRender()
GSRendererHW::Hacks::Hacks()
: m_oi_map(m_oi_list)
, m_oo_map(m_oo_list)
, m_cu_map(m_cu_list)
, m_oi(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::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_oo = m_oo_map[hash];
m_cu = m_cu_map[hash];
if (GSConfig.PointListPalette)
{
@ -4220,7 +4136,7 @@ void GSRendererHW::Hacks::SetGameCRC(const CRC::Game& game)
// 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
// 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
@ -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",
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
GSTexture* t = clear_depth ? ds : rt;
const GSVector4i commitRect = ComputeBoundingBox(t->GetScale(), t->GetSize());
t->CommitRegion(GSVector2i(commitRect.z, 2 * commitRect.w));
// Handle the case where the game stacks FBP and ZBP immediately after one another.
// We incorrectly compute the height here, because both the scissor and draw rectangle will only be half
// the height of what's effectively being cleared. Spider-Man 2's shadows are a good test case here: it
// 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)
{
// Only pure clear are supported for depth
ASSERT(color == 0);
g_gs_device->ClearDepth(t);
g_gs_device->ClearDepth(ds->m_texture);
}
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
const u32 color = v[1].RGBAQ.U32[0];
const GSVector4i commitRect = ComputeBoundingBox(rt->GetScale(), rt->GetSize());
rt->CommitRegion(GSVector2i(commitRect.z, commitRect.w));
const GSVector4i commitRect = ComputeBoundingBox(rt->m_texture->GetScale(), rt->m_texture->GetSize());
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");
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 dRect(0, 0, size.x, size.y);
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, copy_size.x, copy_size.y);
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;
private:
int m_width;
int m_height;
static constexpr float SSR_UV_TOLERANCE = 1.0f;
#pragma region hacks
@ -43,7 +40,7 @@ private:
// Require special argument
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
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_DBZBTGames(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
@ -108,16 +105,13 @@ private:
std::list<HackEntry<OI_Ptr>> m_oi_list;
std::list<HackEntry<OO_Ptr>> m_oo_list;
std::list<HackEntry<CU_Ptr>> m_cu_list;
FunctionMap<OI_Ptr> m_oi_map;
FunctionMap<OO_Ptr> m_oo_map;
FunctionMap<CU_Ptr> m_cu_map;
public:
OI_Ptr m_oi;
OO_Ptr m_oo;
CU_Ptr m_cu;
Hacks();
@ -183,15 +177,14 @@ public:
void SetGameCRC(u32 crc, int options) override;
bool CanUpscale() override;
int GetUpscaleMultiplier() override;
void SetScaling();
void Lines2Sprites();
void EmulateAtst(GSVector4& FogColor_AREF, u8& atst, const bool pass_2);
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba);
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
void MergeSprite(GSTextureCache::Source* tex);
GSVector2 GetTextureScaleFactor(const bool force_upscaling);
GSVector2 GetTextureScaleFactor() override;
GSVector2i GetOutputSize(int real_h);
GSVector2i GetTargetSize();
void Reset(bool hardware_reset) override;

View File

@ -83,6 +83,7 @@ void GSTextureCache::RemoveAll()
m_hash_cache_memory_usage = 0;
m_palette_map.Clear();
m_target_heights.clear();
}
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)
{
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;
GSVector2 res_size{ 0, 0 };
GSVector2i new_size{ 0, 0 };
@ -619,7 +620,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
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);
}
@ -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_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).
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())
@ -1230,6 +1245,36 @@ GSTextureCache::Target* GSTextureCache::GetTargetWithSharedBits(u32 BP, u32 PSM)
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
// 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
@ -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.
@ -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->SetScale(g_gs_renderer->GetTextureScaleFactor());
t->m_texture->SetScale(static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor());
m_dst[type].push_front(t);
@ -1999,6 +2058,47 @@ bool GSTextureCache::Surface::Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i
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::Source(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool dummy_container)
@ -2410,7 +2510,10 @@ void GSTextureCache::Target::UpdateIfDirtyIntersects(const GSVector4i& rc)
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
{
m_valid = m_valid.runion(rect);
if (m_valid.eq(GSVector4i::zero()))
m_valid = rect;
else
m_valid = m_valid.runion(rect);
// Block of the bottom right texel of the validity rectangle, last valid block of the texture
m_end_block = GSLocalMemory::m_psm[m_TEX0.PSM].info.bn(m_valid.z - 1, m_valid.w - 1, m_TEX0.TBP0, m_TEX0.TBW); // Valid only for color formats

View File

@ -92,6 +92,9 @@ public:
void UpdateAge();
bool Inside(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
@ -243,6 +246,25 @@ public:
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
{
u32 psm;
@ -278,6 +300,7 @@ protected:
std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache;
u64 m_hash_cache_memory_usage = 0;
FastList<Target*> m_dst[2];
FastList<TargetHeightElem> m_target_heights;
static u8* m_temp;
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;
@ -309,12 +332,14 @@ public:
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, 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.
Target* GetExactTarget(u32 BP, u32 BW, 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 InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
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:
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.");
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:
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"

View File

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

View File

@ -285,7 +285,6 @@ RendererTab::RendererTab(wxWindow* parent)
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, "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 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[] = {
"autoFlush",
"conservativeFramebuffer",
"cpuFramebufferConversion",
"disableDepthSupport",
"wrapGSMem",
@ -317,7 +316,6 @@ bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id)
case GSHWFixId::Deinterlace:
case GSHWFixId::Mipmap:
case GSHWFixId::TexturePreloading:
case GSHWFixId::ConservativeFramebuffer:
case GSHWFixId::PointListPalette:
return false;
@ -446,9 +444,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::AutoFlush:
return (static_cast<int>(config.UserHacks_AutoFlush) == value);
case GSHWFixId::ConservativeFramebuffer:
return (static_cast<int>(config.ConservativeFramebuffer) == value);
case GSHWFixId::CPUFramebufferConversion:
return (static_cast<int>(config.UserHacks_CPUFBConversion) == value);
@ -542,10 +537,6 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.UserHacks_AutoFlush = (value > 0);
break;
case GSHWFixId::ConservativeFramebuffer:
config.ConservativeFramebuffer = (value > 0);
break;
case GSHWFixId::CPUFramebufferConversion:
config.UserHacks_CPUFBConversion = (value > 0);
break;

View File

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

View File

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