Compare commits

...

17 Commits

Author SHA1 Message Date
refractionpcsx2
bd20651605 GS/TC: Correct some preload behaviour on merging targets 2026-01-18 20:55:11 +00:00
refractionpcsx2
edb2b37a92 GS/HW: Use old hazard copy method when there's no multidraw or barriers 2026-01-17 19:48:01 +01:00
refractionpcsx2
4462b3f91d GS/HW: Add detection for continuous possible vertical shuffles 2026-01-17 19:48:01 +01:00
refractionpcsx2
57ded8a022 GS/HW: Support offsetting for channel shuffle instead of copying 2026-01-17 19:48:01 +01:00
refractionpcsx2
304a7f9d30 GameDB: Add necessary fixes for MotoGP 2026-01-17 18:18:00 +01:00
refractionpcsx2
73a09ffe6c GS/HW: Improve channel shuffle detection and use on 24bit sources 2026-01-17 18:18:00 +01:00
refractionpcsx2
4e5d7bd407 GS/TC: Delete empty target after height adjust 2026-01-17 16:58:37 +01:00
refractionpcsx2
2a3452a489 GameDB: Remove Preload Frame from PachiPara as it just causes problems 2026-01-17 16:52:27 +01:00
refractionpcsx2
2e7f951399 GS/HW: Fix up iRem CRC hack to be more accurate 2026-01-17 16:52:27 +01:00
refractionpcsx2
efb66c1d37 GS/TC: Small optimization to local memory invalidation 2026-01-17 16:32:10 +01:00
refractionpcsx2
6fc88a4499 GS/HW: Improve fog modulation accuracy in HW renderers 2026-01-17 16:32:10 +01:00
refractionpcsx2
238b29836e GS/TC: Only bilinearly scale render target if preserve_scale is not set 2026-01-17 15:53:08 +01:00
refractionpcsx2
58cbb61aac GS/TC: Only bilinear filter depth scales 2026-01-17 15:53:08 +01:00
refractionpcsx2
2c7a168029 GS/HW: Properly scale depth up if required when native scaling is in use 2026-01-17 15:53:08 +01:00
JordanTheToaster
bb4ee5f0fb GS/HW: Optimize source sizes when using CLAMP_CLAMP 2026-01-16 14:37:22 -05:00
RedPanda4552
1940fdb3d3 Revert "Qt: Patch for distinguishing left and right variants of shift/alt/ctrl keys"
This reverts commit 3871d1bd5d.
2026-01-16 12:55:41 -05:00
RedPanda4552
bf269e1295 Revert "PAD: Fix hotkeys to differentiate between left and right keyboard keys"
This reverts commit 3e87bec0c0.
2026-01-16 12:55:41 -05:00
18 changed files with 428 additions and 238 deletions

View File

@@ -4764,6 +4764,9 @@ SCES-50034:
name: "MotoGP"
region: "PAL-M5"
compat: 5
gsHWFixes:
gpuTargetCLUT: 2 # Fixes some (haze?) shuffle colors.
textureInsideRT: 1 # Fixes rendering artifacts on racers.
SCES-50105:
name: "Sky Odyssey"
region: "PAL-M5"
@@ -54743,6 +54746,9 @@ SLPS-20040:
name-sort: "もとGP"
name-en: "MotoGP"
region: "NTSC-J"
gsHWFixes:
gpuTargetCLUT: 2 # Fixes some (haze?) shuffle colors.
textureInsideRT: 1 # Fixes rendering artifacts on racers.
SLPS-20041:
name: "麻雀悟空 大聖"
name-sort: "まーじゃんごくう たいせい"
@@ -60491,7 +60497,6 @@ SLPS-25574:
halfPixelOffset: 2 # Aligns effects.
nativeScaling: 1 # Helps align effects.
textureInsideRT: 1 # Fixes channel shuffles.
preloadFrameData: 1 # Fixes menu graphics.
autoFlush: 1 # Fixes motion blur.
minimumBlendingLevel: 2 # Improves post effects.
SLPS-25575:
@@ -61216,7 +61221,6 @@ SLPS-25682:
halfPixelOffset: 2 # Aligns effects.
nativeScaling: 1 # Helps align effects.
textureInsideRT: 1 # Fixes channel shuffles.
preloadFrameData: 1 # Fixes menu graphics.
autoFlush: 1 # Fixes motion blur.
minimumBlendingLevel: 2 # Improves post effects.
SLPS-25683:
@@ -61846,7 +61850,6 @@ SLPS-25787:
halfPixelOffset: 2 # Aligns effects.
nativeScaling: 1 # Helps align effects.
textureInsideRT: 1 # Fixes channel shuffles.
preloadFrameData: 1 # Fixes menu graphics.
autoFlush: 1 # Fixes motion blur.
minimumBlendingLevel: 2 # Improves post effects.
SLPS-25788:
@@ -62258,7 +62261,6 @@ SLPS-25852:
halfPixelOffset: 2 # Aligns effects.
nativeScaling: 1 # Helps align effects.
textureInsideRT: 1 # Fixes channel shuffles.
preloadFrameData: 1 # Fixes menu graphics.
autoFlush: 1 # Fixes motion blur.
minimumBlendingLevel: 2 # Improves post effects.
SLPS-25853:
@@ -64233,6 +64235,9 @@ SLUS-20058:
name: "MotoGP"
region: "NTSC-U"
compat: 5
gsHWFixes:
gpuTargetCLUT: 2 # Fixes some (haze?) shuffle colors.
textureInsideRT: 1 # Fixes rendering artifacts on racers.
SLUS-20062:
name: "Grand Theft Auto III"
region: "NTSC-U"

View File

