mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-03 21:41:26 +12:00
rsx: Implement framebuffer statistics to track the internal render resolution at runtime.
This commit is contained in:
parent
0f3d2c7085
commit
10d5907f46
9 changed files with 179 additions and 3 deletions
|
@ -478,6 +478,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||||
RSX/Common/TextureUtils.cpp
|
RSX/Common/TextureUtils.cpp
|
||||||
RSX/Common/texture_cache.cpp
|
RSX/Common/texture_cache.cpp
|
||||||
RSX/Core/RSXContext.cpp
|
RSX/Core/RSXContext.cpp
|
||||||
|
RSX/Core/RSXDisplay.cpp
|
||||||
RSX/Core/RSXDrawCommands.cpp
|
RSX/Core/RSXDrawCommands.cpp
|
||||||
RSX/gcm_enums.cpp
|
RSX/gcm_enums.cpp
|
||||||
RSX/gcm_printing.cpp
|
RSX/gcm_printing.cpp
|
||||||
|
|
|
@ -404,18 +404,19 @@ namespace rsx
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sort(std::predicate<const Ty&, const Ty&> auto predicate)
|
simple_array<Ty>& sort(std::predicate<const Ty&, const Ty&> auto predicate)
|
||||||
{
|
{
|
||||||
if (_size < 2)
|
if (_size < 2)
|
||||||
{
|
{
|
||||||
return;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(begin(), end(), predicate);
|
std::sort(begin(), end(), predicate);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename F, typename U = std::invoke_result_t<F, const Ty&>>
|
template <typename F, typename U = std::invoke_result_t<F, const Ty&>>
|
||||||
requires std::is_invocable_v<F, const Ty&>
|
requires (std::is_invocable_v<F, const Ty&> && std::is_trivially_destructible_v<U>)
|
||||||
simple_array<U> map(F&& xform) const
|
simple_array<U> map(F&& xform) const
|
||||||
{
|
{
|
||||||
simple_array<U> result;
|
simple_array<U> result;
|
||||||
|
@ -428,6 +429,20 @@ namespace rsx
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F, typename U = std::invoke_result_t<F, const Ty&>>
|
||||||
|
requires (std::is_invocable_v<F, const Ty&> && !std::is_trivially_destructible_v<U>)
|
||||||
|
std::vector<U> map(F&& xform) const
|
||||||
|
{
|
||||||
|
std::vector<U> result;
|
||||||
|
result.reserve(size());
|
||||||
|
|
||||||
|
for (auto it = begin(); it != end(); ++it)
|
||||||
|
{
|
||||||
|
result.push_back(xform(*it));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename F, typename U>
|
template <typename F, typename U>
|
||||||
requires std::is_invocable_r_v<U, F, const U&, const Ty&>
|
requires std::is_invocable_r_v<U, F, const U&, const Ty&>
|
||||||
U reduce(U initial_value, F&& reducer) const
|
U reduce(U initial_value, F&& reducer) const
|
||||||
|
|
107
rpcs3/Emu/RSX/Core/RSXDisplay.cpp
Normal file
107
rpcs3/Emu/RSX/Core/RSXDisplay.cpp
Normal file
|
@ -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<sorted_message_t> messages;
|
||||||
|
rsx::simple_array<framebuffer_dimensions_t> 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<u8>(x.aa_mode) > static_cast<u8>(y.aa_mode)))
|
||||||
|
.map(FN(real_stats[x.id].to_string()));
|
||||||
|
return fmt::merge(text, ", ");
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,9 +3,48 @@
|
||||||
#include <util/types.hpp>
|
#include <util/types.hpp>
|
||||||
#include <util/logs.hpp>
|
#include <util/logs.hpp>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class named_thread;
|
||||||
|
|
||||||
namespace rsx
|
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<u32>(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<rsx::surface_antialiasing, framebuffer_dimensions_t> 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
|
struct frame_statistics_t
|
||||||
{
|
{
|
||||||
u32 draw_calls;
|
u32 draw_calls;
|
||||||
|
@ -19,6 +58,8 @@ namespace rsx
|
||||||
|
|
||||||
u32 vertex_cache_request_count;
|
u32 vertex_cache_request_count;
|
||||||
u32 vertex_cache_miss_count;
|
u32 vertex_cache_miss_count;
|
||||||
|
|
||||||
|
framebuffer_statistics_t framebuffer_stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct frame_time_t
|
struct frame_time_t
|
||||||
|
|
|
@ -402,6 +402,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
rsx::overlays::set_debug_overlay_text(fmt::format(
|
rsx::overlays::set_debug_overlay_text(fmt::format(
|
||||||
|
"Internal Resolution: %s\n"
|
||||||
"RSX Load: %3d%%\n"
|
"RSX Load: %3d%%\n"
|
||||||
"draw calls: %16d\n"
|
"draw calls: %16d\n"
|
||||||
"draw call setup: %11dus\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"
|
"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"
|
"Texture uploads: %11u (%u from CPU - %02u%%, %u copies avoided)\n"
|
||||||
"Vertex cache hits: %9u/%u (%u%%)",
|
"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,
|
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,
|
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,
|
num_flushes, num_misses, cache_miss_ratio, num_unavoidable, num_mispredict, num_speculate,
|
||||||
|
|
|
@ -1637,6 +1637,10 @@ namespace rsx
|
||||||
layout.aa_factors[0] = aa_factor_u;
|
layout.aa_factors[0] = aa_factor_u;
|
||||||
layout.aa_factors[1] = aa_factor_v;
|
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;
|
bool really_changed = false;
|
||||||
|
|
||||||
for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i)
|
for (u8 i = 0; i < rsx::limits::color_buffers_count; ++i)
|
||||||
|
|
|
@ -831,6 +831,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
rsx::overlays::set_debug_overlay_text(fmt::format(
|
rsx::overlays::set_debug_overlay_text(fmt::format(
|
||||||
|
"Internal Resolution: %s\n"
|
||||||
"RSX Load: %3d%%\n"
|
"RSX Load: %3d%%\n"
|
||||||
"draw calls: %17d\n"
|
"draw calls: %17d\n"
|
||||||
"submits: %20d\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"
|
"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"
|
"Texture uploads: %12u (%u from CPU - %02u%%, %u copies avoided)\n"
|
||||||
"Vertex cache hits: %10u/%u (%u%%)",
|
"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,
|
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,
|
info.stats.textures_upload_time, info.stats.draw_exec_time, info.stats.flip_time,
|
||||||
num_dirty_textures, texture_memory_size, tmp_texture_memory_size,
|
num_dirty_textures, texture_memory_size, tmp_texture_memory_size,
|
||||||
|
|
|
@ -112,6 +112,7 @@
|
||||||
<ClCompile Include="Emu\perf_monitor.cpp" />
|
<ClCompile Include="Emu\perf_monitor.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp" />
|
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Core\RSXContext.cpp" />
|
<ClCompile Include="Emu\RSX\Core\RSXContext.cpp" />
|
||||||
|
<ClCompile Include="Emu\RSX\Core\RSXDisplay.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Core\RSXDrawCommands.cpp" />
|
<ClCompile Include="Emu\RSX\Core\RSXDrawCommands.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Host\MM.cpp" />
|
<ClCompile Include="Emu\RSX\Host\MM.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\Host\RSXDMAWriter.cpp" />
|
<ClCompile Include="Emu\RSX\Host\RSXDMAWriter.cpp" />
|
||||||
|
|
|
@ -1222,6 +1222,9 @@
|
||||||
<ClCompile Include="Emu\RSX\Core\RSXContext.cpp">
|
<ClCompile Include="Emu\RSX\Core\RSXContext.cpp">
|
||||||
<Filter>Emu\GPU\RSX\Core</Filter>
|
<Filter>Emu\GPU\RSX\Core</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Emu\RSX\Core\RSXDisplay.cpp">
|
||||||
|
<Filter>Emu\GPU\RSX\Core</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="Crypto\unzip.cpp">
|
<ClCompile Include="Crypto\unzip.cpp">
|
||||||
<Filter>Crypto</Filter>
|
<Filter>Crypto</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue