Skip to content

Commit 48d2b24

Browse files
committed
rsx: Implement workaround for pitch compatibility of overlapping sections with blit engine targets
- Avoid generating surfaces that introduce pitch conflict!
1 parent 42cfad2 commit 48d2b24

2 files changed

Lines changed: 92 additions & 1 deletion

File tree

rpcs3/Emu/RSX/Common/surface_store.h

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,21 @@ namespace rsx
112112
}
113113
else
114114
{
115+
const bool is_pitch_compatible = Traits::surface_is_pitch_compatible(found->second, prev_surface->get_rsx_pitch());
116+
if (!is_pitch_compatible && found->second->last_use_tag >= prev_surface->last_use_tag) [[unlikely]]
117+
{
118+
// HACK: A newer surface with an incompatible pitch owns the memory, do not evict.
119+
// TODO: Pitch conversion is required to resolve this properly.
120+
rsx_log.warning("[SURFACE CACHE] Discarding block at 0x%x from surface at 0x%x (pitch=%u); block is owned by a newer surface with pitch=%u.",
121+
new_address, prev_surface->base_addr, prev_surface->get_rsx_pitch(), found->second->get_rsx_pitch());
122+
return;
123+
}
124+
115125
invalidate(found->second);
116126
data.erase(new_address);
117127

118128
auto &old = invalidated_resources.back();
119-
if (Traits::surface_is_pitch_compatible(old, prev_surface->get_rsx_pitch()))
129+
if (is_pitch_compatible)
120130
{
121131
if (old->last_use_tag >= prev_surface->last_use_tag) [[unlikely]]
122132
{
@@ -1128,6 +1138,69 @@ namespace rsx
11281138
return nullptr;
11291139
}
11301140

1141+
// Workaround to handle overlapping surfaces with differing pitch
1142+
// For a surface region defined by range [address, length] and pitch, we return the max length we can write without clobbering a surface of different pitch.
1143+
// TODO: Re-evaluate usefulness once pitch-conversion work is completed.
1144+
u32 truncate_memory_range_by_pitch(u32 address, u32 pitch, u32 length, u64 reference_tag)
1145+
{
1146+
if (!length || !pitch)
1147+
{
1148+
return length;
1149+
}
1150+
1151+
const auto test_range = rsx::address_range32::start_length(address, length);
1152+
const auto test_height = static_cast<u16>(utils::aligned_div(length, pitch));
1153+
u32 limit = address + length;
1154+
1155+
auto process_list_function = [&](surface_ranged_map& data)
1156+
{
1157+
for (auto it = data.begin_range(test_range); it != data.end(); ++it)
1158+
{
1159+
// Only a surface that begins after us can shorten the length. An exact address match is (hackishly) handled in bind_surface_address instead.
1160+
const auto base_address = it->first;
1161+
if (base_address <= address || base_address >= limit)
1162+
{
1163+
continue;
1164+
}
1165+
1166+
const auto surface = Traits::get(it->second);
1167+
if (!surface->get_memory_range().overlaps(test_range))
1168+
{
1169+
continue;
1170+
}
1171+
1172+
if (rsx::pitch_compatible(surface, pitch, test_height))
1173+
{
1174+
// Normal inheritance resolves this overlap. Nothing to do.
1175+
continue;
1176+
}
1177+
1178+
if (surface->last_use_tag <= reference_tag)
1179+
{
1180+
// Stale data, safe to swallow.
1181+
continue;
1182+
}
1183+
1184+
// min() is already handled in the original address bounds check
1185+
limit = base_address;
1186+
}
1187+
};
1188+
1189+
if (m_render_targets_memory_range.valid() &&
1190+
test_range.overlaps(m_render_targets_memory_range))
1191+
{
1192+
process_list_function(m_render_targets_storage);
1193+
}
1194+
1195+
if (m_depth_stencil_memory_range.valid() &&
1196+
test_range.overlaps(m_depth_stencil_memory_range))
1197+
{
1198+
process_list_function(m_depth_stencil_storage);
1199+
}
1200+
1201+
return limit - address;
1202+
}
1203+
11311204
/**
11321205
* Invalidates surface that exists at an address
11331206
*/

rpcs3/Emu/RSX/Common/texture_cache.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,24 @@ namespace rsx
17021702
{
17031703
block_end = write_end;
17041704
}
1705+
else if (!use_null_region && block_end > write_end)
1706+
{
1707+
// Before attempting to create any surface that would exceed our bounds, ensure we don't end up in a pitch conflict!
1708+
// TODO: Remove this when pitch conversion is implemented or at least revise usefulness. It may still be beneficial to some extent.
1709+
const auto prev_surface = m_rtts.get_surface_at(dst_base_address);
1710+
const u64 reference_tag = prev_surface ? prev_surface->last_use_tag : 0ull;
1711+
1712+
const u32 block_length = block_end - dst_base_address;
1713+
const u32 safe_length = m_rtts.truncate_memory_range_by_pitch(dst_base_address, dst.pitch, block_length, reference_tag);
1714+
1715+
if (safe_length < block_length) [[ unlikely ]]
1716+
{
1717+
// We have pitch conflicts. Play it safe.
1718+
const auto aligned_safe_length = (safe_length / dst.pitch) * dst.pitch; // <- Align safe_length down to a multiple of pitch.
1719+
const auto proposed_end = aligned_safe_length + dst_base_address;
1720+
block_end = std::max(proposed_end, write_end);
1721+
}
1722+
}
17051723

17061724
const u32 usable_section_length = std::max(write_end, block_end) - dst_base_address;
17071725
dst_dimensions.height = align2(usable_section_length, dst.pitch) / dst.pitch;

0 commit comments

Comments
 (0)