diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index 6cd77a303e..d43875d203 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -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(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 */ diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index ea9b542546..fc0824c770 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -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;