diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 22c98823b5..741d760b8a 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -422,8 +422,7 @@ namespace rsx element_push_buffer.clear(); - if (zcull_ctrl->active) - zcull_ctrl->on_draw(); + zcull_ctrl->on_draw(); if (capture_current_frame) { @@ -2122,8 +2121,6 @@ namespace rsx if (g_cfg.video.disable_zcull_queries) return; - bool testing_enabled = zcull_pixel_cnt_enabled || zcull_stats_enabled; - if (framebuffer_swap) { zcull_surface_active = false; @@ -2148,7 +2145,7 @@ namespace rsx } zcull_ctrl->set_enabled(this, zcull_rendering_enabled); - zcull_ctrl->set_active(this, zcull_rendering_enabled && testing_enabled && zcull_surface_active); + zcull_ctrl->set_status(this, zcull_surface_active, zcull_pixel_cnt_enabled, zcull_stats_enabled); } void thread::clear_zcull_stats(u32 type) @@ -2564,12 +2561,6 @@ namespace rsx Emu.Pause(); } - // Reset ZCULL ctrl - // NOTE: A semaphore release is part of RSX flip control and will handle ZCULL sync - // TODO: These routines belong in the state reset routines controlled by sys_rsx and cellGcmSetFlip - zcull_ctrl->set_active(this, false, true); - zcull_ctrl->clear(this); - // Save current state m_queued_flip.stats = m_frame_stats; m_queued_flip.push(buffer); @@ -2728,26 +2719,15 @@ namespace rsx ZCULL_control::~ZCULL_control() {} - void ZCULL_control::set_enabled(class ::rsx::thread* ptimer, bool state, bool flush_queue) - { - if (state != enabled) - { - enabled = state; - - if (active && !enabled) - set_active(ptimer, false, flush_queue); - } - } - void ZCULL_control::set_active(class ::rsx::thread* ptimer, bool state, bool flush_queue) { - if (state != active) + if (state != host_queries_active) { - active = state; + host_queries_active = state; if (state) { - verify(HERE), enabled && m_current_task == nullptr; + verify(HERE), unit_enabled && m_current_task == nullptr; allocate_new_query(ptimer); begin_occlusion_query(m_current_task); } @@ -2779,6 +2759,58 @@ namespace rsx } } + void ZCULL_control::check_state(class ::rsx::thread* ptimer, bool flush_queue) + { + // NOTE: Only enable host queries if pixel count is active to save on resources + // Can optionally be enabled for either stats enabled or zpass enabled for accuracy + const bool data_stream_available = write_enabled && (zpass_count_enabled /*|| stats_enabled*/); + if (host_queries_active && !data_stream_available) + { + // Stop + set_active(ptimer, false, flush_queue); + } + else if (!host_queries_active && data_stream_available && unit_enabled) + { + // Start + set_active(ptimer, true, flush_queue); + } + } + + void ZCULL_control::set_enabled(class ::rsx::thread* ptimer, bool state, bool flush_queue) + { + if (state != unit_enabled) + { + unit_enabled = state; + check_state(ptimer, flush_queue); + } + } + + void ZCULL_control::set_status(class ::rsx::thread* ptimer, bool surface_active, bool zpass_active, bool zcull_stats_active, bool flush_queue) + { + write_enabled = surface_active; + zpass_count_enabled = zpass_active; + stats_enabled = zcull_stats_active; + + check_state(ptimer, flush_queue); + + if (m_current_task && m_current_task->active) + { + // Data check + u32 expected_type = 0; + if (zpass_active) expected_type |= CELL_GCM_ZPASS_PIXEL_CNT; + if (zcull_stats_active) expected_type |= CELL_GCM_ZCULL_STATS; + + if (m_current_task->data_type != expected_type) [[unlikely]] + { + rsx_log.error("ZCULL queue interrupted by data type change!"); + + // Stop+start the current setup + set_active(ptimer, false, false); + set_active(ptimer, true, false); + } + } + } + void ZCULL_control::read_report(::rsx::thread* ptimer, vm::addr_t sink, u32 type) { if (m_current_task && type == CELL_GCM_ZPASS_PIXEL_CNT) @@ -2855,12 +2887,18 @@ namespace rsx m_current_task = m_free_occlusion_pool.top(); m_free_occlusion_pool.pop(); + m_current_task->data_type = 0; m_current_task->num_draws = 0; m_current_task->result = 0; m_current_task->active = true; m_current_task->owned = false; m_current_task->sync_tag = 0; m_current_task->timestamp = 0; + + // Flags determine what kind of payload is carried by queries in the 'report' + if (zpass_count_enabled) m_current_task->data_type |= CELL_GCM_ZPASS_PIXEL_CNT; + if (stats_enabled) m_current_task->data_type |= CELL_GCM_ZCULL_STATS; + return; } @@ -2988,7 +3026,7 @@ namespace rsx u32 processed = 0; const bool has_unclaimed = (m_pending_writes.back().sink == 0); - //Write all claimed reports unconditionally + // Write all claimed reports unconditionally for (auto &writer : m_pending_writes) { if (!writer.sink) @@ -3009,7 +3047,10 @@ namespace rsx if (query->result) { result += query->result; - m_statistics_map[writer.counter_tag] = result; + if (query->data_type & CELL_GCM_ZPASS_PIXEL_CNT) + { + m_statistics_map[writer.counter_tag] += query->result; + } } } else @@ -3024,7 +3065,8 @@ namespace rsx if (!writer.forwarder) { // No other queries in the chain, write result - write(&writer, ptimer->timestamp(), result); + const auto value = (writer.type == CELL_GCM_ZPASS_PIXEL_CNT) ? m_statistics_map[writer.counter_tag] : result; + write(&writer, ptimer->timestamp(), value); if (query && query->sync_tag == ptimer->cond_render_ctrl.eval_sync_tag) { @@ -3171,7 +3213,10 @@ namespace rsx if (query->result) { result += query->result; - m_statistics_map[writer.counter_tag] = result; + if (query->data_type & CELL_GCM_ZPASS_PIXEL_CNT) + { + m_statistics_map[writer.counter_tag] += query->result; + } } } else @@ -3191,7 +3236,10 @@ namespace rsx if (query->result) { result += query->result; - m_statistics_map[writer.counter_tag] = result; + if (query->data_type & CELL_GCM_ZPASS_PIXEL_CNT) + { + m_statistics_map[writer.counter_tag] += query->result; + } } } else @@ -3212,11 +3260,11 @@ namespace rsx stat_tag_to_remove = writer.counter_tag; - // only zpass supported right now if (!writer.forwarder) { // No other queries in the chain, write result - write(&writer, ptimer->timestamp(), result); + const auto value = (writer.type == CELL_GCM_ZPASS_PIXEL_CNT) ? m_statistics_map[writer.counter_tag] : result; + write(&writer, ptimer->timestamp(), value); if (query && query->sync_tag == ptimer->cond_render_ctrl.eval_sync_tag) { diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 725a7cb9a2..2d65251c37 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -335,6 +335,7 @@ namespace rsx u32 driver_handle; u32 result; u32 num_draws; + u32 data_type; u64 sync_tag; u64 timestamp; bool pending; @@ -367,8 +368,9 @@ namespace rsx sync_no_notify = 2 // If set, backend hint notifications will not be made }; - struct ZCULL_control + class ZCULL_control { + protected: // Delay before a report update operation is forced to retire const u32 max_zcull_delay_us = 300; const u32 min_zcull_tick_us = 100; @@ -377,8 +379,11 @@ namespace rsx const u32 occlusion_query_count = 1024; const u32 max_safe_queue_depth = 892; - bool active = false; - bool enabled = false; + bool unit_enabled = false; // The ZCULL unit is on + bool write_enabled = false; // A surface in the ZCULL-monitored tile region has been loaded for rasterization + bool stats_enabled = false; // Collecting of ZCULL statistics is enabled (not same as pixels passing Z test!) + bool zpass_count_enabled = false; // Collecting of ZPASS statistics is enabled. If this is off, the counter does not increment + bool host_queries_active = false; // The backend/host is gathering Z data for the ZCULL unit std::array m_occlusion_query_data = {}; std::stack m_free_occlusion_pool; @@ -397,17 +402,11 @@ namespace rsx std::vector m_pending_writes; std::unordered_map m_statistics_map; - ZCULL_control(); - ~ZCULL_control(); + // Enables/disables the ZCULL unit + void set_active(class ::rsx::thread* ptimer, bool active, bool flush_queue); - void set_enabled(class ::rsx::thread* ptimer, bool state, bool flush_queue = false); - void set_active(class ::rsx::thread* ptimer, bool state, bool flush_queue = false); - - void write(vm::addr_t sink, u64 timestamp, u32 type, u32 value); - void write(queued_report_write* writer, u64 timestamp, u32 value); - - // Read current zcull statistics into the address provided - void read_report(class ::rsx::thread* ptimer, vm::addr_t sink, u32 type); + // Checks current state of the unit and applies changes + void check_state(class ::rsx::thread* ptimer, bool flush_queue); // Sets up a new query slot and sets it to the current task void allocate_new_query(class ::rsx::thread* ptimer); @@ -415,6 +414,21 @@ namespace rsx // Free a query slot in use void free_query(occlusion_query_info* query); + // Write report to memory + void write(vm::addr_t sink, u64 timestamp, u32 type, u32 value); + void write(queued_report_write* writer, u64 timestamp, u32 value); + + public: + + ZCULL_control(); + ~ZCULL_control(); + + void set_enabled(class ::rsx::thread* ptimer, bool state, bool flush_queue = false); + void set_status(class ::rsx::thread* ptimer, bool surface_active, bool zpass_active, bool zcull_stats_active, bool flush_queue = false); + + // Read current zcull statistics into the address provided + void read_report(class ::rsx::thread* ptimer, vm::addr_t sink, u32 type); + // Clears current stat block and increments stat_tag_id void clear(class ::rsx::thread* ptimer); @@ -459,7 +473,7 @@ namespace rsx bool reserved = false; std::vector eval_sources; - u32 eval_sync_tag = 0; + u64 eval_sync_tag = 0; u32 eval_address = 0; // Resets common data