diff --git a/rpcs3/Emu/RSX/Common/texture_cache.h b/rpcs3/Emu/RSX/Common/texture_cache.h index 757f72c92b..9417e4d862 100644 --- a/rpcs3/Emu/RSX/Common/texture_cache.h +++ b/rpcs3/Emu/RSX/Common/texture_cache.h @@ -183,6 +183,15 @@ namespace rsx constexpr u32 get_block_size() const { return 0x1000000; } inline u32 get_block_address(u32 address) const { return (address & ~0xFFFFFF); } + public: + //Struct to hold data on sections to be paged back onto cpu memory + struct thrashed_set + { + bool violation_handled = false; + std::vector affected_sections; //Always laid out with flushable sections first then other affected sections last + int num_flushable = 0; + }; + private: //Internal implementation methods and helpers @@ -261,12 +270,8 @@ namespace rsx } //Invalidate range base implementation - //Returns a pair: - //1. A boolean - true if the memory range was truly locked and has been dealt with, false otherwise - //2. A vector of all sections that should be flushed if the caller did not set the allow_flush method. That way the caller can make preparations on how to deal with sections that require flushing - // Note that the sections will be unlocked regardless of the allow_flush flag template - std::pair> invalidate_range_impl_base(u32 address, u32 range, bool is_writing, bool discard_only, bool rebuild_cache, bool allow_flush, Args&&... extras) + thrashed_set invalidate_range_impl_base(u32 address, u32 range, bool is_writing, bool discard_only, bool rebuild_cache, bool allow_flush, Args&&... extras) { auto trampled_set = get_intersecting_set(address, range, allow_flush); @@ -310,6 +315,9 @@ namespace rsx obj.second->remove_one(); } + thrashed_set result = {}; + result.violation_handled = true; + if (allow_flush) { // Flush here before 'reprotecting' since flushing will write the whole span @@ -323,6 +331,11 @@ namespace rsx } } } + else if (sections_to_flush.size() > 0) + { + result.num_flushable = static_cast(sections_to_flush.size()); + result.affected_sections = std::move(sections_to_flush); + } for (auto It = to_reprotect; It != trampled_set.end(); It++) { @@ -332,19 +345,22 @@ namespace rsx obj.first->discard(); obj.first->protect(old_prot); obj.first->set_dirty(false); + + if (result.affected_sections.size() > 0) + { + //Append to affected set. Not counted as part of num_flushable + result.affected_sections.push_back(obj.first); + } } - if (discard_only || allow_flush) - return{ true, {} }; - - return std::make_pair(true, sections_to_flush); + return result; } - return{ false, {} }; + return {}; } template - std::pair> invalidate_range_impl(u32 address, u32 range, bool is_writing, bool discard, bool allow_flush, Args&&... extras) + thrashed_set invalidate_range_impl(u32 address, u32 range, bool is_writing, bool discard, bool allow_flush, Args&&... extras) { return invalidate_range_impl_base(address, range, is_writing, discard, true, allow_flush, std::forward(extras)...); } @@ -585,13 +601,13 @@ namespace rsx } template - std::pair> invalidate_address(u32 address, bool is_writing, bool allow_flush, Args&&... extras) + thrashed_set invalidate_address(u32 address, bool is_writing, bool allow_flush, Args&&... extras) { return invalidate_range(address, 4096 - (address & 4095), is_writing, false, allow_flush, std::forward(extras)...); } template - std::pair> invalidate_range(u32 address, u32 range, bool is_writing, bool discard, bool allow_flush, Args&&... extras) + thrashed_set invalidate_range(u32 address, u32 range, bool is_writing, bool discard, bool allow_flush, Args&&... extras) { std::pair trampled_range = std::make_pair(address, address + range); @@ -601,7 +617,7 @@ namespace rsx //Doesnt fall in the read_only textures range; check render targets if (trampled_range.second < no_access_range.first || trampled_range.first > no_access_range.second) - return{ false, {} }; + return {}; } writer_lock lock(m_cache_mutex); @@ -609,16 +625,32 @@ namespace rsx } template - bool flush_all(std::vector& sections_to_flush, Args&&... extras) + bool flush_all(thrashed_set& data, Args&&... extras) { - reader_lock lock(m_cache_mutex); - for (const auto &tex: sections_to_flush) + writer_lock lock(m_cache_mutex); + + std::vector old_protections; + for (int n = data.num_flushable; n < data.affected_sections.size(); ++n) { - if (!tex->flush(std::forward(extras)...)) + old_protections.push_back(data.affected_sections[n]->get_protection()); + data.affected_sections[n]->unprotect(); + } + + for (int n = 0, i = 0; n < data.affected_sections.size(); ++n) + { + if (n < data.num_flushable) { - //Missed address, note this - //TODO: Lower severity when successful to keep the cache from overworking - record_cache_miss(*tex); + if (!data.affected_sections[n]->flush(std::forward(extras)...)) + { + //Missed address, note this + //TODO: Lower severity when successful to keep the cache from overworking + record_cache_miss(*data.affected_sections[n]); + } + } + else + { + //Restore protection on the remaining sections + data.affected_sections[n]->protect(old_protections[i++]); } } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 404b687fd4..ce252c10db 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -1214,12 +1214,12 @@ bool GLGSRender::on_access_violation(u32 address, bool is_writing) bool can_flush = (std::this_thread::get_id() == m_thread_id); auto result = m_gl_texture_cache.invalidate_address(address, is_writing, can_flush); - if (!result.first) + if (!result.violation_handled) return false; - if (result.second.size() > 0) + if (result.num_flushable > 0) { - work_item &task = post_flush_request(address, result.second); + work_item &task = post_flush_request(address, result); vm::temporary_unlock(); { @@ -1237,7 +1237,7 @@ bool GLGSRender::on_access_violation(u32 address, bool is_writing) void GLGSRender::on_notify_memory_unmapped(u32 address_base, u32 size) { //Discard all memory in that range without bothering with writeback (Force it for strict?) - if (std::get<0>(m_gl_texture_cache.invalidate_range(address_base, size, true, true, false))) + if (m_gl_texture_cache.invalidate_range(address_base, size, true, true, false).violation_handled) m_gl_texture_cache.purge_dirty(); } @@ -1254,7 +1254,7 @@ void GLGSRender::do_local_task() if (q.processed) continue; std::unique_lock lock(q.guard_mutex); - q.result = m_gl_texture_cache.flush_all(q.sections_to_flush); + q.result = m_gl_texture_cache.flush_all(q.section_data); q.processed = true; //Notify thread waiting on this @@ -1263,14 +1263,14 @@ void GLGSRender::do_local_task() } } -work_item& GLGSRender::post_flush_request(u32 address, std::vector& sections) +work_item& GLGSRender::post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data) { std::lock_guard lock(queue_guard); work_queue.emplace_back(); work_item &result = work_queue.back(); result.address_to_flush = address; - result.sections_to_flush = std::move(sections); + result.section_data = std::move(flush_data); return result; } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 3f97d22cfa..30b88e91b3 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -28,7 +28,7 @@ struct work_item std::mutex guard_mutex; u32 address_to_flush = 0; - std::vector sections_to_flush; + gl::texture_cache::thrashed_set section_data; volatile bool processed = false; volatile bool result = false; @@ -428,7 +428,7 @@ public: void set_viewport(); void synchronize_buffers(); - work_item& post_flush_request(u32 address, std::vector& sections); + work_item& post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data); bool scaled_image_from_memory(rsx::blit_src_info& src_info, rsx::blit_dst_info& dst_info, bool interpolate) override; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 9ce490064f..771bd058ba 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -740,22 +740,22 @@ VKGSRender::~VKGSRender() bool VKGSRender::on_access_violation(u32 address, bool is_writing) { - std::pair> result; + vk::texture_cache::thrashed_set result; { std::lock_guard lock(m_secondary_cb_guard); result = std::move(m_texture_cache.invalidate_address(address, is_writing, false, *m_device, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue())); } - if (!result.first) + if (!result.violation_handled) return false; - if (result.second.size() > 0) + if (result.num_flushable > 0) { const bool is_rsxthr = std::this_thread::get_id() == rsx_thread; bool has_queue_ref = false; u64 sync_timestamp = 0ull; - for (const auto& tex : result.second) + for (const auto& tex : result.affected_sections) sync_timestamp = std::max(sync_timestamp, tex->get_sync_timestamp()); if (!is_rsxthr) @@ -826,7 +826,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing) } } - m_texture_cache.flush_all(result.second, *m_device, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); + m_texture_cache.flush_all(result, *m_device, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()); if (has_queue_ref) { @@ -840,8 +840,8 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing) void VKGSRender::on_notify_memory_unmapped(u32 address_base, u32 size) { std::lock_guard lock(m_secondary_cb_guard); - if (std::get<0>(m_texture_cache.invalidate_range(address_base, size, true, true, false, - *m_device, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()))) + if (m_texture_cache.invalidate_range(address_base, size, true, true, false, + *m_device, m_secondary_command_buffer, m_memory_type_mapping, m_swap_chain->get_present_queue()).violation_handled) { m_texture_cache.purge_dirty(); }