@@ -168,6 +168,7 @@ cbuffer cb1
float4 LODParams;
float4 STRange;
int4 ChannelShuffle;
float2 ChannelShuffleOffset;
float2 TC_OffsetHack;
float2 STScale;
float4x4 DitherMatrix;
@@ -740,7 +741,7 @@ float4 fog(float4 c, float f)
{
if(PS_FOG)
{
c.rgb = trunc(lerp(FogColor, c.rgb, f));
c.rgb = trunc(lerp(FogColor, c.rgb, (f * 255.0f) / 256.0f));
}
return c;
@@ -757,17 +758,17 @@ float4 ps_color(PS_INPUT input)
#endif
#if PS_CHANNEL_FETCH == 1
float4 T = fetch_red(int2(input.p.xy));
float4 T = fetch_red(int2(input.p.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 2
float4 T = fetch_green(int2(input.p.xy));
float4 T = fetch_green(int2(input.p.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 3
float4 T = fetch_blue(int2(input.p.xy));
float4 T = fetch_blue(int2(input.p.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 4
float4 T = fetch_alpha(int2(input.p.xy));
float4 T = fetch_alpha(int2(input.p.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 5
float4 T = fetch_rgb(int2(input.p.xy));
float4 T = fetch_rgb(int2(input.p.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 6
float4 T = fetch_gXbY(int2(input.p.xy));
float4 T = fetch_gXbY(int2(input.p.xy + ChannelShuffleOffset));
#elif PS_DEPTH_FMT > 0
float4 T = sample_depth(st_int, input.p.xy);
#else

View File

@@ -49,6 +49,7 @@ layout(std140, binding = 0) uniform cb21
vec4 STRange;
ivec4 ChannelShuffle;
vec2 ChannelShuffleOffset;
vec2 TC_OffsetHack;
vec2 STScale;
@@ -315,7 +316,7 @@ int fetch_raw_depth()
#if PS_TEX_IS_FB == 1
return int(sample_from_rt().r * multiplier);
#else
return int(texelFetch(TextureSampler, ivec2(gl_FragCoord.xy), 0).r * multiplier);
return int(texelFetch(TextureSampler, ivec2(gl_FragCoord.xy + ChannelShuffleOffset), 0).r * multiplier);
#endif
}
@@ -324,7 +325,7 @@ vec4 fetch_raw_color()
#if PS_TEX_IS_FB == 1
return sample_from_rt();
#else
return texelFetch(TextureSampler, ivec2(gl_FragCoord.xy), 0);
return texelFetch(TextureSampler, ivec2(gl_FragCoord.xy + ChannelShuffleOffset), 0);
#endif
}
@@ -646,7 +647,7 @@ bool atst(vec4 C)
void fog(inout vec4 C, float f)
{
#if PS_FOG != 0
C.rgb = trunc(mix(FogColor, C.rgb, f));
C.rgb = trunc(mix(FogColor, C.rgb, (f * 255.0f) / 256.0f));
#endif
}

View File

@@ -316,6 +316,7 @@ layout(std140, set = 0, binding = 1) uniform cb1
vec4 LODParams;
vec4 STRange;
ivec4 ChannelShuffle;
vec2 ChannelShuffleOffset;
vec2 TC_OffsetHack;
vec2 STScale;
mat4 DitherMatrix;
@@ -907,7 +908,7 @@ bool atst(vec4 C)
vec4 fog(vec4 c, float f)
{
#if PS_FOG
c.rgb = trunc(mix(FogColor, c.rgb, f));
c.rgb = trunc(mix(FogColor, c.rgb, (f * 255.0f) / 256.0f));
#endif
return c;
@@ -926,17 +927,17 @@ vec4 ps_color()
#if !NEEDS_TEX
vec4 T = vec4(0.0f);
#elif PS_CHANNEL_FETCH == 1
vec4 T = fetch_red(ivec2(gl_FragCoord.xy));
vec4 T = fetch_red(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 2
vec4 T = fetch_green(ivec2(gl_FragCoord.xy));
vec4 T = fetch_green(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 3
vec4 T = fetch_blue(ivec2(gl_FragCoord.xy));
vec4 T = fetch_blue(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 4
vec4 T = fetch_alpha(ivec2(gl_FragCoord.xy));
vec4 T = fetch_alpha(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 5
vec4 T = fetch_rgb(ivec2(gl_FragCoord.xy));
vec4 T = fetch_rgb(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
#elif PS_CHANNEL_FETCH == 6
vec4 T = fetch_gXbY(ivec2(gl_FragCoord.xy));
vec4 T = fetch_gXbY(ivec2(gl_FragCoord.xy + ChannelShuffleOffset));
#elif PS_DEPTH_FMT > 0
vec4 T = sample_depth(st_int, ivec2(gl_FragCoord.xy));
#else

View File

@@ -67,17 +67,6 @@ struct KeyCodeName
const char* icon_name;
};
// Extended keys for left/right versions, Non conflicting with Qt::Key enum
enum ExtendedKeys
{
Key_RightShift = 0x01100022,
Key_LeftShift = 0x01100023,
Key_RightControl = 0x01100024,
Key_LeftControl = 0x01100025,
Key_RightAlt = 0x01100026,
Key_LeftAlt = 0x01100027,
};
static constexpr const KeyCodeName s_qt_key_names[] = {
{Qt::Key_Escape, "Escape", ICON_PF_ESC},
{Qt::Key_Tab, "Tab", ICON_PF_TAB},
@@ -103,13 +92,6 @@ static constexpr const KeyCodeName s_qt_key_names[] = {
{Qt::Key_Control, "Control", ICON_PF_CTRL},
{Qt::Key_Meta, "Meta", ICON_PF_SUPER},
{Qt::Key_Alt, "Alt", ICON_PF_ALT},
// patch for left and right versions of the keys
{ExtendedKeys::Key_LeftShift, "LShift", ICON_PF_SHIFT},
{ExtendedKeys::Key_RightShift, "RShift", ICON_PF_SHIFT},
{ExtendedKeys::Key_LeftControl, "LCtrl", ICON_PF_CTRL},
{ExtendedKeys::Key_RightControl, "RCtrl", ICON_PF_CTRL},
{ExtendedKeys::Key_LeftAlt, "LAlt", ICON_PF_ALT},
{ExtendedKeys::Key_RightAlt, "RAlt", ICON_PF_ALT},
{Qt::Key_CapsLock, "CapsLock", ICON_PF_CAPS},
{Qt::Key_NumLock, "NumLock", ICON_PF_NUMLOCK},
{Qt::Key_ScrollLock, "ScrollLock", ICON_PF_SCRLK},
@@ -593,41 +575,6 @@ u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
const u8 keycode = set_keycode ? map_text_to_keycode(text) : 0;
int key = ev->key();
if (key == Qt::Key_Shift || key == Qt::Key_Alt || key == Qt::Key_Control)
{
#if defined(Q_OS_WIN) or defined(Q_OS_LINUX)
// Scan codes (Tested it)
// 0x2A : Left shift
// 0x36 : Right shift
// 0x1D : Left ctrl
// 0xE01D : Right ctrl
// 0x38 : Left alt
// right alt can become ctrl + right alt in some keyboard layouts (windows)
// but thats fine for our use case
switch (ev->nativeScanCode())
{
case 0x2A:
key = ExtendedKeys::Key_LeftShift;
break;
case 0x36:
key = ExtendedKeys::Key_RightShift;
break;
case 0x1D:
key = ExtendedKeys::Key_LeftControl;
break;
case 0xE01D:
key = ExtendedKeys::Key_RightControl;
break;
case 0x38:
key = ExtendedKeys::Key_LeftAlt;
break;
default:
break;
}
#endif
}
if (keycode != 0)
key = keycode; // Override key if mapped

View File

@@ -1202,7 +1202,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_abort = true;
m_channel_shuffle_finish = true;
}
TEX0.CPSM &= 0xa; // 1010b

View File

@@ -258,7 +258,7 @@ public:
bool m_using_temp_z = false;
bool m_temp_z_full_copy = false;
bool m_in_target_draw = false;
bool m_channel_shuffle_abort = false;
bool m_channel_shuffle_finish = false;
u32 m_target_offset = 0;
u8 m_scanmask_used = 0;

View File

@@ -622,6 +622,7 @@ struct alignas(16) GSHWDrawConfig
GSVector4 LODParams;
GSVector4 STRange;
GSVector4i ChannelShuffle;
GSVector2 ChannelShuffleOffset;
GSVector2 TCOffsetHack;
GSVector2 STScale;

View File

@@ -39,15 +39,77 @@ static bool s_nativeres;
bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
{
static bool first_shuffle = false;
if (skip > 0)
{
if (skip == 1 && first_shuffle)
{
first_shuffle = false;
GIFRegTEX0 RTLookup = GIFRegTEX0::Create(RTBP0, RFBW, RFPSM);
GSTextureCache::Source* src = g_texture_cache->LookupSource(true, RTLookup, r.m_cached_ctx.TEXA, r.m_cached_ctx.CLAMP, GSVector4i(0, 0, 1, 1), nullptr, true, false, r.m_cached_ctx.FRAME, true, true);
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i(0, 0, 1, 1), true, false, true, src);
if (!rt)
return false;
GSLocalMemory::psm_t rt_psm = GSLocalMemory::m_psm[RFPSM];
int page_offset = (RTBP0 - rt->m_TEX0.TBP0) >> 5;
int vertical_offset = page_offset / std::max(rt->m_TEX0.TBW, 1U) * rt_psm.pgs.y;
int horizontal_offset = page_offset % std::max(rt->m_TEX0.TBW, 1U) * rt_psm.pgs.x;
GSVector4i draw_size = GSVector4i(0, 0, 64, 32) + GSVector4i(horizontal_offset, vertical_offset, horizontal_offset, vertical_offset);
rt->UnscaleRTAlpha();
// We need the original red back now for the next channel shuffle.
GSHWDrawConfig& config = r.BeginHLEHardwareDraw(
rt->GetTexture(), nullptr, rt->GetScale(), rt->GetTexture(), rt->GetScale(), draw_size);
config.ps.shuffle = 1;
config.ps.dst_fmt = GSLocalMemory::PSM_FMT_32;
config.ps.write_rg = 0;
config.ps.shuffle_same = 0;
config.ps.real16src = 0;
config.ps.shuffle_across = 1;
config.ps.process_rg = r.SHUFFLE_READWRITE;
config.ps.process_ba = r.SHUFFLE_READWRITE;
config.colormask.wrgba = 0;
config.colormask.wr = 1;
config.colormask.wb = 1;
config.ps.rta_correction = 0;
config.ps.rta_source_correction = 0;
config.ps.tfx = TFX_DECAL;
config.ps.tcc = true;
r.EndHLEHardwareDraw(true);
rt->m_alpha_min = 0;
rt->m_alpha_max = 255;
rt = nullptr;
src = nullptr;
}
else
{
skip--;
return !first_shuffle;
}
}
if (skip == 0)
{
const int get_next_ctx = r.m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = r.m_env.CTXT[get_next_ctx];
// Uses these to do some shuffle tricks, it breaks things for us
r.m_env.SCANMSK.MSK = 0;
r.m_prev_env.SCANMSK.MSK = 0;
// Game does alternate line channel shuffles with blending, we can't handle this and the first one does it, so skip the second.
if (RTME && RTPSM == PSMT8 && (RTBP0 + 0x20) == next_ctx.TEX0.TBP0 && RFBP == next_ctx.FRAME.Block())
{
skip = 1;
skip = 2;
return false;
}
// Detect the deswizzling shuffle from depth, copying the RG and BA separately on each half of the page (ignore the split).
@@ -90,7 +152,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
draw_size = draw_size + GSVector4i(horizontal_offset, vertical_offset, horizontal_offset, vertical_offset);
rt->UnscaleRTAlpha();
// Shuffle the blue channel in to red, leave green as-is.
// Shuffle the blue channel in to red, but swap them, we'll need the original red later.
GSHWDrawConfig& config = r.BeginHLEHardwareDraw(
rt->GetTexture(), nullptr, rt->GetScale(), rt->GetTexture(), rt->GetScale(), draw_size);
config.ps.shuffle = 1;
@@ -99,10 +161,11 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
config.ps.shuffle_same = 0;
config.ps.real16src = 0;
config.ps.shuffle_across = 1;
config.ps.process_rg = r.SHUFFLE_WRITE;
config.ps.process_ba = r.SHUFFLE_READ;
config.ps.process_rg = r.SHUFFLE_READWRITE;
config.ps.process_ba = r.SHUFFLE_READWRITE;
config.colormask.wrgba = 0;
config.colormask.wr = 1;
config.colormask.wb = 1;
config.ps.rta_correction = 0;
config.ps.rta_source_correction = 0;
config.ps.tfx = TFX_DECAL;
@@ -114,6 +177,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
rt = nullptr;
src = nullptr;
first_shuffle = true;
}
}
}

View File

@@ -2345,9 +2345,18 @@ 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 = !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;
const bool is_hle_skip = m_conf.ps.urban_chaos_hle || m_conf.ps.tales_of_abyss_hle;
const u32 max_skip = ((m_channel_shuffle_finish || !m_channel_shuffle_width) ? std::max(m_context->FRAME.FBW, 1U) : m_channel_shuffle_width) << 5;
const bool shuffle_detect = IsPossibleChannelShuffle() && m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && (m_last_channel_shuffle_fbp + max_skip) >= m_context->FRAME.Block() &&
m_last_channel_shuffle_end_block > m_context->FRAME.Block() && m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0
&& (m_last_channel_shuffle_tbp + max_skip) >= m_context->TEX0.TBP0;
const bool shuffle_detect_loose = 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;
m_channel_shuffle = !m_channel_shuffle_finish && ((!is_hle_skip && shuffle_detect) || (is_hle_skip && shuffle_detect_loose));
if (m_channel_shuffle)
{
@@ -2415,12 +2424,21 @@ void GSRendererHW::Draw()
CleanupDraw(false);
}
}
if (!shuffle_detect)
{
m_last_channel_shuffle_fbp = 0xffff;
m_last_channel_shuffle_tbp = 0xffff;
m_last_channel_shuffle_end_block = 0xffff;
}
#ifdef ENABLE_OGL_DEBUG
if (num_skipped_channel_shuffle_draws > 0)
GL_CACHE("HW: Skipped %d channel shuffle draws ending at %d", num_skipped_channel_shuffle_draws, s_n);
#endif
num_skipped_channel_shuffle_draws = 0;
}
else
{
m_last_channel_shuffle_fbp = 0xffff;
m_last_channel_shuffle_tbp = 0xffff;
m_last_channel_shuffle_end_block = 0xffff;
@@ -2429,7 +2447,7 @@ void GSRendererHW::Draw()
m_last_rt = nullptr;
m_channel_shuffle_width = 0;
m_full_screen_shuffle = false;
m_channel_shuffle_abort = false;
m_channel_shuffle_finish = false;
m_channel_shuffle_src_valid = GSVector4i::zero();
GL_PUSH("HW: Draw %d (Context %u)", s_n, PRIM->CTXT);
@@ -3085,15 +3103,16 @@ void GSRendererHW::Draw()
}
}
}
possible_shuffle = !no_rt && (((shuffle_target /*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || IsPossibleChannelShuffle());
const bool is_possible_channel_shuffle = IsPossibleChannelShuffle();
possible_shuffle = !no_rt && (((shuffle_target /*&& GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16*/) /*|| (m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 && ((m_cached_ctx.TEX0.PSM & 0x6) || m_cached_ctx.FRAME.PSM != m_cached_ctx.TEX0.PSM))*/) || is_possible_channel_shuffle);
const u32 channel_shuffle_targets = is_possible_channel_shuffle ? EmulateChannelShuffle(nullptr, true) : ChannelFetch_NONE;
const bool need_aem_color = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 24 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal == 0 && ((NeedsBlending() && m_context->ALPHA.C == 0) || IsDiscardingDstAlpha()) && m_cached_ctx.TEXA.AEM;
const u32 color_mask = (m_vt.m_max.c > GSVector4i::zero()).mask();
const bool texture_function_color = m_cached_ctx.TEX0.TFX == TFX_DECAL || (color_mask & 0xFFF) || (m_cached_ctx.TEX0.TFX > TFX_DECAL && (color_mask & 0xF000));
const bool texture_function_alpha = m_cached_ctx.TEX0.TFX != TFX_MODULATE || (color_mask & 0xF000);
const bool req_color = (texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color;
const bool req_color = (is_possible_channel_shuffle && channel_shuffle_targets != ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && ((texture_function_color && (!PRIM->ABE || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp < 16 || (NeedsBlending() && IsUsingCsInBlend())) && (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0x00FFFFFF)) != (fm_mask & 0x00FFFFFF))) || need_aem_color));
const bool alpha_used = (GSUtil::GetChannelMask(m_context->TEX0.PSM) == 0x8 || (m_context->TEX0.TCC && texture_function_alpha)) && ((NeedsBlending() && IsUsingAsInBlend()) || (m_cached_ctx.TEST.ATE && m_cached_ctx.TEST.ATST > ATST_ALWAYS) || (possible_shuffle || (m_cached_ctx.FRAME.FBMSK & (fm_mask & 0xFF000000)) != (fm_mask & 0xFF000000)));
const bool req_alpha = (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used;
const bool req_alpha = (is_possible_channel_shuffle && channel_shuffle_targets == ChannelFetch_ALPHA) || (!is_possible_channel_shuffle && (GSUtil::GetChannelMask(m_context->TEX0.PSM) & 0x8) && alpha_used);
// TODO: Be able to send an alpha of 1.0 (blended with vertex alpha maybe?) so we can avoid sending the texture, since we don't always need it.
// Example games: Evolution Snowboarding, Final Fantasy Dirge of Cerberus, Red Dead Revolver, Stuntman, Tony Hawk's Underground 2, Ultimate Spider-Man.
@@ -3277,7 +3296,7 @@ void GSRendererHW::Draw()
ZBUF_TEX0.PSM = m_cached_ctx.ZBUF.PSM;
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,
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(), !no_rt,
src, nullptr, -1);
ZBUF_TEX0.TBW = m_channel_shuffle ? src->m_from_target_TEX0.TBW : m_cached_ctx.FRAME.FBW;
@@ -3892,7 +3911,7 @@ void GSRendererHW::Draw()
{
const GSVector2i unscaled_size(ds->m_unscaled_size.x, ds->m_unscaled_size.y);
ds->m_scale = 1;
ds->ResizeTexture(ds->m_unscaled_size.x * target_scale, ds->m_unscaled_size.y * target_scale, true);
ds->ResizeTexture(ds->m_unscaled_size.x * target_scale, ds->m_unscaled_size.y * target_scale, true, true, GSVector4i::loadh(ds->m_unscaled_size * target_scale));
// Slightly abusing the texture resize.
ds->m_scale = target_scale;
ds->m_unscaled_size = unscaled_size;
@@ -3904,7 +3923,6 @@ void GSRendererHW::Draw()
if (m_channel_shuffle)
{
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
m_last_channel_shuffle_tbp = src->m_TEX0.TBP0;
// If it's a new target, we don't know where the end is as it's starting on a shuffle, so just do every shuffle following.
@@ -4041,7 +4059,6 @@ void GSRendererHW::Draw()
m_last_channel_shuffle_fbmsk = m_context->FRAME.FBMSK;
if (rt)
{
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
m_last_channel_shuffle_tbp = src->m_TEX0.TBP0;
// Urban Chaos goes from Z16 to C32, so let's just use the rt's original end block.
if (!src->m_from_target || GSLocalMemory::m_psm[src->m_from_target_TEX0.PSM].bpp != GSLocalMemory::m_psm[rt->m_TEX0.PSM].bpp)
@@ -4268,7 +4285,7 @@ void GSRendererHW::Draw()
if (rt)
{
const bool update_fbw = (FRAME_TEX0.TBW != rt->m_TEX0.TBW || rt->m_TEX0.TBW == 1) && !m_in_target_draw && (m_channel_shuffle && src->m_target) && (!NeedsBlending() || IsOpaque() || m_context->ALPHA.IsBlack());
rt->m_TEX0.TBW = update_fbw ? ((src && src->m_from_target && src->m_32_bits_fmt) ? src->m_from_target->m_TEX0.TBW : FRAME_TEX0.TBW) : std::max(rt->m_TEX0.TBW, FRAME_TEX0.TBW);
rt->m_TEX0.TBW = update_fbw ? ((src && src->m_from_target && src->m_from_target->m_32_bits_fmt) ? src->m_from_target->m_TEX0.TBW : FRAME_TEX0.TBW) : std::max(rt->m_TEX0.TBW, FRAME_TEX0.TBW);
rt->m_TEX0.PSM = FRAME_TEX0.PSM;
}
if (ds)
@@ -4740,6 +4757,10 @@ void GSRendererHW::Draw()
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(real_rect, !frame_masked && (can_update_size || (real_rect.w <= (resolution.y * 2) && !m_texture_shuffle)));
if (m_channel_shuffle)
{
m_last_channel_shuffle_fbp = rt->m_TEX0.TBP0;
}
}
if (ds)
@@ -5341,13 +5362,13 @@ bool GSRendererHW::TestChannelShuffle(GSTextureCache::Target* src)
const bool shuffle = m_channel_shuffle || IsPossibleChannelShuffle();
// This is a little redundant since it'll get called twice, but the only way to stop us wasting time on copies.
m_channel_shuffle = (shuffle && EmulateChannelShuffle(src, true));
m_channel_shuffle = (shuffle && EmulateChannelShuffle(src, true)) != 0;
return m_channel_shuffle;
}
__ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt)
__ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt)
{
if ((src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
if (src && (src->m_texture->GetType() == GSTexture::Type::DepthStencil) && !src->m_32_bits_fmt)
{
// So far 2 games hit this code path. Urban Chaos and Tales of Abyss
// UC: will copy depth to green channel
@@ -5357,7 +5378,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
// Green channel is masked
GL_INS("HW: HLE Shuffle Tales Of Abyss");
if (test_only)
return true;
return ChannelFetch_RGB;
m_conf.ps.tales_of_abyss_hle = 1;
}
@@ -5365,7 +5386,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
{
GL_INS("HW: HLE Shuffle Urban Chaos");
if (test_only)
return true;
return ChannelFetch_RGB;
m_conf.ps.urban_chaos_hle = 1;
}
@@ -5380,18 +5401,18 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
// handled above.
GL_INS("HW: Might not be channel shuffle");
if (test_only)
return false;
return ChannelFetch_NONE;
m_channel_shuffle = false;
return false;
}
else if (m_cached_ctx.CLAMP.WMS == 3 && ((m_cached_ctx.CLAMP.MAXU & 0x8) == 8))
{
const ChannelFetch channel_select = ((m_cached_ctx.CLAMP.WMT != 3 && (m_vertex.buff[m_index.buff[0]].V & 0x20) == 0) || (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 0))) ? ChannelFetch_BLUE : ChannelFetch_ALPHA;
// MGS3/Kill Zone
if (test_only)
return true;
const ChannelFetch channel_select = ((m_cached_ctx.CLAMP.WMT != 3 && (m_vertex.buff[m_index.buff[0]].V & 0x20) == 0) || (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 0))) ? ChannelFetch_BLUE : ChannelFetch_ALPHA;
return channel_select;
GL_INS("HW: %s channel", (channel_select == ChannelFetch_BLUE) ? "blue" : "alpha");
@@ -5401,7 +5422,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
{
// Read either Red or Green. Let's check the V coordinate. 0-1 is likely top so
// red. 2-3 is likely bottom so green (actually depends on texture base pointer offset)
const bool green = PRIM->FST && (m_vertex.buff[0].V & 32);
const bool green = (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 2)) || (PRIM->FST && (m_vertex.buff[0].V & 32));
if (green && (m_cached_ctx.FRAME.FBMSK & 0x00FFFFFF) == 0x00FFFFFF)
{
// Typically used in Terminator 3
@@ -5429,7 +5450,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
GL_INS("HW: Green/Blue channel (%d, %d)", blue_shift, green_shift);
if (test_only)
return true;
return ChannelFetch_GXBY;
m_conf.cb_ps.ChannelShuffle = GSVector4i(blue_mask, blue_shift, green_mask, green_shift);
m_conf.ps.channel = ChannelFetch_GXBY;
@@ -5439,7 +5460,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
{
GL_INS("HW: Green channel (wrong mask) (fbmask %x)", blue_mask);
if (test_only)
return true;
return ChannelFetch_GREEN;
m_conf.ps.channel = ChannelFetch_GREEN;
}
@@ -5448,7 +5469,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
{
GL_INS("HW: Green channel");
if (test_only)
return true;
return ChannelFetch_GREEN;
m_conf.ps.channel = ChannelFetch_GREEN;
}
@@ -5457,7 +5478,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
// Pop
GL_INS("HW: Red channel");
if (test_only)
return true;
return ChannelFetch_RED;
m_conf.ps.channel = ChannelFetch_RED;
}
@@ -5502,7 +5523,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
#endif
if (test_only)
return true;
return channel;
m_conf.ps.channel = channel;
}
@@ -5512,7 +5533,7 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
m_r.x, m_r.y, m_r.z, m_r.w, min_uv.x, min_uv.y);
if (test_only)
return false;
return ChannelFetch_NONE;
m_channel_shuffle = false;
return false;
@@ -5546,7 +5567,6 @@ __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)
{
@@ -5556,21 +5576,14 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
m_channel_shuffle_width = src->m_TEX0.TBW;
}
m_channel_shuffle_finish = false;
}
else
{
const u32 frame_page_offset = std::max(static_cast<int>(((m_r.x / frame_psm.pgs.x) + (m_r.y / frame_psm.pgs.y) * rt->m_TEX0.TBW)), 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));
// Hitman suffers from this, not sure on the exact scenario at the moment, but we need the barrier.
if (NeedsBlending() && m_context->ALPHA.IsCdInBlend())
{
// Needed to enable IsFeedbackLoop.
m_conf.ps.channel_fb = 1;
// Assume no overlap when it's a channel shuffle, no need for full barriers.
m_conf.require_one_barrier = true;
}
// This is for offsetting the texture, however if the texture has a region clamp, we don't want to move it.
// A good two test games for this is Ghost in the Shell (no region clamp) and Tekken 5 (offset clamp on shadows)
if (rt && rt->m_TEX0.TBP0 == m_cached_ctx.FRAME.Block())
@@ -5594,7 +5607,6 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
s[1].U = m_r.z << 4;
s[0].V = m_r.y << 4;
s[1].V = m_r.w << 4;
m_last_channel_shuffle_fbmsk = 0xFFFFFFFF;
// If we're doing per page copying, then set the valid 1 frame ahead if we're continuing, as this will save the target lookup making a new target for the new row.
const u32 frame_offset = m_cached_ctx.FRAME.Block() + (IsPageCopy() ? 0x20 : 0);
@@ -5613,13 +5625,15 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
new_valid.w = std::max(new_valid.w, offset_height);
rt->UpdateValidity(new_valid, true);
m_channel_shuffle_finish = true;
}
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
m_primitive_covers_without_gaps = NoGapsType::FullCover;
m_channel_shuffle_abort = false;
m_conf.cb_ps.ChannelShuffleOffset = GSVector2(0, 0);
return true;
}
@@ -6425,7 +6439,10 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
{
// don't overwrite the texture when using channel shuffle, but keep the palette
if (!m_channel_shuffle)
{
m_conf.cb_ps.ChannelShuffleOffset = GSVector2(0, 0);
m_conf.tex = tex->m_texture;
}
m_conf.pal = tex->m_palette;
// Hazard handling (i.e. reading from the current RT/DS).
@@ -6817,7 +6834,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
const GSTextureCache::Target* src_target = nullptr;
if (!m_downscale_source || !tex->m_from_target)
{
if (rt && m_conf.tex == m_conf.rt && !(m_channel_shuffle && tex && (tex_diff != frame_diff || target_region)))
if (rt && m_conf.tex == m_conf.rt && !(m_channel_shuffle && tex && tex_diff != frame_diff))
{
// Can we read the framebuffer directly? (i.e. sample location matches up).
if (CanUseTexIsFB(rt, tex, tmm))
@@ -6900,37 +6917,63 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
GSVector4i::storel(&copy_dst_offset, copy_range);
if (m_channel_shuffle && (tex_diff || frame_diff))
{
const int page_offset = (m_cached_ctx.TEX0.TBP0 - src_target->m_TEX0.TBP0) >> 5;
const int horizontal_offset = ((page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x);
const int vertical_offset = ((page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y);
const u32 page_offset = (m_cached_ctx.TEX0.TBP0 - src_target->m_TEX0.TBP0) >> 5;
const u32 horizontal_offset = (page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x;
const u32 vertical_offset = (page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y;
copy_range.x += horizontal_offset;
copy_range.y += vertical_offset;
copy_range.z += horizontal_offset;
copy_range.w += vertical_offset;
if (!m_channel_shuffle)
if (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy)
{
copy_size.y -= vertical_offset;
copy_size.x -= horizontal_offset;
}
target_region = false;
source_region.bits = 0;
//copied_rt = tex->m_from_target != nullptr;
if (m_in_target_draw && (page_offset || frame_diff))
{
copy_range.z = copy_range.x + m_r.width();
copy_range.w = copy_range.y + m_r.height();
const u32 max_skip = ((m_channel_shuffle_finish || !m_channel_shuffle_width) ? 1 : m_channel_shuffle_width) << 5;
const bool new_shuffle = !(m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
m_last_channel_shuffle_fbp <= m_context->FRAME.Block() && (m_last_channel_shuffle_fbp + max_skip) >= m_context->FRAME.Block() &&
m_last_channel_shuffle_end_block > m_context->FRAME.Block() && m_last_channel_shuffle_tbp <= m_context->TEX0.TBP0 && (m_last_channel_shuffle_tbp + max_skip) >= m_context->TEX0.TBP0);
if (tex_diff != frame_diff)
if (rt == tex->m_from_target && new_shuffle)
{
GSVector4i::storel(&copy_dst_offset, m_r);
if (m_prim_overlap == PRIM_OVERLAP_NO)
m_conf.require_one_barrier = true;
else
m_conf.require_full_barrier = true;
}
}
copy_range.z = std::min(copy_range.z, src_target->m_unscaled_size.x);
copy_range.w = std::min(copy_range.w, src_target->m_unscaled_size.y);
m_conf.cb_ps.ChannelShuffleOffset = GSVector2((horizontal_offset - m_r.x) * tex->GetScale(), (vertical_offset - m_r.y) * tex->GetScale());
m_conf.ps.channel_fb = 1;
target_region = false;
source_region.bits = 0;
unscaled_size = src_target->GetUnscaledSize();
scale = src_target->GetScale();
return;
}
else
{
copy_range.x += horizontal_offset;
copy_range.y += vertical_offset;
copy_range.z += horizontal_offset;
copy_range.w += vertical_offset;
if (!m_channel_shuffle)
{
copy_size.y -= vertical_offset;
copy_size.x -= horizontal_offset;
}
target_region = false;
source_region.bits = 0;
//copied_rt = tex->m_from_target != nullptr;
if (m_in_target_draw && (page_offset || frame_diff))
{
copy_range.z = copy_range.x + m_r.width();
copy_range.w = copy_range.y + m_r.height();
if (tex_diff != frame_diff)
{
GSVector4i::storel(&copy_dst_offset, m_r);
}
}
copy_range.z = std::min(copy_range.z, src_target->m_unscaled_size.x);
copy_range.w = std::min(copy_range.w, src_target->m_unscaled_size.y);
}
}
}
else
@@ -9070,8 +9113,9 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
// Bottom of Texture (half height frame, will be the copy of Top texture after the draw)
// -----------------------------------------------------------------
const int tw = static_cast<int>(1 << m_cached_ctx.TEX0.TW);
const int th = static_cast<int>(1 << m_cached_ctx.TEX0.TH);
// Do not use tw and th, if it has been optimized to not be the TEX0 size, it will crash.
const int tw = tex->m_unscaled_size.x;
int th = tex->m_unscaled_size.y;
// Compute the Bottom of texture rectangle
pxAssert(m_cached_ctx.TEX0.TBP0 > m_cached_ctx.FRAME.Block());
@@ -9079,30 +9123,48 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
GSVector4i r_texture(r_draw);
r_texture.y -= offset;
r_texture.w -= offset;
const int new_height = std::max(r_texture.w, th);
if (GSTexture* rt = g_gs_device->CreateRenderTarget(tw, th, GSTexture::Format::Color))
GSTexture* temp_tex = g_gs_device->CreateTexture(tw, new_height, 1, tex->m_texture->GetFormat(), true);
if (temp_tex)
{
// sRect is the top of texture
// Need to half pixel offset the dest tex coordinates as draw pixels are top left instead of centre for texel reads.
const GSVector4 sRect(m_vt.m_min.t.x / tw, m_vt.m_min.t.y / th, m_vt.m_max.t.x / tw, m_vt.m_max.t.y / th);
const GSVector4 dRect = GSVector4(r_texture) + GSVector4(0.5f);
const GSVector4i r_full(0, 0, tw, th);
if (GSTexture* rt = g_gs_device->CreateRenderTarget(tw, new_height, GSTexture::Format::Color))
{
// sRect is the top of texture
// Need to half pixel offset the dest tex coordinates as draw pixels are top left instead of centre for texel reads.
const GSVector4 dRect = GSVector4(r_texture) + GSVector4(0.5f);
const GSVector4i r_full(0, 0, tw, th);
g_gs_device->CopyRect(tex->m_texture, rt, r_full, 0, 0);
g_gs_device->CopyRect(tex->m_texture, rt, r_full, 0, 0);
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect, ShaderConvert::COPY, m_vt.IsRealLinear());
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_texture_cache->ReplaceSourceTexture(tex, temp_tex, tex->m_scale, GSVector2i(tw, new_height), nullptr, false);
g_gs_device->CopyRect(rt, tex->m_texture, r_full, 0, 0);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
if (tex->m_region.HasY())
{
if (tex->m_region.GetMaxY() == th)
{
tex->m_region.bits &= ~(0xFFFF0000ULL << 32);
tex->m_region.SetY(tex->m_region.GetMinY(), new_height);
}
}
th = new_height;
const GSVector4 sRect(m_vt.m_min.t.x / tw, m_vt.m_min.t.y / th, m_vt.m_max.t.x / tw, m_vt.m_max.t.y / th);
const GSVector4i r_full_new(0, 0, tw, th);
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect, ShaderConvert::COPY, m_vt.IsRealLinear());
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->CopyRect(rt, tex->m_texture, r_full_new, 0, 0);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->Recycle(rt);
}
// Copy back the texture into the GS mem. I don't know why but it will be
// reuploaded again later
g_texture_cache->Read(tex, r_texture.rintersect(tex->m_texture->GetRect()));
g_gs_device->Recycle(rt);
}
// Copy back the texture into the GS mem. I don't know why but it will be
// reuploaded again later
g_texture_cache->Read(tex, r_texture.rintersect(tex->m_texture->GetRect()));
g_texture_cache->InvalidateVideoMemSubTarget(_rt);
return false; // skip current draw

View File

@@ -96,7 +96,7 @@ private:
void HandleProvokingVertexFirst();
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, GSTextureCache::Target* rt = nullptr);
u32 EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, const bool DATE, bool& DATE_PRIMID, bool& DATE_BARRIER, GSTextureCache::Target* rt,
bool can_scale_rt_alpha, bool& new_rt_alpha_scale);
void CleanupDraw(bool invalidate_temp_src);

View File

@@ -1059,12 +1059,13 @@ void GSTextureCache::DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVe
__ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA,
const GSLocalMemory::psm_t& psm_s, const u32* clut, const GSTexture* gpu_clut, const GSVector2i& compare_lod,
const GSTextureCache::SourceRegion& region, u32 fixed_tex0, FastList<GSTextureCache::Source*>& map)
const GSTextureCache::SourceRegion& region, u32 fixed_tex0, FastList<GSTextureCache::Source*>& map, const GIFRegCLAMP& CLAMP, GSVector4i read_area)
{
GSTextureCache::Source* best_s = nullptr;
for (auto i = map.begin(); i != map.end(); ++i)
{
GSTextureCache::Source* s = *i;
if (((TEX0.U32[0] ^ s->m_TEX0.U32[0]) | ((TEX0.U32[1] ^ s->m_TEX0.U32[1]) & 3)) != 0) // TBP0 TBW PSM TW TH
continue;
@@ -1094,8 +1095,23 @@ __ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, cons
// When fixed tex0 is used, we must find a matching region texture. The base likely
// doesn't contain to the correct region. Bit cheeky here, avoid a logical or by
// adding the invalid tex0 bit in.
if (((s->m_region.bits | fixed_tex0) != 0) && s->m_region.bits != region.bits)
continue;
if (((s->m_region.bits | fixed_tex0) != 0))
{
const bool is_clamped = CLAMP.WMS == CLAMP_CLAMP && CLAMP.WMT == CLAMP_CLAMP;
if (is_clamped && s->m_region.GetMinX() == 0 && s->m_region.GetMinY() == 0)
{
const GSVector4i read_region = GSVector4i(0, 0, read_area.z, read_area.w);
const GSVector4i region_area = GSVector4i(s->m_region.GetMinX(), s->m_region.GetMinY(), s->m_region.HasX() ? s->m_region.GetMaxX() : (1 << s->m_TEX0.TW), s->m_region.HasY() ? s->m_region.GetMaxY() : (1 << s->m_TEX0.TW));
if (!region_area.rintersect(read_region).eq(read_region))
continue;
}
else if (((s->m_region.bits | fixed_tex0) != 0) && s->m_region.bits != region.bits)
{
continue;
}
}
// Same base mip texture, but we need to check that MXL was the same as well.
// When mipmapping is off, this will be 0,0 vs 0,0.
@@ -1107,7 +1123,10 @@ __ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, cons
return s;
}
return nullptr;
if (best_s != nullptr)
return best_s;
else
return nullptr;
}
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const GIFRegFRAME& frame, bool req_color, bool req_alpha, bool palette)
@@ -1127,7 +1146,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const bool is_depth, c
const u32* const clut = g_gs_renderer->m_mem.m_clut;
GSTexture* const gpu_clut = (psm_s.pal > 0) ? g_gs_renderer->m_mem.m_clut.GetGPUTexture() : nullptr;
Source* src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, GSVector2i(0, 0), region,
region.IsFixedTEX0(TEX0), m_src.m_map[TEX0.TBP0 >> 5]);
region.IsFixedTEX0(TEX0), m_src.m_map[TEX0.TBP0 >> 5], CLAMP, r);
if (src)
{
GL_CACHE("TC: src hit: (0x%x, %s)", TEX0.TBP0, GSUtil::GetPSMName(TEX0.PSM));
@@ -1332,10 +1351,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
const GSOffset offset(psm_s.info, TEX0.TBP0, TEX0.TBW, TEX0.PSM);
const u32 region_page = offset.bn(region.GetMinX(), region.GetMinY()) >> 5;
if (lookup_page != region_page)
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, is_fixed_tex0, m_src.m_map[region_page]);
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, is_fixed_tex0, m_src.m_map[region_page], CLAMP, r);
}
if (!src)
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, is_fixed_tex0, m_src.m_map[lookup_page]);
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, is_fixed_tex0, m_src.m_map[lookup_page], CLAMP, r);
if (src && src->m_from_target && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::MergeTargets && GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, r) > src->m_from_target->m_end_block)
{
@@ -1703,7 +1722,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
if (found_t && (bw != t->m_TEX0.TBW || t->m_TEX0.PSM != psm))
match = false;
//if (!t_clean && can_convert)
// Different swizzle, different width, and dirty, so probably not what we want.
// DevCon.Warning("Expected %x Got %x shuffle %d draw %d", psm, t_psm, possible_shuffle, GSState::s_n);
if (match)
{
@@ -2186,7 +2205,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
}
}
src = CreateSource(src_TEX0, TEXA, dst, x_offset, y_offset, lod, &rect, gpu_clut, region, target_bp_hit_outside_valid_area && TEX0.PSM != PSMT8);
src = CreateSource(src_TEX0, TEXA, CLAMP, dst, x_offset, y_offset, lod, &rect, gpu_clut, region, target_bp_hit_outside_valid_area && TEX0.PSM != PSMT8);
if (!src) [[unlikely]]
return nullptr;
}
@@ -2294,10 +2313,10 @@ void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache:
continue;
}
// Formats match
if (t->m_TEX0.TBW == target->m_TEX0.TBW && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[target->m_TEX0.PSM].bpp)
if ((t->m_TEX0.TBW == target->m_TEX0.TBW || t->m_TEX0.TBW == 1) && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[target->m_TEX0.PSM].bpp)
{
const GSLocalMemory::psm_t& t_psm = GSLocalMemory::m_psm[t->m_TEX0.PSM];
const u32 page_offset = ((t->m_TEX0.TBP0 - target->m_TEX0.TBP0) >> 5) % std::max(1U, t->m_TEX0.TBW);
const GSLocalMemory::psm_t& t_psm = GSLocalMemory::m_psm[target->m_TEX0.PSM];
const u32 page_offset = ((t->m_TEX0.TBP0 - target->m_TEX0.TBP0) >> 5) % std::max(1U, target->m_TEX0.TBW);
const u32 page_width = (t->m_valid.z + (t_psm.pgs.x - 1)) / t_psm.pgs.x;
if ((page_offset + page_width) <= target->m_TEX0.TBW)
@@ -2308,7 +2327,7 @@ void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache:
{
t->Update();
const u32 vertical_offset = (((t->m_TEX0.TBP0 - target->m_TEX0.TBP0) >> 5) / std::max(1U, t->m_TEX0.TBW)) * t_psm.pgs.y;
const u32 vertical_offset = (((t->m_TEX0.TBP0 - target->m_TEX0.TBP0) >> 5) / std::max(1U, target->m_TEX0.TBW)) * t_psm.pgs.y;
const u32 horizontal_offset = page_offset * t_psm.pgs.x;
const GSVector4i target_drect_unscaled = t->m_drawn_since_read + GSVector4i(horizontal_offset, vertical_offset).xyxy();
@@ -2748,7 +2767,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
dst->m_TEX0.TBP0, dst->m_TEX0.TBW, GSUtil::GetPSMName(dst->m_TEX0.PSM));
}
if (dst->m_scale != scale && (!preserve_scale || is_shuffle || !dst->m_downscaled || TEX0.TBW != dst->m_TEX0.TBW))
if (dst->m_scale != scale && (!preserve_scale || (type == RenderTarget && (is_shuffle || !dst->m_downscaled || TEX0.TBW != dst->m_TEX0.TBW))))
{
calcRescale(dst);
GSTexture* tex = type == RenderTarget ? g_gs_device->CreateRenderTarget(new_scaled_size.x, new_scaled_size.y, GSTexture::Format::Color, clear) :
@@ -2756,7 +2775,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
if (!tex)
return nullptr;
if (scale == 1.0f)
if (scale == 1.0f && type == RenderTarget)
{
// When using native HPO, the top-left column/row of pixels are often not drawn. Clamp these away to avoid sampling black,
// causing bleeding into the edges of the downsampled texture.
@@ -2770,7 +2789,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
g_gs_device->FilteredDownsampleTexture(dst->m_texture, tex, downsample_factor, clamp_min, dRect);
}
else
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, dst->m_scale < scale);
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, type == RenderTarget && !preserve_scale);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
@@ -3757,23 +3776,54 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && t->Inside(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid) &&
GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp)
{
dst->m_TEX0.TBP0 = t->m_TEX0.TBP0;
dst->m_valid = t->m_valid;
dst->m_drawn_since_read = t->m_drawn_since_read;
dst->m_end_block = t->m_end_block;
dst->m_valid_rgb = true;
t->m_valid_rgb = false;
t->m_was_dst_matched = true;
// if this part is dirty, then let's break it in two.
GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
if (GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect) >= dst->m_TEX0.TBP0)
{
t->m_valid.w = dirty_rect.y;
dst->ResizeTexture(t->m_unscaled_size.x, t->m_unscaled_size.y);
if (t->m_valid.rempty())
{
if (src && src->m_target && src->m_from_target == t)
{
src->m_from_target = nullptr;
src->m_texture = t->m_texture;
src->m_target_direct = false;
src->m_shared_texture = false;
const ShaderConvert shader = (GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 16) ? ShaderConvert::RGB5A1_TO_FLOAT16 :
(GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 32) ? ShaderConvert::RGBA8_TO_FLOAT32 :
ShaderConvert::RGBA8_TO_FLOAT24;
t->m_texture = nullptr;
i = list.erase(j);
delete t;
}
else
{
InvalidateSourcesFromTarget(t);
i = list.erase(j);
delete t;
}
}
else
t->UpdateValidity(t->m_valid, true);
}
else
{
dst->m_TEX0.TBP0 = t->m_TEX0.TBP0;
dst->m_valid = t->m_valid;
dst->m_drawn_since_read = t->m_drawn_since_read;
dst->m_end_block = t->m_end_block;
dst->m_valid_rgb = true;
t->m_valid_rgb = false;
t->m_was_dst_matched = true;
g_gs_device->StretchRect(t->m_texture, GSVector4(0, 0, 1, 1),
dst->m_texture, GSVector4(t->GetUnscaledRect()) * GSVector4(dst->GetScale()), shader, false);
dst->ResizeTexture(t->m_unscaled_size.x, t->m_unscaled_size.y);
const ShaderConvert shader = (GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 16) ? ShaderConvert::RGB5A1_TO_FLOAT16 :
(GSLocalMemory::m_psm[dst->m_TEX0.PSM].trbpp == 32) ? ShaderConvert::RGBA8_TO_FLOAT32 :
ShaderConvert::RGBA8_TO_FLOAT24;
g_gs_device->StretchRect(t->m_texture, GSVector4(0, 0, 1, 1),
dst->m_texture, GSVector4(t->GetUnscaledRect()) * GSVector4(dst->GetScale()), shader, false);
}
break;
}
else
@@ -3796,34 +3846,51 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
}
const int height_adjust = ((((dst_end_block + 31) - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
bool delete_target = true;
if (height_adjust < t->m_unscaled_size.y)
{
t->m_TEX0.TBP0 = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, GSVector4i(0, height_adjust, t->m_valid.z, t->m_valid.w));
u32 new_base_tbp = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, GSVector4i(0, height_adjust, t->m_valid.z, t->m_valid.w));
if (t->m_dirty.size() > 0)
{
u32 dirty_end = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size));
if (new_base_tbp >= dirty_end)
t->m_dirty.clear();
else
t->Update(true);
}
t->m_TEX0.TBP0 = new_base_tbp;
t->m_valid.w -= height_adjust;
t->ResizeValidity(t->m_valid);
GSTexture* tex = (t->m_type == RenderTarget) ?
g_gs_device->CreateRenderTarget(t->m_texture->GetWidth(), t->m_texture->GetHeight(), GSTexture::Format::Color, true) :
g_gs_device->CreateDepthStencil(t->m_texture->GetWidth(), t->m_texture->GetHeight(), GSTexture::Format::DepthStencil, true);
if (tex)
if (!t->m_valid.rempty())
{
g_gs_device->CopyRect(t->m_texture, tex, GSVector4i(0, height_adjust * t->m_scale, t->m_texture->GetWidth(), t->m_texture->GetHeight()), 0, 0);
if (src && src->m_target && src->m_from_target == t)
delete_target = false;
GSTexture* tex = (t->m_type == RenderTarget) ?
g_gs_device->CreateRenderTarget(t->m_texture->GetWidth(), t->m_texture->GetHeight(), GSTexture::Format::Color, true) :
g_gs_device->CreateDepthStencil(t->m_texture->GetWidth(), t->m_texture->GetHeight(), GSTexture::Format::DepthStencil, true);
if (tex)
{
src->m_from_target = t;
src->m_texture = t->m_texture;
src->m_target_direct = false;
src->m_shared_texture = false;
g_gs_device->CopyRect(t->m_texture, tex, GSVector4i(0, height_adjust * t->m_scale, t->m_texture->GetWidth(), t->m_texture->GetHeight()), 0, 0);
if (src && src->m_target && src->m_from_target == t)
{
src->m_from_target = t;
src->m_texture = t->m_texture;
src->m_target_direct = false;
src->m_shared_texture = false;
}
else
{
g_gs_device->Recycle(t->m_texture);
}
t->m_texture = tex;
}
else
{
g_gs_device->Recycle(t->m_texture);
}
t->m_texture = tex;
}
}
else
if (delete_target)
{
if (src && src->m_target && src->m_from_target == t)
{
@@ -4305,7 +4372,7 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
const u32 end_width = write_bw * 64;
const u32 end_height = ((end_page_offset / std::max(write_bw, 1U)) * GSLocalMemory::m_psm[write_psm].pgs.y) + GSLocalMemory::m_psm[write_psm].pgs.y;
const GSVector4i r = GSVector4i(0, 0, end_width, end_height);
const GSVector4i invalidate_r = TranslateAlignedRectByPage(t, start_bp, write_psm, write_bw, r, false).rintersect(t->m_valid); // it is invalidation but we need a real rect.
const GSVector4i invalidate_r = TranslateAlignedRectByPage(t, start_bp, write_psm, write_bw, r, true).rintersect(t->m_valid); // it is invalidation but we need a real rect.
if (offset == 0 || dirty_rect.rempty() || !dirty_rect.rintersect(invalidate_r).rempty())
{
@@ -4314,7 +4381,10 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
RGBAMask mask;
mask._u32 = GSUtil::GetChannelMask(write_psm);
AddDirtyRectTarget(t, invalidate_r, t->m_TEX0.PSM, t->m_TEX0.TBW, mask, false);
if (write_bw != t->m_TEX0.TBW)
DirtyRectByPage(start_bp, write_psm, write_bw, t, r);
else
AddDirtyRectTarget(t, invalidate_r, t->m_TEX0.PSM, t->m_TEX0.TBW, mask, false);
}
++i;
@@ -4838,7 +4908,10 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
{
if (swizzle_match)
{
targetr = TranslateAlignedRectByPage(t, bp, psm, bw, r, true);
if (exact_bp && GSUtil::HasSameSwizzleBits(psm, t->m_TEX0.PSM) && bw == t->m_TEX0.TBW)
targetr = r;
else
targetr = TranslateAlignedRectByPage(t, bp, psm, bw, r, true);
}
else
{
@@ -5733,10 +5806,7 @@ void GSTextureCache::ReplaceSourceTexture(Source* s, GSTexture* new_texture, flo
if (s->m_from_hash_cache)
s->m_from_hash_cache->refcount++;
else if (!s->m_shared_texture)
{
DevCon.Warning("replace %d", m_source_memory_usage);
m_source_memory_usage += s->m_texture->GetMemUsage();
}
}
void GSTextureCache::IncAge()
@@ -5816,7 +5886,7 @@ void GSTextureCache::IncAge()
}
//Fixme: Several issues in here. Not handling depth stencil, pitch conversion doesnt work.
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* dst, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temp)
GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, Target* dst, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temp)
{
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
Source* src = new Source(TEX0, TEXA);
@@ -6296,6 +6366,41 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
m_temporary_source = src;
}
// We can check for a recent upload to see if we can find out out what the actual size is.
if (!region.HasEither() && m_temporary_source == nullptr && CLAMP.WMS == CLAMP_CLAMP && CLAMP.WMT == CLAMP_CLAMP)
{
if (src_range->z < tw && src_range->w < th)
{
auto& transfers = GSRendererHW::GetInstance()->m_draw_transfers;
if (transfers.size() > 0)
{
const int last_draw = transfers.back().draw;
for (auto iter = transfers.rbegin(); iter != transfers.rend(); ++iter)
{
if (last_draw - iter->draw > 10)
break;
// If the format, and location doesn't overlap
if (iter->blit.DBP == TEX0.TBP0 && iter->blit.DBW == TEX0.TBW && iter->blit.DPSM == TEX0.PSM)
{
const int new_max_x = std::min(tw, std::max(iter->rect.z, Common::AlignUpPow2(src_range->z, psm.pgs.x)));
const int new_max_y = std::min(th, std::max(iter->rect.w, Common::AlignUpPow2(src_range->w, psm.pgs.y)));
if (new_max_x != tw && new_max_y != th)
{
region.SetX(0, new_max_x);
region.SetY(0, new_max_y);
}
break;
}
}
}
}
}
// maintain the clut even when paltex is on for the dump/replacement texture lookup
bool paltex = (GSConfig.GPUPaletteConversion && psm.pal > 0) || gpu_clut;
const u32* clut = (psm.pal > 0) ? static_cast<const u32*>(g_gs_renderer->m_mem.m_clut) : nullptr;
@@ -7923,7 +8028,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
// 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_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, false);
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, true);
}
else
{
@@ -8650,6 +8755,7 @@ GSTextureCache::SourceRegion GSTextureCache::SourceRegion::Create(GIFRegTEX0 TEX
GL_CACHE("TC: Region repeat optimization: %d width -> %d", 1 << TEX0.TW, region.GetWidth());
}
}
if (CLAMP.WMT == CLAMP_REGION_CLAMP && CLAMP.MAXV >= CLAMP.MINV)
{
const u32 rh = CLAMP.MAXV - CLAMP.MINV + 1;

View File

@@ -268,7 +268,7 @@ public:
void UpdateValidChannels(u32 psm, u32 fbmsk);
/// Resizes target texture, DOES NOT RESCALE.
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true, bool require_offset = false, GSVector4i offset = GSVector4i::zero(), bool keep_old = false);
bool ResizeTexture(int new_unscaled_width, int new_unscaled_height, bool recycle_old = true, bool require_new_rect = false, GSVector4i new_rect = GSVector4i::zero(), bool keep_old = false);
private:
void UpdateTextureDebugName();
@@ -445,7 +445,7 @@ protected:
std::unique_ptr<GSDownloadTexture> m_uint16_download_texture;
std::unique_ptr<GSDownloadTexture> m_uint32_download_texture;
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false);
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, Target* t, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region, bool force_temporary = false);
bool PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);

