diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt index 9e6dac4e0c..eaefb3f048 100644 --- a/rpcs3/Emu/CMakeLists.txt +++ b/rpcs3/Emu/CMakeLists.txt @@ -478,6 +478,7 @@ target_sources(rpcs3_emu PRIVATE RSX/Common/TextureUtils.cpp RSX/Common/texture_cache.cpp RSX/Core/RSXContext.cpp + RSX/Core/RSXDisplay.cpp RSX/Core/RSXDrawCommands.cpp RSX/gcm_enums.cpp RSX/gcm_printing.cpp diff --git a/rpcs3/Emu/RSX/Common/simple_array.hpp b/rpcs3/Emu/RSX/Common/simple_array.hpp index e791af0274..8ae4179a0c 100644 --- a/rpcs3/Emu/RSX/Common/simple_array.hpp +++ b/rpcs3/Emu/RSX/Common/simple_array.hpp @@ -404,18 +404,19 @@ namespace rsx return ret; } - void sort(std::predicate auto predicate) + simple_array& sort(std::predicate auto predicate) { if (_size < 2) { - return; + return *this; } std::sort(begin(), end(), predicate); + return *this; } template > - requires std::is_invocable_v + requires (std::is_invocable_v && std::is_trivially_destructible_v) simple_array map(F&& xform) const { simple_array result; @@ -428,6 +429,20 @@ namespace rsx return result; } + template > + requires (std::is_invocable_v && !std::is_trivially_destructible_v) + std::vector map(F&& xform) const + { + std::vector result; + result.reserve(size()); + + for (auto it = begin(); it != end(); ++it) + { + result.push_back(xform(*it)); + } + return result; + } + template requires std::is_invocable_r_v U reduce(U initial_value, F&& reducer) const diff --git a/rpcs3/Emu/RSX/Core/RSXDisplay.cpp b/rpcs3/Emu/RSX/Core/RSXDisplay.cpp new file mode 100644 index 0000000000..e263a945ef --- /dev/null +++ b/rpcs3/Emu/RSX/Core/RSXDisplay.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "RSXDisplay.h" + +#include "../Common/simple_array.hpp" +#include "../rsx_utils.h" + +namespace rsx +{ + std::string framebuffer_dimensions_t::to_string(bool skip_aa_suffix) const + { + std::string suffix = ""; + const auto spp = samples_x * samples_y; + + if (!skip_aa_suffix && spp > 1) + { + suffix = std::string(" @MSAA ") + std::to_string(spp) + "x"; + } + + return std::to_string(width) + "x" + std::to_string(height) + suffix; + } + + framebuffer_dimensions_t framebuffer_dimensions_t::make(u16 width, u16 height, rsx::surface_antialiasing aa) + { + framebuffer_dimensions_t result { .width = width, .height = height }; + switch (aa) + { + case rsx::surface_antialiasing::center_1_sample: + result.samples_x = result.samples_y = 1; + break; + case rsx::surface_antialiasing::diagonal_centered_2_samples: + result.samples_x = 2; + result.samples_y = 1; + break; + case rsx::surface_antialiasing::square_centered_4_samples: + case rsx::surface_antialiasing::square_rotated_4_samples: + result.samples_x = result.samples_y = 2; + break; + } + return result; + } + + void framebuffer_statistics_t::add(u16 width, u16 height, rsx::surface_antialiasing aa) + { + auto& stashed = data[aa]; + const auto& incoming = framebuffer_dimensions_t::make(width, height, aa); + if (incoming > stashed) + { + stashed = incoming; + } + } + + std::string framebuffer_statistics_t::to_string(bool squash) const + { + // Format is sorted by sample count + struct sorted_message_t + { + u32 id; + surface_antialiasing aa_mode; + u32 samples; + }; + + if (data.size() == 0) + { + return "None"; + } + + rsx::simple_array messages; + rsx::simple_array real_stats; + + for (const auto& [aa_mode, stat] : data) + { + auto real_stat = stat; + std::tie(real_stat.width, real_stat.height) = apply_resolution_scale(stat.width, stat.height); + real_stats.push_back(real_stat); + + sorted_message_t msg; + msg.id = real_stats.size() - 1; + msg.aa_mode = aa_mode; + msg.samples = real_stat.samples_total(); + messages.push_back(msg); + } + + if (squash) + { + messages.sort(FN(x.samples > y.samples)); + return real_stats[messages.front().id] + .to_string(g_cfg.video.antialiasing_level == msaa_level::none); + } + + if (messages.size() > 1) + { + // Should we bother showing the No-AA entry? + // This heurestic ignores pointless no-AA surfaces usually used as compositing buffers for output. + messages.sort(FN(x.samples > y.samples)); + if (messages.back().aa_mode == rsx::surface_antialiasing::center_1_sample) + { + // Drop the last entry if it has no AA. + messages.resize(messages.size() - 1); + } + } + + const auto text = messages + .sort(FN(static_cast(x.aa_mode) > static_cast(y.aa_mode))) + .map(FN(real_stats[x.id].to_string())); + return fmt::merge(text, ", "); + } +} diff --git a/rpcs3/Emu/RSX/Core/RSXDisplay.h b/rpcs3/Emu/RSX/Core/RSXDisplay.h index 5628ce77a2..4d4c90cd0d 100644 --- a/rpcs3/Emu/RSX/Core/RSXDisplay.h +++ b/rpcs3/Emu/RSX/Core/RSXDisplay.h @@ -3,9 +3,48 @@ #include #include #include +#include + +template +class named_thread; namespace rsx { + enum class surface_antialiasing : u8; + + struct framebuffer_dimensions_t + { + u16 width; + u16 height; + u8 samples_x; + u8 samples_y; + + inline u32 samples_total() const + { + return static_cast(width) * height * samples_x * samples_y; + } + + inline bool operator > (const framebuffer_dimensions_t& that) const + { + return samples_total() > that.samples_total(); + } + + std::string to_string(bool skip_aa_suffix = false) const; + + static framebuffer_dimensions_t make(u16 width, u16 height, rsx::surface_antialiasing aa); + }; + + struct framebuffer_statistics_t + { + std::unordered_map data; + + // Replace the existing data with this input if it is greater than what is already known + void add(u16 width, u16 height, rsx::surface_antialiasing aa); + + // Returns a formatted string representing the statistics collected over the frame. + std::string to_string(bool squash) const; + }; + struct frame_statistics_t { u32 draw_calls; @@ -19,6 +58,8 @@ namespace rsx u32 vertex_cache_request_count; u32 vertex_cache_miss_count; + + framebuffer_statistics_t framebuffer_stats; }; struct frame_time_t diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index 2decb52c97..1eaa3a6193 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -402,6 +402,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) : 0; rsx::overlays::set_debug_overlay_text(fmt::format( + "Internal Resolution: %s\n" "RSX Load: %3d%%\n" "draw calls: %16d\n" "draw call setup: %11dus\n" @@ -413,6 +414,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) "Flush requests: %12d = %2d (%3d%%) hard faults, %2d unavoidable, %2d misprediction(s), %2d speculation(s)\n" "Texture uploads: %11u (%u from CPU - %02u%%, %u copies avoided)\n" "Vertex cache hits: %9u/%u (%u%%)", + info.stats.framebuffer_stats.to_string(!backend_config.supports_hw_msaa), get_load(), info.stats.draw_calls, info.stats.setup_time, info.stats.vertex_upload_time, info.stats.textures_upload_time, info.stats.draw_exec_time, num_dirty_textures, texture_memory_size, num_flushes, num_misses, cache_miss_ratio, num_unavoidable, num_mispredict, num_speculate, diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 703c762ae0..e128203957 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1637,6 +1637,10 @@ namespace rsx layout.aa_factors[0] = aa_factor_u; layout.aa_factors[1] = aa_factor_v; + // Log this to frame stats + m_frame_stats.framebuffer_stats.add(layout.width, layout.height, aa_mode); + + // Check if anything has changed bool really_changed = false; for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i) diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index 0c32d9034d..f1bcc214f9 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -831,6 +831,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) : 0; rsx::overlays::set_debug_overlay_text(fmt::format( + "Internal Resolution: %s\n" "RSX Load: %3d%%\n" "draw calls: %17d\n" "submits: %20d\n" @@ -845,6 +846,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) "Flush requests: %13d = %2d (%3d%%) hard faults, %2d unavoidable, %2d misprediction(s), %2d speculation(s)\n" "Texture uploads: %12u (%u from CPU - %02u%%, %u copies avoided)\n" "Vertex cache hits: %10u/%u (%u%%)", + info.stats.framebuffer_stats.to_string(!backend_config.supports_hw_msaa), get_load(), info.stats.draw_calls, info.stats.submit_count, info.stats.setup_time, info.stats.vertex_upload_time, info.stats.textures_upload_time, info.stats.draw_exec_time, info.stats.flip_time, num_dirty_textures, texture_memory_size, tmp_texture_memory_size, diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 049dc05ab2..07ca501ff3 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -112,6 +112,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 53038d5652..cf3626a7f5 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1222,6 +1222,9 @@ Emu\GPU\RSX\Core + + Emu\GPU\RSX\Core + Crypto