mirror of https://github.com/RPCS3/rpcs3.git
rsx: Implement RSX reports area access detection and optimize around it
- If nobody is reading RSX reports, do not be in a hurry to write them - Requires HLE of some methods (cellGcmGetTimestamp) to function correctly
This commit is contained in:
parent
34220ec447
commit
9a1e6cc3e8
|
@ -960,7 +960,7 @@ bool GLGSRender::on_access_violation(u32 address, bool is_writing)
|
||||||
|
|
||||||
if (!result.violation_handled)
|
if (!result.violation_handled)
|
||||||
{
|
{
|
||||||
return false;
|
return zcull_ctrl->on_access_violation(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.num_flushable > 0)
|
if (result.num_flushable > 0)
|
||||||
|
|
|
@ -2603,15 +2603,7 @@ namespace rsx
|
||||||
{
|
{
|
||||||
if (zcull_ctrl->has_pending())
|
if (zcull_ctrl->has_pending())
|
||||||
{
|
{
|
||||||
if (g_cfg.video.relaxed_zcull_sync)
|
zcull_ctrl->sync(this);
|
||||||
{
|
|
||||||
// Emit zcull sync hint and update; guarantees results to be written shortly after this event
|
|
||||||
zcull_ctrl->update(this, 0, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
zcull_ctrl->sync(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fragment constants may have been updated
|
// Fragment constants may have been updated
|
||||||
|
|
|
@ -3,6 +3,18 @@
|
||||||
|
|
||||||
namespace rsx
|
namespace rsx
|
||||||
{
|
{
|
||||||
|
static inline std::string_view location_tostring(u32 location)
|
||||||
|
{
|
||||||
|
ensure(location < 2);
|
||||||
|
const char* location_names[] = {"CELL_GCM_LOCATION_LOCAL", "CELL_GCM_LOCATION_MAIN"};
|
||||||
|
return location_names[location];
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 classify_location(u32 address)
|
||||||
|
{
|
||||||
|
return (address >= rsx::constants::local_mem_base) ? CELL_GCM_LOCATION_LOCAL : CELL_GCM_LOCATION_MAIN;
|
||||||
|
}
|
||||||
|
|
||||||
namespace reports
|
namespace reports
|
||||||
{
|
{
|
||||||
ZCULL_control::ZCULL_control()
|
ZCULL_control::ZCULL_control()
|
||||||
|
@ -166,6 +178,8 @@ namespace rsx
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on_report_enqueued(sink);
|
||||||
|
|
||||||
ptimer->async_tasks_pending++;
|
ptimer->async_tasks_pending++;
|
||||||
|
|
||||||
if (m_statistics_map[m_statistics_tag_id].result != 0)
|
if (m_statistics_map[m_statistics_tag_id].result != 0)
|
||||||
|
@ -308,12 +322,14 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
rsx::reservation_lock<true> lock(sink, 16);
|
rsx::reservation_lock<true> lock(sink, 16);
|
||||||
vm::_ref<atomic_t<CellGcmReportData>>(sink).store({ timestamp, value, 0 });
|
auto report = vm::get_super_ptr<atomic_t<CellGcmReportData>>(sink);
|
||||||
|
report->store({ timestamp, value, 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZCULL_control::write(queued_report_write* writer, u64 timestamp, u32 value)
|
void ZCULL_control::write(queued_report_write* writer, u64 timestamp, u32 value)
|
||||||
{
|
{
|
||||||
write(writer->sink, timestamp, writer->type, value);
|
write(writer->sink, timestamp, writer->type, value);
|
||||||
|
on_report_completed(writer->sink);
|
||||||
|
|
||||||
for (auto& addr : writer->sink_alias)
|
for (auto& addr : writer->sink_alias)
|
||||||
{
|
{
|
||||||
|
@ -352,94 +368,109 @@ namespace rsx
|
||||||
|
|
||||||
void ZCULL_control::sync(::rsx::thread* ptimer)
|
void ZCULL_control::sync(::rsx::thread* ptimer)
|
||||||
{
|
{
|
||||||
if (!m_pending_writes.empty())
|
if (m_pending_writes.empty())
|
||||||
{
|
{
|
||||||
// Quick reverse scan to push commands ahead of time
|
// Nothing to do
|
||||||
for (auto It = m_pending_writes.rbegin(); It != m_pending_writes.rend(); ++It)
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_critical_reports_in_flight)
|
||||||
|
{
|
||||||
|
// Valid call, but nothing important queued up
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_cfg.video.relaxed_zcull_sync)
|
||||||
|
{
|
||||||
|
update(ptimer, 0, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick reverse scan to push commands ahead of time
|
||||||
|
for (auto It = m_pending_writes.rbegin(); It != m_pending_writes.rend(); ++It)
|
||||||
|
{
|
||||||
|
if (It->query && It->query->num_draws)
|
||||||
{
|
{
|
||||||
if (It->query && It->query->num_draws)
|
if (It->query->sync_tag > m_sync_tag)
|
||||||
{
|
{
|
||||||
if (It->query->sync_tag > m_sync_tag)
|
// rsx_log.trace("[Performance warning] Query hint emit during sync command.");
|
||||||
{
|
ptimer->sync_hint(FIFO_hint::hint_zcull_sync, It->query);
|
||||||
// rsx_log.trace("[Performance warning] Query hint emit during sync command.");
|
|
||||||
ptimer->sync_hint(FIFO_hint::hint_zcull_sync, It->query);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 processed = 0;
|
|
||||||
const bool has_unclaimed = (m_pending_writes.back().sink == 0);
|
|
||||||
|
|
||||||
// Write all claimed reports unconditionally
|
|
||||||
for (auto& writer : m_pending_writes)
|
|
||||||
{
|
|
||||||
if (!writer.sink)
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto query = writer.query;
|
|
||||||
auto& counter = m_statistics_map[writer.counter_tag];
|
|
||||||
|
|
||||||
if (query)
|
|
||||||
{
|
|
||||||
ensure(query->pending);
|
|
||||||
|
|
||||||
const bool implemented = (writer.type == CELL_GCM_ZPASS_PIXEL_CNT || writer.type == CELL_GCM_ZCULL_STATS3);
|
|
||||||
const bool have_result = counter.result && !g_cfg.video.precise_zpass_count;
|
|
||||||
|
|
||||||
if (implemented && !have_result && query->num_draws)
|
|
||||||
{
|
|
||||||
get_occlusion_query_result(query);
|
|
||||||
counter.result += query->result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Already have a hit, no need to retest
|
|
||||||
discard_occlusion_query(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
free_query(query);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retire(ptimer, &writer, counter.result);
|
break;
|
||||||
processed++;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 processed = 0;
|
||||||
|
const bool has_unclaimed = (m_pending_writes.back().sink == 0);
|
||||||
|
|
||||||
|
// Write all claimed reports unconditionally
|
||||||
|
for (auto& writer : m_pending_writes)
|
||||||
|
{
|
||||||
|
if (!writer.sink)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto query = writer.query;
|
||||||
|
auto& counter = m_statistics_map[writer.counter_tag];
|
||||||
|
|
||||||
|
if (query)
|
||||||
|
{
|
||||||
|
ensure(query->pending);
|
||||||
|
|
||||||
|
const bool implemented = (writer.type == CELL_GCM_ZPASS_PIXEL_CNT || writer.type == CELL_GCM_ZCULL_STATS3);
|
||||||
|
const bool have_result = counter.result && !g_cfg.video.precise_zpass_count;
|
||||||
|
|
||||||
|
if (implemented && !have_result && query->num_draws)
|
||||||
|
{
|
||||||
|
get_occlusion_query_result(query);
|
||||||
|
counter.result += query->result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Already have a hit, no need to retest
|
||||||
|
discard_occlusion_query(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_query(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_unclaimed)
|
retire(ptimer, &writer, counter.result);
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_unclaimed)
|
||||||
|
{
|
||||||
|
ensure(processed == m_pending_writes.size());
|
||||||
|
m_pending_writes.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto remaining = m_pending_writes.size() - processed;
|
||||||
|
ensure(remaining > 0);
|
||||||
|
|
||||||
|
if (remaining == 1)
|
||||||
{
|
{
|
||||||
ensure(processed == m_pending_writes.size());
|
m_pending_writes[0] = std::move(m_pending_writes.back());
|
||||||
m_pending_writes.clear();
|
m_pending_writes.resize(1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto remaining = m_pending_writes.size() - processed;
|
std::move(m_pending_writes.begin() + processed, m_pending_writes.end(), m_pending_writes.begin());
|
||||||
ensure(remaining > 0);
|
m_pending_writes.resize(remaining);
|
||||||
|
|
||||||
if (remaining == 1)
|
|
||||||
{
|
|
||||||
m_pending_writes[0] = std::move(m_pending_writes.back());
|
|
||||||
m_pending_writes.resize(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::move(m_pending_writes.begin() + processed, m_pending_writes.end(), m_pending_writes.begin());
|
|
||||||
m_pending_writes.resize(remaining);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete all statistics caches but leave the current one
|
|
||||||
for (auto It = m_statistics_map.begin(); It != m_statistics_map.end(); )
|
|
||||||
{
|
|
||||||
if (It->first == m_statistics_tag_id)
|
|
||||||
++It;
|
|
||||||
else
|
|
||||||
It = m_statistics_map.erase(It);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Decrement jobs counter
|
|
||||||
ptimer->async_tasks_pending -= processed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Delete all statistics caches but leave the current one
|
||||||
|
for (auto It = m_statistics_map.begin(); It != m_statistics_map.end(); )
|
||||||
|
{
|
||||||
|
if (It->first == m_statistics_tag_id)
|
||||||
|
++It;
|
||||||
|
else
|
||||||
|
It = m_statistics_map.erase(It);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decrement jobs counter
|
||||||
|
ptimer->async_tasks_pending -= processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZCULL_control::update(::rsx::thread* ptimer, u32 sync_address, bool hint)
|
void ZCULL_control::update(::rsx::thread* ptimer, u32 sync_address, bool hint)
|
||||||
|
@ -486,7 +517,7 @@ namespace rsx
|
||||||
m_next_tsc = m_tsc + min_zcull_tick_us;
|
m_next_tsc = m_tsc + min_zcull_tick_us;
|
||||||
|
|
||||||
// Schedule a queue flush if needed
|
// Schedule a queue flush if needed
|
||||||
if (!g_cfg.video.relaxed_zcull_sync &&
|
if (!g_cfg.video.relaxed_zcull_sync && m_critical_reports_in_flight &&
|
||||||
front.query && front.query->num_draws && front.query->sync_tag > m_sync_tag)
|
front.query && front.query->num_draws && front.query->sync_tag > m_sync_tag)
|
||||||
{
|
{
|
||||||
const auto elapsed = m_tsc - front.query->timestamp;
|
const auto elapsed = m_tsc - front.query->timestamp;
|
||||||
|
@ -624,7 +655,27 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sync_address || !query)
|
if (!sync_address || !query)
|
||||||
|
{
|
||||||
return result_none;
|
return result_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable optimizations across the accessed range if reports reside within
|
||||||
|
{
|
||||||
|
std::scoped_lock lock(m_pages_mutex);
|
||||||
|
|
||||||
|
const auto location1 = rsx::classify_location(memory_address);
|
||||||
|
const auto location2 = rsx::classify_location(memory_end - 1);
|
||||||
|
|
||||||
|
if (!m_pages_accessed[location1])
|
||||||
|
{
|
||||||
|
disable_optimizations(ptimer, location1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_pages_accessed[location2])
|
||||||
|
{
|
||||||
|
disable_optimizations(ptimer, location2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!(flags & sync_defer_copy))
|
if (!(flags & sync_defer_copy))
|
||||||
{
|
{
|
||||||
|
@ -650,6 +701,7 @@ namespace rsx
|
||||||
|
|
||||||
flags32_t ZCULL_control::read_barrier(class ::rsx::thread* ptimer, u32 memory_address, occlusion_query_info* query)
|
flags32_t ZCULL_control::read_barrier(class ::rsx::thread* ptimer, u32 memory_address, occlusion_query_info* query)
|
||||||
{
|
{
|
||||||
|
// Called by cond render control. Internal RSX usage, do not disable optimizations
|
||||||
while (query->pending && !Emu.IsStopped())
|
while (query->pending && !Emu.IsStopped())
|
||||||
{
|
{
|
||||||
update(ptimer, memory_address);
|
update(ptimer, memory_address);
|
||||||
|
@ -729,6 +781,127 @@ namespace rsx
|
||||||
return bytes_to_write;
|
return bytes_to_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ZCULL_control::on_report_enqueued(vm::addr_t address)
|
||||||
|
{
|
||||||
|
const auto location = (address >= rsx::constants::local_mem_base) ? CELL_GCM_LOCATION_LOCAL : CELL_GCM_LOCATION_MAIN;
|
||||||
|
std::scoped_lock lock(m_pages_mutex);
|
||||||
|
|
||||||
|
if (!m_pages_accessed[location]) [[ likely ]]
|
||||||
|
{
|
||||||
|
const auto page_address = static_cast<u32>(address) & ~0xfff;
|
||||||
|
auto& page = m_locked_pages[location][page_address];
|
||||||
|
page.add_ref();
|
||||||
|
|
||||||
|
if (page.prot == utils::protection::rw)
|
||||||
|
{
|
||||||
|
utils::memory_protect(vm::base(page_address), 4096, utils::protection::no);
|
||||||
|
page.prot = utils::protection::no;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_critical_reports_in_flight++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZCULL_control::on_report_completed(vm::addr_t address)
|
||||||
|
{
|
||||||
|
const auto location = (address >= rsx::constants::local_mem_base) ? CELL_GCM_LOCATION_LOCAL : CELL_GCM_LOCATION_MAIN;
|
||||||
|
if (!m_pages_accessed[location])
|
||||||
|
{
|
||||||
|
const auto page_address = static_cast<u32>(address) & ~0xfff;
|
||||||
|
std::scoped_lock lock(m_pages_mutex);
|
||||||
|
|
||||||
|
if (auto found = m_locked_pages[location].find(page_address);
|
||||||
|
found != m_locked_pages[location].end())
|
||||||
|
{
|
||||||
|
auto& page = found->second;
|
||||||
|
|
||||||
|
ensure(page.has_refs());
|
||||||
|
page.release();
|
||||||
|
|
||||||
|
if (!page.has_refs())
|
||||||
|
{
|
||||||
|
if (page.prot != utils::protection::rw)
|
||||||
|
{
|
||||||
|
utils::memory_protect(vm::base(page_address), 4096, utils::protection::rw);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locked_pages[location].erase(page_address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pages_accessed[location])
|
||||||
|
{
|
||||||
|
m_critical_reports_in_flight--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZCULL_control::disable_optimizations(::rsx::thread* ptimer, u32 location)
|
||||||
|
{
|
||||||
|
// Externally synchronized
|
||||||
|
rsx_log.warning("Reports area at location %s was accessed. ZCULL optimizations will be disabled.", location_tostring(location));
|
||||||
|
m_pages_accessed[location] = true;
|
||||||
|
|
||||||
|
// Flush all pending writes
|
||||||
|
m_critical_reports_in_flight += 0x100000;
|
||||||
|
sync(ptimer);
|
||||||
|
m_critical_reports_in_flight -= 0x100000;
|
||||||
|
|
||||||
|
// Unlock pages
|
||||||
|
for (auto& p : m_locked_pages[location])
|
||||||
|
{
|
||||||
|
const auto this_address = p.first;
|
||||||
|
auto& page = p.second;
|
||||||
|
|
||||||
|
if (page.prot != utils::protection::rw)
|
||||||
|
{
|
||||||
|
utils::memory_protect(vm::base(this_address), 4096, utils::protection::rw);
|
||||||
|
page.prot = utils::protection::rw;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (page.has_refs())
|
||||||
|
{
|
||||||
|
m_critical_reports_in_flight++;
|
||||||
|
page.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_locked_pages[location].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZCULL_control::on_access_violation(u32 address)
|
||||||
|
{
|
||||||
|
const auto page_address = address & ~0xfff;
|
||||||
|
const auto location = rsx::classify_location(address);
|
||||||
|
|
||||||
|
if (m_pages_accessed[location])
|
||||||
|
{
|
||||||
|
// Already faulted, no locks possible
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
reader_lock lock(m_pages_mutex);
|
||||||
|
|
||||||
|
if (auto found = m_locked_pages[location].find(page_address);
|
||||||
|
found != m_locked_pages[location].end())
|
||||||
|
{
|
||||||
|
lock.upgrade();
|
||||||
|
|
||||||
|
auto& fault_page = m_locked_pages[location][page_address];
|
||||||
|
if (fault_page.prot != utils::protection::rw)
|
||||||
|
{
|
||||||
|
disable_optimizations(rsx::get_current_renderer(), location);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Conditional rendering helpers
|
// Conditional rendering helpers
|
||||||
void conditional_render_eval::reset()
|
void conditional_render_eval::reset()
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <util/types.hpp>
|
#include <util/types.hpp>
|
||||||
|
#include <util/vm.hpp>
|
||||||
#include <Emu/Memory/vm.h>
|
#include <Emu/Memory/vm.h>
|
||||||
|
|
||||||
#include "rsx_utils.h"
|
#include "rsx_utils.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -51,6 +53,11 @@ namespace rsx
|
||||||
u32 reserved;
|
u32 reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MMIO_page_data_t : public rsx::ref_counted
|
||||||
|
{
|
||||||
|
utils::protection prot = utils::protection::rw;
|
||||||
|
};
|
||||||
|
|
||||||
enum sync_control
|
enum sync_control
|
||||||
{
|
{
|
||||||
sync_none = 0,
|
sync_none = 0,
|
||||||
|
@ -60,6 +67,16 @@ namespace rsx
|
||||||
|
|
||||||
class ZCULL_control
|
class ZCULL_control
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
std::unordered_map<u32, MMIO_page_data_t> m_locked_pages[2];
|
||||||
|
atomic_t<bool> m_pages_accessed[2] = { false, false };
|
||||||
|
atomic_t<s32> m_critical_reports_in_flight = { 0 };
|
||||||
|
shared_mutex m_pages_mutex;
|
||||||
|
|
||||||
|
void on_report_enqueued(vm::addr_t address);
|
||||||
|
void on_report_completed(vm::addr_t address);
|
||||||
|
void disable_optimizations(class ::rsx::thread* ptimer, u32 location);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Delay before a report update operation is forced to retire
|
// Delay before a report update operation is forced to retire
|
||||||
const u32 max_zcull_delay_us = 300;
|
const u32 max_zcull_delay_us = 300;
|
||||||
|
@ -153,6 +170,9 @@ namespace rsx
|
||||||
// Copies queries in range rebased from source range to destination range
|
// Copies queries in range rebased from source range to destination range
|
||||||
u32 copy_reports_to(u32 start, u32 range, u32 dest);
|
u32 copy_reports_to(u32 start, u32 range, u32 dest);
|
||||||
|
|
||||||
|
// Check paging issues
|
||||||
|
bool on_access_violation(u32 address);
|
||||||
|
|
||||||
// Backend methods (optional, will return everything as always visible by default)
|
// Backend methods (optional, will return everything as always visible by default)
|
||||||
virtual void begin_occlusion_query(occlusion_query_info* /*query*/) {}
|
virtual void begin_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||||
virtual void end_occlusion_query(occlusion_query_info* /*query*/) {}
|
virtual void end_occlusion_query(occlusion_query_info* /*query*/) {}
|
||||||
|
|
|
@ -789,7 +789,7 @@ bool VKGSRender::on_access_violation(u32 address, bool is_writing)
|
||||||
|
|
||||||
if (!result.violation_handled)
|
if (!result.violation_handled)
|
||||||
{
|
{
|
||||||
return false;
|
return zcull_ctrl->on_access_violation(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.num_flushable > 0)
|
if (result.num_flushable > 0)
|
||||||
|
|
Loading…
Reference in New Issue