View File

@@ -2081,6 +2081,7 @@ static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, HalfTexel) == of
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, MinMax) == offsetof(GSMTLMainPSUniform, uv_min_max));
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, STRange) == offsetof(GSMTLMainPSUniform, st_range));
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, ChannelShuffle) == offsetof(GSMTLMainPSUniform, channel_shuffle));
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, ChannelShuffleOffset) == offsetof(GSMTLMainPSUniform, channel_shuffle_offset));
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, TCOffsetHack) == offsetof(GSMTLMainPSUniform, tc_offset));
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, STScale) == offsetof(GSMTLMainPSUniform, st_scale));
static_assert(offsetof(GSHWDrawConfig::PSConstantBuffer, DitherMatrix) == offsetof(GSMTLMainPSUniform, dither_matrix));

View File

@@ -129,6 +129,7 @@ struct GSMTLMainPSUniform
unsigned int green_mask;
unsigned int green_shift;
} channel_shuffle;
vector_float2 channel_shuffle_offset;
vector_float2 tc_offset;
vector_float2 st_scale;
matrix_float4x4 dither_matrix;

View File

@@ -509,7 +509,7 @@ struct PSMain
uint fetch_raw_depth()
{
return tex_depth.read(ushort2(in.p.xy)) * 0x1p32f;
return tex_depth.read(ushort2(in.p.xy + cb.channel_shuffle_offset)) * 0x1p32f;
}
float4 fetch_raw_color()
@@ -517,7 +517,7 @@ struct PSMain
if (PS_TEX_IS_FB)
return current_color;
else
return tex.read(ushort2(in.p.xy));
return tex.read(ushort2(in.p.xy + cb.channel_shuffle_offset));
}
float4 fetch_c(ushort2 uv)
@@ -790,7 +790,7 @@ struct PSMain
void fog(thread float4& C, float f)
{
if (PS_FOG)
C.rgb = trunc(mix(cb.fog_color, C.rgb, f));
C.rgb = trunc(mix(cb.fog_color, C.rgb, (f * 255.0f) / 256.0f));
}
float4 ps_color()

