mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Add detection for upscales
This commit is contained in:
parent
eb5f90027d
commit
ae57878b27
|
@ -3082,7 +3082,7 @@ bool GSState::PrimitiveCoversWithoutGaps()
|
|||
}
|
||||
else
|
||||
{
|
||||
if (std::abs(dpX - first_dpX) >= 16 || std::abs(this_start_X - last_pX) >= 16)
|
||||
if ((std::abs(dpX - first_dpX) >= 16 && (i + 2) < m_vertex.next) || std::abs(this_start_X - last_pX) >= 16)
|
||||
{
|
||||
m_primitive_covers_without_gaps = false;
|
||||
return false;
|
||||
|
|
|
@ -2614,18 +2614,26 @@ void GSRendererHW::Draw()
|
|||
m_r = m_r.rintersect(t_size_rect);
|
||||
|
||||
float target_scale = GetTextureScaleFactor();
|
||||
|
||||
if (isDownscaleDraw(src, no_gaps) || (IsPossibleChannelShuffle() && src && src->m_from_target && src->m_from_target->GetScale() == 1.0f))
|
||||
const int scale_draw = IsScalingDraw(src, no_gaps);
|
||||
if (target_scale > 1.0f && scale_draw > 0)
|
||||
{
|
||||
target_scale = 1.0f;
|
||||
// 1 == Downscale, so we need to reduce the size of the target also.
|
||||
// 2 == Upscale, so likely putting it over the top of the render target.
|
||||
if(scale_draw == 1)
|
||||
target_scale = 1.0f;
|
||||
m_downscale_source = src->m_from_target->GetScale() > 1.0f;
|
||||
}
|
||||
else
|
||||
m_downscale_source = false;
|
||||
|
||||
if (IsPossibleChannelShuffle() && src && src->m_from_target && src->m_from_target->GetScale() != target_scale)
|
||||
{
|
||||
target_scale = src->m_from_target->GetScale();
|
||||
}
|
||||
// This upscaling hack is for games which construct P8 textures by drawing a bunch of small sprites in C32,
|
||||
// then reinterpreting it as P8. We need to keep the off-screen intermediate textures at native resolution,
|
||||
// but not propagate that through to the normal render targets. Test Case: Crash Wrath of Cortex.
|
||||
if (no_ds && src && !m_channel_shuffle && GSConfig.UserHacks_NativePaletteDraw && src->m_from_target &&
|
||||
if (no_ds && src && !m_channel_shuffle && src->m_from_target && (GSConfig.UserHacks_NativePaletteDraw || (src->m_from_target->m_downscaled && scale_draw <= 1)) &&
|
||||
src->m_scale == 1.0f && (src->m_TEX0.PSM == PSMT8 || src->m_TEX0.TBP0 == m_cached_ctx.FRAME.Block()))
|
||||
{
|
||||
GL_CACHE("Using native resolution for target based on texture source");
|
||||
|
@ -2662,7 +2670,7 @@ void GSRendererHW::Draw()
|
|||
GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) ||
|
||||
IsPossibleChannelShuffle());
|
||||
rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, target_scale, GSTextureCache::RenderTarget, true,
|
||||
fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, unclamped_draw_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(), src);
|
||||
fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, unclamped_draw_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block());
|
||||
|
||||
// Draw skipped because it was a clear and there was no target.
|
||||
if (!rt)
|
||||
|
@ -2694,7 +2702,16 @@ void GSRendererHW::Draw()
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (src && src->m_from_target && src->m_target_direct && src->m_from_target == rt)
|
||||
{
|
||||
src->m_texture = rt->m_texture;
|
||||
src->m_scale = rt->m_scale;
|
||||
src->m_unscaled_size = rt->m_unscaled_size;
|
||||
}
|
||||
|
||||
target_scale = rt->GetScale();
|
||||
}
|
||||
|
||||
// The target might have previously been a C32 format with valid alpha. If we're switching to C24, we need to preserve it.
|
||||
preserve_rt_alpha |= (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].trbpp == 24 && rt->HasValidAlpha());
|
||||
|
@ -4979,7 +4996,8 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
|
|||
// Apply a small offset (based on upscale amount) for edges of textures to avoid reading garbage during a clamp+stscale down
|
||||
// Bigger problem when WH is 1024x1024 and the target is only small.
|
||||
// This "fixes" a lot of the rainbow garbage in games when upscaling (and xenosaga shadows + VP2 forest seem quite happy).
|
||||
const GSVector4 region_clamp_offset = GSVector4::cxpr(0.5f, 0.5f, 0.1f, 0.1f) + (GSVector4::cxpr(0.1f, 0.1f, 0.0f, 0.0f) * scale);
|
||||
// Note that this is done on the original texture scale, during upscales it can mess up otherwise.
|
||||
const GSVector4 region_clamp_offset = GSVector4::cxpr(0.5f, 0.5f, 0.1f, 0.1f) + (GSVector4::cxpr(0.1f, 0.1f, 0.0f, 0.0f) * tex->GetScale());
|
||||
|
||||
const GSVector4 region_clamp = (GSVector4(clamp) + region_clamp_offset) / WH.xyxy();
|
||||
if (wms >= CLAMP_REGION_CLAMP)
|
||||
|
@ -5180,7 +5198,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
|||
}
|
||||
|
||||
unscaled_size = copy_size;
|
||||
scale = m_downscale_source ? 1 : src_target->GetScale();
|
||||
scale = m_downscale_source ? 1.0f : src_target->GetScale();
|
||||
const float src_scale = src_target->GetScale();
|
||||
GL_CACHE("Copy size: %dx%d, range: %d,%d -> %d,%d (%dx%d) @ %.1f", copy_size.x, copy_size.y, copy_range.x,
|
||||
copy_range.y, copy_range.z, copy_range.w, copy_range.width(), copy_range.height(), scale);
|
||||
|
@ -5195,7 +5213,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
|||
src_copy.reset(src_target->m_texture->IsDepthStencil() ?
|
||||
g_gs_device->CreateDepthStencil(
|
||||
scaled_copy_size.x, scaled_copy_size.y, src_target->m_texture->GetFormat(), false) :
|
||||
(m_downscale_source ? g_gs_device->CreateRenderTarget(scaled_copy_size.x, scaled_copy_size.y, src_target->m_texture->GetFormat(), false,
|
||||
(m_downscale_source ? g_gs_device->CreateRenderTarget(scaled_copy_size.x, scaled_copy_size.y, src_target->m_texture->GetFormat(), true,
|
||||
true) :
|
||||
g_gs_device->CreateTexture(
|
||||
scaled_copy_size.x, scaled_copy_size.y, 1, src_target->m_texture->GetFormat(), true)));
|
||||
|
@ -5208,10 +5226,10 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
|||
}
|
||||
if (m_downscale_source)
|
||||
{
|
||||
DevCon.Warning("Resizing to scale %f", scale);
|
||||
GL_INS("Rescaling %f -> %f", src_scale, scale);
|
||||
const GSVector4 dst_rect = GSVector4(0, 0, src_unscaled_size.x, src_unscaled_size.y);
|
||||
const GSVector4 src_rect = GSVector4(scaled_copy_range) / GSVector4(src_unscaled_size * src_scale).xyxy();
|
||||
g_gs_device->StretchRect(src_target->m_texture, GSVector4(0.0f,0.0f,1.0f,1.0f), src_copy.get(), dst_rect, src_target->m_texture->IsDepthStencil() ? ShaderConvert::DEPTH_COPY : ShaderConvert::COPY, true);
|
||||
g_gs_device->StretchRect(src_target->m_texture, GSVector4::cxpr(0.0f,0.0f,1.0f,1.0f), src_copy.get(), dst_rect, src_target->m_texture->IsDepthStencil() ? ShaderConvert::DEPTH_COPY : ShaderConvert::COPY, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -7183,24 +7201,29 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GSRendererHW::isDownscaleDraw(GSTextureCache::Source* src, bool no_gaps)
|
||||
int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
|
||||
{
|
||||
// Try to detect if a game is downscaling/upscaling the image in order to do post processing/bloom effects.
|
||||
const GSVector2i draw_size = GSVector2i(m_vt.m_max.p.x - m_vt.m_min.p.x, m_vt.m_max.p.y - m_vt.m_min.p.y);
|
||||
|
||||
if (draw_size.x >= PCRTCDisplays.GetResolution().x)
|
||||
return false;
|
||||
|
||||
const GSVector2i tex_size = GSVector2i(m_vt.m_max.t.x - m_vt.m_min.t.x, m_vt.m_max.t.y - m_vt.m_min.t.y);
|
||||
const bool is_downscale = (tex_size.x / 2.0f) >= draw_size.x && (tex_size.y / 2.0f) >= draw_size.y;
|
||||
if (is_downscale && draw_size.x >= PCRTCDisplays.GetResolution().x)
|
||||
return 0;
|
||||
|
||||
if (no_gaps && m_vt.m_primclass >= GS_TRIANGLE_CLASS && m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 && !IsMipMapDraw() && IsDiscardingDstColor() &&
|
||||
src && src->m_from_target && m_context->TEX1.MMAG == 1 &&
|
||||
(tex_size.x / 2.0f) >= draw_size.x && (tex_size.y / 2.0f) >= draw_size.y)
|
||||
const bool is_upscale = (draw_size.x / 2.0f) >= tex_size.x && (draw_size.y / 2.0f) >= tex_size.y;
|
||||
|
||||
// DMC does a blit in strips with the scissor to keep it inside page boundaries, so that's not technically full coverage
|
||||
// but good enough for what we want.
|
||||
const bool no_gaps_or_single_sprite = (no_gaps || (m_vt.m_primclass == GS_SPRITE_CLASS && m_index.tail == 2));
|
||||
if (no_gaps_or_single_sprite && m_vt.m_primclass >= GS_TRIANGLE_CLASS && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp > 8 && m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 && !IsMipMapDraw() &&
|
||||
((is_upscale && !IsDiscardingDstColor()) || (IsDiscardingDstColor() && is_downscale)) &&
|
||||
src && src->m_from_target && !src->m_from_target->m_downscaled && m_context->TEX1.MMAG == 1)
|
||||
{
|
||||
DevCon.Warning("Draw %d is a downscale draw from %dx%d to %dx%d", s_n, tex_size.x, tex_size.y, draw_size.x, draw_size.y);
|
||||
return true;
|
||||
GL_INS("%s draw detected - from %dx%d to %dx%d", is_downscale ? "Downscale" : "Upscale", tex_size.x, tex_size.y, draw_size.x, draw_size.y);
|
||||
return is_upscale ? 2 : 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsConstantDirectWriteMemClear()
|
||||
|
|
|
@ -48,7 +48,7 @@ private:
|
|||
float alpha1(int L, int X0, int X1);
|
||||
void SwSpriteRender();
|
||||
bool CanUseSwSpriteRender();
|
||||
bool isDownscaleDraw(GSTextureCache::Source* src, bool no_gaps);
|
||||
int IsScalingDraw(GSTextureCache::Source* src, bool no_gaps);
|
||||
bool IsConstantDirectWriteMemClear();
|
||||
u32 GetConstantDirectWriteMemClearColor() const;
|
||||
u32 GetConstantDirectWriteMemClearDepth() const;
|
||||
|
|
|
@ -1797,7 +1797,7 @@ GSVector2i GSTextureCache::ScaleRenderTargetSize(const GSVector2i& sz, float sca
|
|||
}
|
||||
|
||||
GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type,
|
||||
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect, bool is_shuffle, bool possible_clear, Source* src)
|
||||
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb, bool preserve_alpha, const GSVector4i draw_rect, bool is_shuffle, bool possible_clear)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
const u32 bp = TEX0.TBP0;
|
||||
|
@ -2002,12 +2002,12 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
if (!tex)
|
||||
return nullptr;
|
||||
|
||||
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, dst->m_scale == 1.0f);
|
||||
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, dst->m_scale < scale);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
|
||||
|
||||
// If we're changing resolution scale, just toss the texture, it's not going to get reused.
|
||||
if (!GSConfig.UserHacks_NativePaletteDraw || (dst->m_scale != 1.0f && scale != 1.0f))
|
||||
if ((!GSConfig.UserHacks_NativePaletteDraw && !dst->m_downscaled) || (dst->m_scale != 1.0f && scale != 1.0f))
|
||||
delete dst->m_texture;
|
||||
else
|
||||
g_gs_device->Recycle(dst->m_texture);
|
||||
|
@ -2015,6 +2015,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
dst->m_texture = tex;
|
||||
dst->m_scale = scale;
|
||||
dst->m_unscaled_size = new_size;
|
||||
dst->m_downscaled = scale == 1.0f;
|
||||
}
|
||||
|
||||
// If our RGB was invalidated, we need to pull it from depth.
|
||||
|
@ -2397,6 +2398,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
dst->readbacks_since_draw = 0;
|
||||
|
||||
dst->m_last_draw = GSState::s_n;
|
||||
dst->m_downscaled = scale != GSRendererHW::GetInstance()->GetTextureScaleFactor();
|
||||
|
||||
if (dst->m_dirty.empty() && GSLocalMemory::m_psm[TEX0.PSM].depth == 0 && (GSUtil::GetChannelMask(TEX0.PSM) & 0x8))
|
||||
dst->m_rt_alpha_scale = true;
|
||||
|
|
|
@ -220,6 +220,7 @@ public:
|
|||
bool m_valid_alpha_high = false;
|
||||
bool m_valid_rgb = false;
|
||||
bool m_rt_alpha_scale = false;
|
||||
bool m_downscaled = false;
|
||||
int m_last_draw = 0;
|
||||
|
||||
bool m_is_frame = false;
|
||||
|
@ -490,7 +491,7 @@ public:
|
|||
Target* FindTargetOverlap(Target* target, int type, int psm);
|
||||
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
|
||||
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
|
||||
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, Source* src = nullptr);
|
||||
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false);
|
||||
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
|
||||
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
|
||||
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
|
||||
|
|
Loading…
Reference in New Issue