mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-10-12 02:09:17 +00:00
rsx: Implement workaround for pitch compatibility of overlapping sections with blit engine targets
- Avoid generating surfaces that introduce pitch conflict!
This commit is contained in:
@@ -112,11 +112,21 @@ namespace rsx
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool is_pitch_compatible = Traits::surface_is_pitch_compatible(found->second, prev_surface->get_rsx_pitch());
|
||||
if (!is_pitch_compatible && found->second->last_use_tag >= prev_surface->last_use_tag) [[unlikely]]
|
||||
{
|
||||
// HACK: A newer surface with an incompatible pitch owns the memory, do not evict.
|
||||
// TODO: Pitch conversion is required to resolve this properly.
|
||||
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.",
|
||||
new_address, prev_surface->base_addr, prev_surface->get_rsx_pitch(), found->second->get_rsx_pitch());
|
||||
return;
|
||||
}
|
||||
|
||||
invalidate(found->second);
|
||||
data.erase(new_address);
|
||||
|
||||
auto &old = invalidated_resources.back();
|
||||
if (Traits::surface_is_pitch_compatible(old, prev_surface->get_rsx_pitch()))
|
||||
if (is_pitch_compatible)
|
||||
{
|
||||
if (old->last_use_tag >= prev_surface->last_use_tag) [[unlikely]]
|
||||
{
|
||||
@@ -1128,6 +1138,69 @@ namespace rsx
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Workaround to handle overlapping surfaces with differing pitch
|
||||
// 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.
|
||||
// TODO: Re-evaluate usefulness once pitch-conversion work is completed.
|
||||
u32 truncate_memory_range_by_pitch(u32 address, u32 pitch, u32 length, u64 reference_tag)
|
||||
{
|
||||
if (!length || !pitch)
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
const auto test_range = rsx::address_range32::start_length(address, length);
|
||||
const auto test_height = static_cast<u16>(utils::aligned_div(length, pitch));
|
||||
u32 limit = address + length;
|
||||
|
||||
auto process_list_function = [&](surface_ranged_map& data)
|
||||
{
|
||||
for (auto it = data.begin_range(test_range); it != data.end(); ++it)
|
||||
{
|
||||
// Only a surface that begins after us can shorten the length. An exact address match is (hackishly) handled in bind_surface_address instead.
|
||||
const auto base_address = it->first;
|
||||
if (base_address <= address || base_address >= limit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto surface = Traits::get(it->second);
|
||||
if (!surface->get_memory_range().overlaps(test_range))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rsx::pitch_compatible(surface, pitch, test_height))
|
||||
{
|
||||
// Normal inheritance resolves this overlap. Nothing to do.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (surface->last_use_tag <= reference_tag)
|
||||
{
|
||||
// Stale data, safe to swallow.
|
||||
continue;
|
||||
}
|
||||
|
||||
// min() is already handled in the original address bounds check
|
||||
limit = base_address;
|
||||
}
|
||||
};
|
||||
|
||||
if (m_render_targets_memory_range.valid() &&
|
||||
test_range.overlaps(m_render_targets_memory_range))
|
||||
{
|
||||
process_list_function(m_render_targets_storage);
|
||||
}
|
||||
|
||||
if (m_depth_stencil_memory_range.valid() &&
|
||||
test_range.overlaps(m_depth_stencil_memory_range))
|
||||
{
|
||||
process_list_function(m_depth_stencil_storage);
|
||||
}
|
||||
|
||||
return limit - address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates surface that exists at an address
|
||||
*/
|
||||
|
||||
@@ -1702,6 +1702,24 @@ namespace rsx
|
||||
{
|
||||
block_end = write_end;
|
||||
}
|
||||
else if (!use_null_region && block_end > write_end)
|
||||
{
|
||||
// Before attempting to create any surface that would exceed our bounds, ensure we don't end up in a pitch conflict!
|
||||
// TODO: Remove this when pitch conversion is implemented or at least revise usefulness. It may still be beneficial to some extent.
|
||||
const auto prev_surface = m_rtts.get_surface_at(dst_base_address);
|
||||
const u64 reference_tag = prev_surface ? prev_surface->last_use_tag : 0ull;
|
||||
|
||||
const u32 block_length = block_end - dst_base_address;
|
||||
const u32 safe_length = m_rtts.truncate_memory_range_by_pitch(dst_base_address, dst.pitch, block_length, reference_tag);
|
||||
|
||||
if (safe_length < block_length) [[ unlikely ]]
|
||||
{
|
||||
// We have pitch conflicts. Play it safe.
|
||||
const auto aligned_safe_length = (safe_length / dst.pitch) * dst.pitch; // <- Align safe_length down to a multiple of pitch.
|
||||
const auto proposed_end = aligned_safe_length + dst_base_address;
|
||||
block_end = std::max(proposed_end, write_end);
|
||||
}
|
||||
}
|
||||
|
||||
const u32 usable_section_length = std::max(write_end, block_end) - dst_base_address;
|
||||
dst_dimensions.height = align2(usable_section_length, dst.pitch) / dst.pitch;
|
||||
|
||||
Reference in New Issue
Block a user