rsx: Export more information about affected cache sections when handling violations

- This allows for better handling of deferred flushes.
-- There's still no guarantee that cache contents will have changed between the set acquisition and following flush operation
-- Hopefully this is rare enough that it doesnt cause serious issues for now
This commit is contained in:
kd-11 2017-10-28 00:32:27 +03:00
parent 49f4da3016
commit 055f0e2e4a
4 changed files with 69 additions and 37 deletions

View file

@ -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<section_storage_type*> 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 <typename ...Args>
std::pair<bool, std::vector<section_storage_type*>> 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<int>(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 <typename ...Args>
std::pair<bool, std::vector<section_storage_type*>> 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<Args>(extras)...);
}
@ -585,13 +601,13 @@ namespace rsx
}
template <typename ...Args>
std::pair<bool, std::vector<section_storage_type*>> 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<Args>(extras)...);
}
template <typename ...Args>
std::pair<bool, std::vector<section_storage_type*>> 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<u32, u32> 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 <typename ...Args>
bool flush_all(std::vector<section_storage_type*>& 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<utils::protection> old_protections;
for (int n = data.num_flushable; n < data.affected_sections.size(); ++n)
{
if (!tex->flush(std::forward<Args>(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<Args>(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++]);
}
}

View file

@ -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<std::mutex> 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<gl::cached_texture_section*>& sections)
work_item& GLGSRender::post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data)
{
std::lock_guard<std::mutex> 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;
}

View file

@ -28,7 +28,7 @@ struct work_item
std::mutex guard_mutex;
u32 address_to_flush = 0;
std::vector<gl::cached_texture_section*> 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<gl::cached_texture_section*>& 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;

View file

@ -740,22 +740,22 @@ VKGSRender::~VKGSRender()
bool VKGSRender::on_access_violation(u32 address, bool is_writing)
{
std::pair<bool, std::vector<vk::cached_texture_section*>> result;
vk::texture_cache::thrashed_set result;
{
std::lock_guard<std::mutex> 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<std::mutex> 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();
}