mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Fix up shuffle behaviour and affected areas
- Channel shuffles now check how many pages require drawing before doing the shuffle. - Split texture shuffles don't create new targets with bad valid areas.
This commit is contained in:
parent
47d4dc8c7e
commit
818527e8e1
|
@ -119,6 +119,10 @@ GSState::~GSState()
|
|||
_aligned_free(m_vertex.buff);
|
||||
if (m_index.buff)
|
||||
_aligned_free(m_index.buff);
|
||||
if (m_draw_vertex.buff)
|
||||
_aligned_free(m_draw_vertex.buff);
|
||||
if (m_draw_index.buff)
|
||||
_aligned_free(m_draw_index.buff);
|
||||
}
|
||||
|
||||
std::string GSState::GetDrawDumpPath(const char* format, ...)
|
||||
|
@ -850,7 +854,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
|||
// Urban Chaos writes to the memory backing the CLUT in the middle of a shuffle, and
|
||||
// it's unclear whether the CLUT would actually get reloaded in that case.
|
||||
if (TEX0.CBP != m_mem.m_clut.GetCLUTCBP())
|
||||
m_channel_shuffle = false;
|
||||
m_channel_shuffle_abort = true;
|
||||
}
|
||||
|
||||
TEX0.CPSM &= 0xa; // 1010b
|
||||
|
@ -2796,8 +2800,10 @@ void GSState::GrowVertexBuffer()
|
|||
const u32 maxcount = std::max<u32>(m_vertex.maxcount * 3 / 2, 10000);
|
||||
|
||||
GSVertex* vertex = static_cast<GSVertex*>(_aligned_malloc(sizeof(GSVertex) * maxcount, 32));
|
||||
GSVertex* draw_vertex = static_cast<GSVertex*>(_aligned_malloc(sizeof(GSVertex) * maxcount, 32));
|
||||
// Worst case index list is a list of points with vs expansion, 6 indices per point
|
||||
u16* index = static_cast<u16*>(_aligned_malloc(sizeof(u16) * maxcount * 6, 32));
|
||||
u16* draw_index = static_cast<u16*>(_aligned_malloc(sizeof(u16) * maxcount * 6, 32));
|
||||
|
||||
if (!vertex || !index)
|
||||
{
|
||||
|
@ -2823,6 +2829,22 @@ void GSState::GrowVertexBuffer()
|
|||
_aligned_free(m_index.buff);
|
||||
}
|
||||
|
||||
if (m_draw_vertex.buff)
|
||||
{
|
||||
std::memcpy(draw_vertex, m_draw_vertex.buff, sizeof(GSVertex) * m_vertex.tail);
|
||||
|
||||
_aligned_free(m_draw_vertex.buff);
|
||||
}
|
||||
|
||||
if (m_draw_index.buff)
|
||||
{
|
||||
std::memcpy(draw_index, m_draw_index.buff, sizeof(u16) * m_index.tail);
|
||||
|
||||
_aligned_free(m_draw_index.buff);
|
||||
}
|
||||
|
||||
m_draw_vertex.buff = draw_vertex;
|
||||
m_draw_index.buff = draw_index;
|
||||
m_vertex.buff = vertex;
|
||||
m_vertex.maxcount = maxcount - 3; // -3 to have some space at the end of the buffer before DrawingKick can grow it
|
||||
m_index.buff = index;
|
||||
|
@ -3872,7 +3894,7 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
|
|||
// Adjust texture range when sprites get scissor clipped. Since we linearly interpolate, this
|
||||
// optimization doesn't work when perspective correction is enabled.
|
||||
// Allowing for quads when the gradiant is 1. It's not guaranteed (would need to check the grandient on each vector), but should be close enough.
|
||||
if ((m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && TrianglesAreQuads(false) && grad.x == 1.0f && grad.y == 1.0f)) && m_primitive_covers_without_gaps != NoGapsType::GapsFound)
|
||||
if (m_primitive_covers_without_gaps != NoGapsType::GapsFound && (m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && grad.x == 1.0f && grad.y == 1.0f && TrianglesAreQuads(false))))
|
||||
{
|
||||
// When coordinates are fractional, GS appears to draw to the right/bottom (effectively
|
||||
// taking the ceiling), not to the top/left (taking the floor).
|
||||
|
|
|
@ -145,6 +145,21 @@ protected:
|
|||
u32 tail;
|
||||
} m_index = {};
|
||||
|
||||
struct
|
||||
{
|
||||
GSVertex* buff;
|
||||
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
|
||||
u32 xy_tail;
|
||||
GSVector4i xy[4];
|
||||
GSVector4i xyhead;
|
||||
} m_draw_vertex = {};
|
||||
|
||||
struct
|
||||
{
|
||||
u16* buff;
|
||||
u32 tail;
|
||||
} m_draw_index = {};
|
||||
|
||||
void UpdateContext();
|
||||
void UpdateScissor();
|
||||
|
||||
|
@ -225,6 +240,8 @@ public:
|
|||
bool m_isPackedUV_HackFlag = false;
|
||||
bool m_channel_shuffle = false;
|
||||
bool m_in_target_draw = false;
|
||||
bool m_channel_shuffle_abort = false;
|
||||
|
||||
u32 m_target_offset = 0;
|
||||
u8 m_scanmask_used = 0;
|
||||
u32 m_dirty_gs_regs = 0;
|
||||
|
|
|
@ -940,6 +940,10 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
|
|||
&& r.m_cached_ctx.FRAME.FBMSK == 0 // No frame buffer masking.
|
||||
)
|
||||
{
|
||||
int mask = (r.m_vt.m_max.p.xyxy() == r.m_vt.m_min.p.xyxy()).mask();
|
||||
if (mask == 0xf)
|
||||
return true;
|
||||
|
||||
const u32 FBP = r.m_cached_ctx.FRAME.Block();
|
||||
const u32 FBW = r.m_cached_ctx.FRAME.FBW;
|
||||
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %u, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
|
||||
|
|
|
@ -1036,7 +1036,8 @@ GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex, const
|
|||
bool GSRendererHW::IsPossibleChannelShuffle() const
|
||||
{
|
||||
if (!PRIM->TME || m_cached_ctx.TEX0.PSM != PSMT8 || // 8-bit texture draw
|
||||
m_vt.m_primclass != GS_SPRITE_CLASS) // draw_sprite_tex
|
||||
m_vt.m_primclass != GS_SPRITE_CLASS || // draw_sprite_tex
|
||||
(m_vertex.tail <= 2 && (((m_vt.m_max.p - m_vt.m_min.p) <= GSVector4(8.0f)).mask() & 0x3) == 0x3)) // Powerdrome does a tiny shuffle on a couple of pixels, can't reliably translate this.
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1050,6 +1051,7 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
|
|||
const int draw_height = std::abs(v[1].XYZ.Y - v[0].XYZ.Y) >> 4;
|
||||
|
||||
const bool mask_clamp = (m_cached_ctx.CLAMP.WMS | m_cached_ctx.CLAMP.WMT) & 0x2;
|
||||
|
||||
const bool draw_match = (draw_height == 2) || (draw_width == 8);
|
||||
|
||||
if (draw_match || mask_clamp)
|
||||
|
@ -1121,7 +1123,7 @@ bool GSRendererHW::NextDrawMatchesShuffle() const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsSplitTextureShuffle(GSTextureCache::Target* rt)
|
||||
bool GSRendererHW::IsSplitTextureShuffle(GIFRegTEX0& rt_TEX0, GSVector4i& valid_area)
|
||||
{
|
||||
// For this to work, we're peeking into the next draw, therefore we need dirty registers.
|
||||
if (m_dirty_gs_regs == 0)
|
||||
|
@ -1164,7 +1166,7 @@ bool GSRendererHW::IsSplitTextureShuffle(GSTextureCache::Target* rt)
|
|||
const u32 pages_high = static_cast<u32>(aligned_rc.height()) / frame_psm.pgs.y;
|
||||
const u32 num_pages = m_context->FRAME.FBW * pages_high;
|
||||
// Jurassic - The Hunted will do a split shuffle with a height of 512 (256) when it's supposed to be 448, so it redoes one row of the shuffle.
|
||||
const u32 rt_half = (((rt->m_valid.height() / GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.y) / 2) * rt->m_TEX0.TBW) + (rt->m_TEX0.TBP0 >> 5);
|
||||
const u32 rt_half = (((valid_area.height() / GSLocalMemory::m_psm[rt_TEX0.PSM].pgs.y) / 2) * rt_TEX0.TBW) + (rt_TEX0.TBP0 >> 5);
|
||||
// If this is a split texture shuffle, the next draw's FRAME/TEX0 should line up.
|
||||
// Re-add the offset we subtracted in Draw() to get the original FBP/TBP0.. this won't handle wrapping. Oh well.
|
||||
// "Potential" ones are for Jak3 which does a split shuffle on a 128x128 texture with a width of 256, writing to the lower half then offsetting 2 pages.
|
||||
|
@ -1200,7 +1202,7 @@ bool GSRendererHW::IsSplitTextureShuffle(GSTextureCache::Target* rt)
|
|||
|
||||
// If the game has changed the texture width to 1 we need to retanslate it to whatever the rt has so the final rect is correct.
|
||||
if (m_cached_ctx.FRAME.FBW == 1)
|
||||
m_split_texture_shuffle_fbw = rt->m_TEX0.TBW;
|
||||
m_split_texture_shuffle_fbw = rt_TEX0.TBW;
|
||||
else
|
||||
m_split_texture_shuffle_fbw = m_cached_ctx.FRAME.FBW;
|
||||
}
|
||||
|
@ -1209,10 +1211,10 @@ bool GSRendererHW::IsSplitTextureShuffle(GSTextureCache::Target* rt)
|
|||
u32 total_pages = num_pages;
|
||||
|
||||
// If the current draw is further than the half way point and the next draw is the half way point, then we can assume it's just overdrawing.
|
||||
if (next_ctx.FRAME.FBP == rt_half && num_pages > (rt_half - (rt->m_TEX0.TBP0 >> 5)))
|
||||
if (next_ctx.FRAME.FBP == rt_half && num_pages > (rt_half - (rt_TEX0.TBP0 >> 5)))
|
||||
{
|
||||
vertical_pages = (rt->m_valid.height() / GSLocalMemory::m_psm[rt->m_TEX0.PSM].pgs.y) / 2;
|
||||
total_pages = vertical_pages * rt->m_TEX0.TBW;
|
||||
vertical_pages = (valid_area.height() / GSLocalMemory::m_psm[rt_TEX0.PSM].pgs.y) / 2;
|
||||
total_pages = vertical_pages * rt_TEX0.TBW;
|
||||
}
|
||||
|
||||
if ((m_split_texture_shuffle_pages % m_split_texture_shuffle_fbw) == 0)
|
||||
|
@ -2119,12 +2121,14 @@ void GSRendererHW::Draw()
|
|||
// Fortunately, it seems to change the FBMSK along the way, so this check alone is sufficient.
|
||||
// Tomb Raider: Underworld does similar, except with R, G, B in separate palettes, therefore
|
||||
// we need to split on those too.
|
||||
m_channel_shuffle = IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
|
||||
m_channel_shuffle = !m_channel_shuffle_abort && IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
|
||||
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && m_last_channel_shuffle_end_block > m_context->FRAME.Block() &&
|
||||
m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0;
|
||||
|
||||
if (m_channel_shuffle)
|
||||
{
|
||||
// Tombraider does vertical strips 2 pages at a time, then puts them horizontally, it's a mess, so let it do the full screen shuffle.
|
||||
m_full_screen_shuffle |= !IsPageCopy() && NextDrawMatchesShuffle();
|
||||
// These HLE's skip several channel shuffles in a row which change blends etc. Let's not break the flow, it gets upset.
|
||||
if (!m_conf.ps.urban_chaos_hle && !m_conf.ps.tales_of_abyss_hle)
|
||||
{
|
||||
|
@ -2136,16 +2140,61 @@ void GSRendererHW::Draw()
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_channel_shuffle_width)
|
||||
{
|
||||
if (m_last_rt)
|
||||
{
|
||||
//DevCon.Warning("Skipped %d draw %d was abort %d", num_skipped_channel_shuffle_draws, s_n, (int)m_channel_shuffle_abort);
|
||||
// Some games like Tomb raider abort early, we're never going to know the real height, and the system doesn't work right for partials.
|
||||
// But it's good enough for games like Hitman Blood Money which only shuffle part of the screen
|
||||
|
||||
if (!m_full_screen_shuffle)
|
||||
{
|
||||
const u32 width_pages = ((num_skipped_channel_shuffle_draws + 1) % std::max(1U, m_channel_shuffle_width) % std::max(1U, m_channel_shuffle_width)) * 64;;
|
||||
m_conf.scissor.w = m_conf.scissor.y + (((num_skipped_channel_shuffle_draws + 1 + (m_channel_shuffle_width - 1)) / std::max(1U, m_channel_shuffle_width)) * 32) * m_conf.cb_ps.ScaleFactor.z;
|
||||
if (width_pages)
|
||||
m_conf.scissor.z = m_conf.scissor.x + (((num_skipped_channel_shuffle_draws + 1) % std::max(1U, m_channel_shuffle_width) % std::max(1U, m_channel_shuffle_width)) * 64) * m_conf.cb_ps.ScaleFactor.z;
|
||||
}
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
|
||||
if (GSConfig.DumpGSData)
|
||||
{
|
||||
const u64 frame = g_perfmon.GetFrame();
|
||||
|
||||
std::string s;
|
||||
|
||||
if (GSConfig.SaveRT && (s_n - 1) >= GSConfig.SaveN)
|
||||
{
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_(%05x)_%s.bmp", s_n - 1, frame, m_cached_ctx.FRAME.Block(), m_last_rt-> m_TEX0.TBP0, psm_str(m_cached_ctx.FRAME.PSM));
|
||||
|
||||
m_last_rt->m_texture->Save(s);
|
||||
}
|
||||
|
||||
if (GSConfig.SaveL > 0 && ((s_n - 1) - GSConfig.SaveN) > GSConfig.SaveL)
|
||||
{
|
||||
GSConfig.DumpGSData = 0;
|
||||
}
|
||||
}
|
||||
g_texture_cache->InvalidateTemporarySource();
|
||||
CleanupDraw(false);
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_OGL_DEBUG
|
||||
if (num_skipped_channel_shuffle_draws > 0)
|
||||
GL_CACHE("Skipped %d channel shuffle draws ending at %d", num_skipped_channel_shuffle_draws, s_n);
|
||||
#endif
|
||||
num_skipped_channel_shuffle_draws = 0;
|
||||
|
||||
m_last_channel_shuffle_fbp = 0xffff;
|
||||
m_last_channel_shuffle_tbp = 0xffff;
|
||||
m_last_channel_shuffle_end_block = 0xffff;
|
||||
}
|
||||
|
||||
m_last_rt = nullptr;
|
||||
m_channel_shuffle_width = 0;
|
||||
m_full_screen_shuffle = false;
|
||||
m_channel_shuffle_abort = false;
|
||||
|
||||
GL_PUSH("HW Draw %d (Context %u)", s_n, PRIM->CTXT);
|
||||
GL_INS("FLUSH REASON: %s%s", GetFlushReasonString(m_state_flush_reason),
|
||||
(m_state_flush_reason != GSFlushReason::CONTEXTCHANGE && m_dirty_gs_regs) ? " AND POSSIBLE CONTEXT CHANGE" :
|
||||
|
@ -2255,14 +2304,6 @@ void GSRendererHW::Draw()
|
|||
|
||||
const bool draw_sprite_tex = PRIM->TME && (m_vt.m_primclass == GS_SPRITE_CLASS);
|
||||
|
||||
// We trigger the sw prim render here super early, to avoid creating superfluous render targets.
|
||||
if (CanUseSwPrimRender(no_rt, no_ds, draw_sprite_tex) && SwPrimRender(*this, true, true))
|
||||
{
|
||||
GL_CACHE("Possible texture decompression, drawn with SwPrimRender() (BP %x BW %u TBP0 %x TBW %u)",
|
||||
m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBMSK, m_cached_ctx.TEX0.TBP0, m_cached_ctx.TEX0.TBW);
|
||||
return;
|
||||
}
|
||||
|
||||
// GS doesn't fill the right or bottom edges of sprites/triangles, and for a pixel to be shaded, the vertex
|
||||
// must cross the center. In other words, the range is equal to the floor of coordinates +0.5. Except for
|
||||
// the case where the minimum equals the maximum, because at least one pixel is filled per line.
|
||||
|
@ -2290,6 +2331,14 @@ void GSRendererHW::Draw()
|
|||
return;
|
||||
}
|
||||
|
||||
// We trigger the sw prim render here super early, to avoid creating superfluous render targets.
|
||||
if (CanUseSwPrimRender(no_rt, no_ds, draw_sprite_tex) && SwPrimRender(*this, true, true))
|
||||
{
|
||||
GL_CACHE("Possible texture decompression, drawn with SwPrimRender() (BP %x BW %u TBP0 %x TBW %u)",
|
||||
m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBMSK, m_cached_ctx.TEX0.TBP0, m_cached_ctx.TEX0.TBW);
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to fix up the context if we're doing a double half clear, regardless of whether we do the CPU fill.
|
||||
const ClearType is_possible_mem_clear = IsConstantDirectWriteMemClear();
|
||||
if (!GSConfig.UserHacks_DisableSafeFeatures && is_possible_mem_clear)
|
||||
|
@ -2823,7 +2872,7 @@ void GSRendererHW::Draw()
|
|||
|
||||
ds = g_texture_cache->LookupTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
|
||||
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), false,
|
||||
src, -1);
|
||||
src, nullptr, -1);
|
||||
|
||||
ZBUF_TEX0.TBW = m_channel_shuffle ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
|
||||
|
||||
|
@ -2886,10 +2935,12 @@ void GSRendererHW::Draw()
|
|||
possible_shuffle |= draw_sprite_tex && m_primitive_covers_without_gaps != NoGapsType::FullCover && (((src && src->m_target && src->m_from_target && src->m_from_target->m_32_bits_fmt) && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) ||
|
||||
IsPossibleChannelShuffle());
|
||||
|
||||
const bool possible_horizontal_texture_shuffle = possible_shuffle && src && src->m_from_target && m_r.w <= src->m_from_target->m_valid.w && m_r.z > src->m_from_target->m_valid.z && m_cached_ctx.FRAME.FBW > src->m_from_target_TEX0.TBW;
|
||||
|
||||
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
|
||||
FRAME_TEX0.U64 = 0;
|
||||
FRAME_TEX0.TBP0 = ((m_last_channel_shuffle_end_block + 1) == m_cached_ctx.FRAME.Block() && possible_shuffle) ? m_last_channel_shuffle_fbp : m_cached_ctx.FRAME.Block();
|
||||
FRAME_TEX0.TBW = (possible_shuffle && IsPossibleChannelShuffle() && src && src->m_from_target) ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
|
||||
FRAME_TEX0.TBW = (possible_horizontal_texture_shuffle || (possible_shuffle && src && src->m_from_target && IsPossibleChannelShuffle())) ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
|
||||
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;
|
||||
|
||||
// Don't clamp on shuffle, the height cache may troll us with the REAL height.
|
||||
|
@ -2912,10 +2963,10 @@ void GSRendererHW::Draw()
|
|||
FRAME_TEX0.PSM = PSMCT32; // Guess full color if no upcoming hint, it'll fix itself later.
|
||||
|
||||
// This is just for overlap detection, it doesn't matter which direction we do this in
|
||||
if (GSLocalMemory::m_psm[FRAME_TEX0.PSM].bpp == 32)
|
||||
if (GSLocalMemory::m_psm[FRAME_TEX0.PSM].bpp == 32 && src && src->m_from_target)
|
||||
{
|
||||
// Shuffling with a double width (Sonic Unleashed for example which does a wierd shuffle/not shuffle green backup/restore).
|
||||
if (src && std::abs((lookup_rect.width() / 2) - src->m_from_target->m_unscaled_size.x) <= 8)
|
||||
if (std::abs((lookup_rect.width() / 2) - src->m_from_target->m_unscaled_size.x) <= 8)
|
||||
{
|
||||
lookup_rect.x /= 2;
|
||||
lookup_rect.z /= 2;
|
||||
|
@ -2940,7 +2991,7 @@ void GSRendererHW::Draw()
|
|||
|
||||
rt = g_texture_cache->LookupTarget(FRAME_TEX0, t_size, ((src && src->m_scale != 1) && GSConfig.UserHacks_NativeScaling == GSNativeScaling::Normal && !possible_shuffle) ? GetTextureScaleFactor() : target_scale, GSTextureCache::RenderTarget, true,
|
||||
fm, false, force_preload, preserve_rt_rgb, preserve_rt_alpha, lookup_rect, possible_shuffle, is_possible_mem_clear && FRAME_TEX0.TBP0 != m_cached_ctx.ZBUF.Block(),
|
||||
GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && preserve_downscale_draw && is_possible_mem_clear != ClearType::NormalClear, src, (no_ds || !ds) ? -1 : (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0));
|
||||
GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off && preserve_downscale_draw && is_possible_mem_clear != ClearType::NormalClear, src, ds, (no_ds || !ds) ? -1 : (m_cached_ctx.ZBUF.Block() - ds->m_TEX0.TBP0));
|
||||
|
||||
// Draw skipped because it was a clear and there was no target.
|
||||
if (!rt)
|
||||
|
@ -2966,6 +3017,18 @@ void GSRendererHW::Draw()
|
|||
FRAME_TEX0.TBW = src->m_from_target->m_TEX0.TBW;
|
||||
}
|
||||
|
||||
if (possible_shuffle && IsSplitTextureShuffle(FRAME_TEX0, lookup_rect))
|
||||
{
|
||||
DevCon.Warning("Split texture shuffle early exit");
|
||||
// If TEX0 == FBP, we're going to have a source left in the TC.
|
||||
// That source will get used in the actual draw unsafely, so kick it out.
|
||||
if (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0)
|
||||
g_texture_cache->InvalidateVideoMem(context->offset.fb, m_r, false);
|
||||
|
||||
CleanupDraw(true);
|
||||
return;
|
||||
}
|
||||
|
||||
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), (scale_draw < 0 && is_possible_mem_clear != ClearType::NormalClear) ? src->m_from_target->GetScale() : target_scale,
|
||||
GSTextureCache::RenderTarget, true, fm, false, force_preload, preserve_rt_color || possible_shuffle, lookup_rect, src);
|
||||
|
||||
|
@ -3130,7 +3193,7 @@ void GSRendererHW::Draw()
|
|||
|
||||
ds = g_texture_cache->LookupTarget(ZBUF_TEX0, t_size, target_scale, GSTextureCache::DepthStencil,
|
||||
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, preserve_depth, unclamped_draw_rect, IsPossibleChannelShuffle(), is_possible_mem_clear && ZBUF_TEX0.TBP0 != m_cached_ctx.FRAME.Block(), false,
|
||||
src, -1);
|
||||
src, nullptr, -1);
|
||||
|
||||
ZBUF_TEX0.TBW = m_channel_shuffle ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
|
||||
|
||||
|
@ -3220,10 +3283,10 @@ void GSRendererHW::Draw()
|
|||
(shuffle_coords || rt->m_32_bits_fmt)) &&
|
||||
(src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle) &&
|
||||
(draw_sprite_tex || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads(true)));
|
||||
};
|
||||
|
||||
if (m_texture_shuffle && IsSplitTextureShuffle(rt))
|
||||
if (m_texture_shuffle && IsSplitTextureShuffle(rt->m_TEX0, rt->m_valid))
|
||||
{
|
||||
DevCon.Warning("Split texture shuffle");
|
||||
// If TEX0 == FBP, we're going to have a source left in the TC.
|
||||
// That source will get used in the actual draw unsafely, so kick it out.
|
||||
if (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0)
|
||||
|
@ -3232,6 +3295,7 @@ void GSRendererHW::Draw()
|
|||
CleanupDraw(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((src->m_target || (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0)) && IsPossibleChannelShuffle())
|
||||
{
|
||||
|
@ -3779,7 +3843,6 @@ void GSRendererHW::Draw()
|
|||
if (!skip_draw)
|
||||
DrawPrims(rt, ds, src, tmm);
|
||||
|
||||
//
|
||||
|
||||
// Temporary source *must* be invalidated before normal, because otherwise it'll be double freed.
|
||||
g_texture_cache->InvalidateTemporarySource();
|
||||
|
@ -3841,7 +3904,7 @@ void GSRendererHW::Draw()
|
|||
|
||||
std::string s;
|
||||
|
||||
if (rt && GSConfig.SaveRT && s_n >= GSConfig.SaveN)
|
||||
if (rt && GSConfig.SaveRT && s_n >= GSConfig.SaveN && !m_last_rt)
|
||||
{
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_(%05x)_%s.bmp", s_n, frame, m_cached_ctx.FRAME.Block(), rt->m_TEX0.TBP0, psm_str(m_cached_ctx.FRAME.PSM));
|
||||
|
||||
|
@ -3855,7 +3918,7 @@ void GSRendererHW::Draw()
|
|||
ds->m_texture->Save(s);
|
||||
}
|
||||
|
||||
if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)
|
||||
if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL && !m_last_rt)
|
||||
{
|
||||
GSConfig.DumpGSData = 0;
|
||||
}
|
||||
|
@ -3925,7 +3988,7 @@ bool GSRendererHW::VerifyIndices()
|
|||
return true;
|
||||
}
|
||||
|
||||
void GSRendererHW::SetupIA(float target_scale, float sx, float sy)
|
||||
void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert_backup)
|
||||
{
|
||||
GL_PUSH("IA");
|
||||
|
||||
|
@ -4006,7 +4069,20 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy)
|
|||
{
|
||||
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
|
||||
m_conf.vs.expand = GSHWDrawConfig::VSExpand::Sprite;
|
||||
|
||||
if (req_vert_backup)
|
||||
{
|
||||
memcpy(m_draw_vertex.buff, m_vertex.buff, sizeof(GSVertex) * m_vertex.next);
|
||||
memcpy(m_draw_index.buff, m_index.buff, sizeof(u16) * m_index.tail);
|
||||
|
||||
m_conf.verts = m_draw_vertex.buff;
|
||||
m_conf.indices = m_draw_index.buff;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.verts = m_vertex.buff;
|
||||
m_conf.indices = m_index.buff;
|
||||
}
|
||||
m_conf.nverts = m_vertex.next;
|
||||
m_conf.nindices = m_index.tail * 3;
|
||||
m_conf.indices_per_prim = 6;
|
||||
|
@ -4047,9 +4123,20 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy)
|
|||
ASSUME(0);
|
||||
}
|
||||
|
||||
if (req_vert_backup)
|
||||
{
|
||||
memcpy(m_draw_vertex.buff, m_vertex.buff, sizeof(GSVertex) * m_vertex.next);
|
||||
memcpy(m_draw_index.buff, m_index.buff, sizeof(u16) * m_index.tail);
|
||||
|
||||
m_conf.verts = m_draw_vertex.buff;
|
||||
m_conf.indices = m_draw_index.buff;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.verts = m_vertex.buff;
|
||||
m_conf.nverts = m_vertex.next;
|
||||
m_conf.indices = m_index.buff;
|
||||
}
|
||||
m_conf.nverts = m_vertex.next;
|
||||
m_conf.nindices = m_index.tail;
|
||||
}
|
||||
|
||||
|
@ -4477,6 +4564,8 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
|||
// Performance GPU note: it could be wise to reduce the size to
|
||||
// the rendered size of the framebuffer
|
||||
|
||||
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||
m_full_screen_shuffle = (m_r.height() > frame_psm.pgs.y) || (m_r.width() > frame_psm.pgs.x) || GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled;
|
||||
if (GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled || (!m_in_target_draw && IsPageCopy()) || m_conf.ps.urban_chaos_hle || m_conf.ps.tales_of_abyss_hle)
|
||||
{
|
||||
GSVertex* s = &m_vertex.buff[0];
|
||||
|
@ -4491,10 +4580,13 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
|||
s[1].V = 16384;
|
||||
|
||||
m_r = GSVector4i(0, 0, 1024, 1024);
|
||||
|
||||
// We need to count the pages that get shuffled to, some games (like Hitman Blood Money dialogue blur effects) only do half the screen.
|
||||
if (!m_full_screen_shuffle && !m_conf.ps.urban_chaos_hle && !m_conf.ps.tales_of_abyss_hle && src)
|
||||
m_channel_shuffle_width = src->m_TEX0.TBW;
|
||||
}
|
||||
else
|
||||
{
|
||||
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||
const u32 frame_page_offset = std::max(static_cast<int>(((m_r.x / frame_psm.pgs.x) + (m_r.y / frame_psm.pgs.y) * src->m_TEX0.TBW) - m_target_offset), 0);
|
||||
m_r = GSVector4i(m_r.x & ~(frame_psm.pgs.x - 1), m_r.y & ~(frame_psm.pgs.y - 1), (m_r.z + (frame_psm.pgs.x - 1)) & ~(frame_psm.pgs.x - 1), (m_r.w + (frame_psm.pgs.y - 1)) & ~(frame_psm.pgs.y - 1));
|
||||
//m_cached_ctx.FRAME.FBP += frame_page_offset;
|
||||
|
@ -4519,6 +4611,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
|||
m_index.tail = 2;
|
||||
|
||||
m_primitive_covers_without_gaps = NoGapsType::FullCover;
|
||||
m_channel_shuffle_abort = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -6613,7 +6706,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
|||
float sx, sy, ox2, oy2;
|
||||
const float ox = static_cast<float>(static_cast<int>(m_context->XYOFFSET.OFX));
|
||||
const float oy = static_cast<float>(static_cast<int>(m_context->XYOFFSET.OFY));
|
||||
if (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native && rtscale > 1.0f)
|
||||
if ((GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native || m_channel_shuffle) && rtscale > 1.0f)
|
||||
{
|
||||
sx = 2.0f * rtscale / (rtsize.x << 4);
|
||||
sy = 2.0f * rtscale / (rtsize.y << 4);
|
||||
|
@ -6756,7 +6849,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
|||
m_conf.drawarea = m_channel_shuffle ? scissor : scissor.rintersect(ComputeBoundingBox(rtsize, rtscale));
|
||||
m_conf.scissor = (DATE && !DATE_BARRIER) ? m_conf.drawarea : scissor;
|
||||
|
||||
SetupIA(rtscale, sx, sy);
|
||||
SetupIA(rtscale, sx, sy, m_channel_shuffle_width != 0);
|
||||
|
||||
if (ate_second_pass)
|
||||
{
|
||||
|
@ -6844,7 +6937,10 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
|||
|
||||
m_conf.drawlist = (m_conf.require_full_barrier && m_vt.m_primclass == GS_SPRITE_CLASS) ? &m_drawlist : nullptr;
|
||||
|
||||
if (!m_channel_shuffle_width)
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
else
|
||||
m_last_rt = rt;
|
||||
}
|
||||
|
||||
// If the EE uploaded a new CLUT since the last draw, use that.
|
||||
|
@ -7104,7 +7200,7 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
|
|||
{
|
||||
GSTextureCache::Target* rt = g_texture_cache->GetTargetWithSharedBits(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.PSM);
|
||||
|
||||
if (!rt)
|
||||
if (!rt || (!rt->m_dirty.empty() && rt->m_dirty.GetTotalRect(rt->m_TEX0, rt->m_unscaled_size).rintersect(m_r).eq(m_r)))
|
||||
return true;
|
||||
|
||||
rt = nullptr;
|
||||
|
|
|
@ -92,7 +92,7 @@ private:
|
|||
void DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm);
|
||||
|
||||
void ResetStates();
|
||||
void SetupIA(float target_scale, float sx, float sy);
|
||||
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup);
|
||||
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
|
||||
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
|
||||
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, const bool DATE, bool& DATE_PRIMID, bool& DATE_BARRIER, GSTextureCache::Target* rt,
|
||||
|
@ -115,7 +115,7 @@ private:
|
|||
bool IsPossibleChannelShuffle() const;
|
||||
bool IsPageCopy() const;
|
||||
bool NextDrawMatchesShuffle() const;
|
||||
bool IsSplitTextureShuffle(GSTextureCache::Target* rt);
|
||||
bool IsSplitTextureShuffle(GIFRegTEX0& rt_TEX0, GSVector4i& valid_area);
|
||||
GSVector4i GetSplitTextureShuffleDrawRect() const;
|
||||
u32 GetEffectiveTextureShuffleFbmsk() const;
|
||||
|
||||
|
@ -176,6 +176,10 @@ private:
|
|||
u32 m_last_channel_shuffle_fbp = 0;
|
||||
u32 m_last_channel_shuffle_tbp = 0;
|
||||
u32 m_last_channel_shuffle_end_block = 0;
|
||||
u32 m_channel_shuffle_width = 0;
|
||||
bool m_full_screen_shuffle = false;
|
||||
|
||||
GSTextureCache::Target* m_last_rt;
|
||||
|
||||
GIFRegFRAME m_split_clear_start = {};
|
||||
GIFRegZBUF m_split_clear_start_Z = {};
|
||||
|
|
|
@ -1296,9 +1296,23 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
|||
if (!possible_shuffle && frame_fbp != t->m_TEX0.TBP0 && rect_clean && bp == t->m_TEX0.TBP0 && t && GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM) && width_match && real_fmt_match)
|
||||
{
|
||||
if (!tex_merge_rt && t->Overlaps(bp, bw, psm, req_rect))
|
||||
{
|
||||
// Resize but be careful of +bilinear in req_rect, as it can screw valid areas.
|
||||
if (psm_s.bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp && !block_boundary_rect.rintersect(t->m_valid).eq(block_boundary_rect))
|
||||
{
|
||||
RGBAMask rgba_mask;
|
||||
rgba_mask.c.a = req_alpha;
|
||||
rgba_mask.c.r = rgba_mask.c.g = rgba_mask.c.b = req_color;
|
||||
if (block_boundary_rect.z > t->m_valid.z)
|
||||
AddDirtyRectTarget(t, GSVector4i(t->m_valid.z, t->m_valid.y, block_boundary_rect.z, std::max(block_boundary_rect.w, t->m_valid.w)), t->m_TEX0.PSM, t->m_TEX0.TBW, rgba_mask);
|
||||
if (block_boundary_rect.w > t->m_valid.w)
|
||||
AddDirtyRectTarget(t, GSVector4i(t->m_valid.x, t->m_valid.w, std::max(block_boundary_rect.z, t->m_valid.z), block_boundary_rect.w), t->m_TEX0.PSM, t->m_TEX0.TBW, rgba_mask);
|
||||
}
|
||||
// Resize including the extra pixel for bilinear.
|
||||
ResizeTarget(t, req_rect, bp, psm, bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (t->m_TEX0.TBP0 != frame_fbp && !possible_shuffle && bp > t->m_TEX0.TBP0 && t->Overlaps(bp, bw, psm, req_rect) && GSUtil::GetChannelMask(psm) == GSUtil::GetChannelMask(t->m_TEX0.PSM) && !width_match)
|
||||
{
|
||||
|
@ -1800,6 +1814,21 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
|
|||
}
|
||||
}
|
||||
|
||||
if (src->m_from_target && src->m_target_direct && src->m_region.HasEither())
|
||||
{
|
||||
if (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0)
|
||||
{
|
||||
src->m_region.SetX(std::min(region.GetMinX(), src->m_region.GetMinX()), std::max(region.GetMaxX(), src->m_region.GetMaxX()));
|
||||
src->m_region.SetY(std::min(region.GetMinY(), src->m_region.GetMinY()), std::max(region.GetMaxY(), src->m_region.GetMaxY()));
|
||||
}
|
||||
else if (src->m_TEX0.TBP0 > src->m_from_target->m_TEX0.TBP0)
|
||||
{
|
||||
GSVector4i dst_offset = TranslateAlignedRectByPage(src->m_from_target, src->m_TEX0.TBP0, src->m_TEX0.PSM, src->m_TEX0.TBW, GSVector4i(0, 0, 1, 1), false);
|
||||
src->m_region.SetX(dst_offset.x + region.GetMinX(), dst_offset.x + region.GetMaxX());
|
||||
src->m_region.SetY(dst_offset.y + region.GetMinY(), dst_offset.y + region.GetMaxY());
|
||||
}
|
||||
}
|
||||
|
||||
if (gpu_clut)
|
||||
AttachPaletteToSource(src, gpu_clut);
|
||||
else if (src->m_palette && (!src->m_palette_obj || !src->ClutMatch({clut, psm_s.pal})))
|
||||
|
@ -1830,7 +1859,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, bool preserve_scale, GSTextureCache::Source* src, int offset)
|
||||
bool is_shuffle, bool possible_clear, bool preserve_scale, GSTextureCache::Source* src, GSTextureCache::Target* ds, int offset)
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
const u32 bp = TEX0.TBP0;
|
||||
|
@ -1968,6 +1997,23 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
}
|
||||
else if (t->m_dirty.empty())
|
||||
{
|
||||
|
||||
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63)/ 64) > 1)
|
||||
{
|
||||
// Beyond Good and Evil does this awful thing where it puts one framebuffer at 0xf00, with the first row of pages blanked out, and the whole thing goes down to 0x2080
|
||||
// which is a problem, because it then puts the Z buffer at 0x1fc0, then offsets THAT by 1 row of pages, so it starts at, you guessed it, 2080.
|
||||
// So let's check the *real* start.
|
||||
u32 real_start_address = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_drawn_since_read);
|
||||
u32 new_end_address = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
|
||||
// Not really overlapping.
|
||||
if (real_start_address > new_end_address)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//DevCon.Warning("Here draw %d wanted %x PSM %x got %x PSM %x offset of %d pages width %d pages draw width %d", GSState::s_n, bp, TEX0.PSM, t->m_TEX0.TBP0, t->m_TEX0.PSM, (bp - t->m_TEX0.TBP0) >> 5, t->m_TEX0.TBW, draw_rect.width());
|
||||
dst = t;
|
||||
|
||||
|
@ -2322,7 +2368,9 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
|||
continue;
|
||||
}
|
||||
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
|
||||
if (!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||
// Make sure it's not currently in use, that could be bad.
|
||||
if (!is_shuffle && (!ds || (ds != t)) &&
|
||||
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||
{
|
||||
DevCon.Warning("Deleting Z draw %d", GSState::s_n);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
|
@ -2637,11 +2685,12 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
|||
|
||||
RGBAMask rgba;
|
||||
rgba._u32 = GSUtil::GetChannelMask(TEX0.PSM);
|
||||
|
||||
dst->UpdateValidity(GSVector4i::loadh(valid_size));
|
||||
|
||||
if (!is_frame && !preload && !(src && src->m_TEX0.TBP0 == dst->m_TEX0.TBP0))
|
||||
{
|
||||
if ((preserve_target || !draw_rect.eq(dst->m_valid)) && GSRendererHW::GetInstance()->m_draw_transfers.size() > 0)
|
||||
if ((preserve_target || !draw_rect.eq(GSVector4i::loadh(valid_size))) && GSRendererHW::GetInstance()->m_draw_transfers.size() > 0)
|
||||
{
|
||||
auto& transfers = GSRendererHW::GetInstance()->m_draw_transfers;
|
||||
const int last_draw = transfers.back().draw;
|
||||
|
@ -2735,8 +2784,6 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
|||
{
|
||||
const GSVector4i save_rect = preserve_target ? newrect : eerect;
|
||||
|
||||
if (!hw_clear)
|
||||
dst->UpdateValidity(save_rect);
|
||||
GL_INS("Preloading the RT DATA from updated GS Memory");
|
||||
AddDirtyRectTarget(dst, save_rect, TEX0.PSM, TEX0.TBW, rgba, GSLocalMemory::m_psm[TEX0.PSM].trbpp >= 16);
|
||||
}
|
||||
|
@ -4878,7 +4925,8 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
|||
src->m_valid_rect = dst->m_valid;
|
||||
src->m_end_block = dst->m_end_block;
|
||||
|
||||
// Do this first as we could be adding in alpha from an upgraded 24bit target.
|
||||
// Do this first as we could be adding in alpha from an upgraded 24bit target. if the rect intersects a dirty area.
|
||||
if (!dst->m_dirty.empty() && !src_range->rintersect(dst->m_dirty.GetTotalRect(dst->m_TEX0, dst->m_unscaled_size)).rempty())
|
||||
dst->Update();
|
||||
|
||||
src->m_valid_alpha_minmax = true;
|
||||
|
@ -6596,7 +6644,7 @@ void GSTextureCache::Target::Update(bool cannot_scale)
|
|||
{
|
||||
if (alpha_minmax.second > 128 || (m_TEX0.PSM & 0xf) == PSMCT24)
|
||||
UnscaleRTAlpha();
|
||||
else if (!cannot_scale && total_rect.eq(m_valid))
|
||||
else if (!cannot_scale && total_rect.rintersect(m_valid).eq(m_valid))
|
||||
m_rt_alpha_scale = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -492,7 +492,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, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, int offset = -1);
|
||||
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
|
||||
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