View File

@@ -218,7 +218,7 @@ void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
// PCSX2 Controller Settings - Hotkeys
// PCSX2 Controller Settings - Hotkeys - General
si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/LAlt & Keyboard/Return");
si.SetStringValue("Hotkeys", "ToggleFullscreen", "Keyboard/Alt & Keyboard/Return");
// PCSX2 Controller Settings - Hotkeys - Graphics
si.SetStringValue("Hotkeys", "CycleAspectRatio", "Keyboard/F6");
@@ -228,24 +228,24 @@ void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
// si.SetStringValue("Hotkeys", "DecreaseUpscaleMultiplier", "Keyboard"); TBD
// si.SetStringValue("Hotkeys", "IncreaseUpscaleMultiplier", "Keyboard"); TBD
// si.SetStringValue("Hotkeys", "ReloadTextureReplacements", "Keyboard"); TBD
si.SetStringValue("Hotkeys", "GSDumpMultiFrame", "Keyboard/RCtrl & Keyboard/RShift & Keyboard/F8");
si.SetStringValue("Hotkeys", "GSDumpMultiFrame", "Keyboard/Control & Keyboard/Shift & Keyboard/F8");
si.SetStringValue("Hotkeys", "Screenshot", "Keyboard/F8");
si.SetStringValue("Hotkeys", "GSDumpSingleFrame", "Keyboard/RShift & Keyboard/F8");
si.SetStringValue("Hotkeys", "GSDumpSingleFrame", "Keyboard/Shift & Keyboard/F8");
si.SetStringValue("Hotkeys", "ToggleSoftwareRendering", "Keyboard/F9");
// si.SetStringValue("Hotkeys", "ToggleTextureDumping", "Keyboard"); TBD
// si.SetStringValue("Hotkeys", "ToggleTextureReplacements", "Keyboard"); TBD
si.SetStringValue("Hotkeys", "ZoomIn", "Keyboard/LCtrl & Keyboard/Plus");
si.SetStringValue("Hotkeys", "ZoomOut", "Keyboard/LCtrl & Keyboard/Minus");
// Missing hotkey for resetting zoom back to 100 with Keyboard/LCtrl & Keyboard/Asterisk
si.SetStringValue("Hotkeys", "ZoomIn", "Keyboard/Control & Keyboard/Plus");
si.SetStringValue("Hotkeys", "ZoomOut", "Keyboard/Control & Keyboard/Minus");
// Missing hotkey for resetting zoom back to 100 with Keyboard/Control & Keyboard/Asterisk
// PCSX2 Controller Settings - Hotkeys - Input Recording
si.SetStringValue("Hotkeys", "InputRecToggleMode", "Keyboard/LShift & Keyboard/R");
si.SetStringValue("Hotkeys", "InputRecToggleMode", "Keyboard/Shift & Keyboard/R");
// PCSX2 Controller Settings - Hotkeys - Save States
si.SetStringValue("Hotkeys", "LoadStateFromSlot", "Keyboard/F3");
si.SetStringValue("Hotkeys", "SaveStateToSlot", "Keyboard/F1");
si.SetStringValue("Hotkeys", "NextSaveStateSlot", "Keyboard/F2");
si.SetStringValue("Hotkeys", "PreviousSaveStateSlot", "Keyboard/LShift & Keyboard/F2");
si.SetStringValue("Hotkeys", "PreviousSaveStateSlot", "Keyboard/Shift & Keyboard/F2");
// PCSX2 Controller Settings - Hotkeys - System
// si.SetStringValue("Hotkeys", "DecreaseSpeed", "Keyboard"); TBD
@@ -256,7 +256,7 @@ void Pad::SetDefaultHotkeyConfig(SettingsInterface& si)
si.SetStringValue("Hotkeys", "OpenPauseMenu", "Keyboard/Escape");
si.SetStringValue("Hotkeys", "ToggleFrameLimit", "Keyboard/F4");
si.SetStringValue("Hotkeys", "TogglePause", "Keyboard/Space");
si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/LShift & Keyboard/Backtab");
si.SetStringValue("Hotkeys", "ToggleSlowMotion", "Keyboard/Shift & Keyboard/Backtab");
si.SetStringValue("Hotkeys", "ToggleTurbo", "Keyboard/Tab");
si.SetStringValue("Hotkeys", "HoldTurbo", "Keyboard/Period");
}

View File

@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 78;
static constexpr u32 SHADER_CACHE_VERSION = 80;