Skip to content

Commit

Permalink
feat: individual vgui draw call timing
Browse files Browse the repository at this point in the history
  • Loading branch information
ThisAMJ committed Aug 30, 2024
1 parent 8b05eae commit 2b2f6f8
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 34 deletions.
1 change: 1 addition & 0 deletions docs/cvars.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@
|sar_performance_hud|0|Enables the performance HUD.<br>1 = normal,<br>2 = stats only.|
|sar_performance_hud_clear|cmd|Clears the performance HUD data.|
|sar_performance_hud_duration|60|How long (in frames) to measure performance for.|
|sar_performance_hud_duration_vgui|60|How long (in frames) to measure performance for each individual VGui element.|
|sar_performance_hud_font_index|6|Font index of the performance HUD.|
|sar_performance_hud_x|20|X position of the performance HUD.|
|sar_performance_hud_y|300|Y position of the performance HUD.|
Expand Down
98 changes: 64 additions & 34 deletions src/Features/Hud/PerformanceHud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

Variable sar_performance_hud("sar_performance_hud", "0", "Enables the performance HUD.\n1 = normal,\n2 = stats only.\n");
Variable sar_performance_hud_duration("sar_performance_hud_duration", "60", 1, "How long (in frames) to measure performance for.\n");
Variable sar_performance_hud_duration_vgui("sar_performance_hud_duration_vgui", "60", 1, "How long (in frames) to measure performance for each individual VGui element.\n");

Variable sar_performance_hud_x("sar_performance_hud_x", "20", "X position of the performance HUD.\n");
Variable sar_performance_hud_y("sar_performance_hud_y", "300", "Y position of the performance HUD.\n");
Expand Down Expand Up @@ -79,46 +80,62 @@ void PerformanceHud::Paint(int slot) {
drawTimeType(this->times_preTick, "pre tick ");
drawTimeType(this->times_postTick, "post tick");

if (sar_performance_hud.GetInt() > 1) return;
if (sar_performance_hud.GetInt() == 1) {
int buckets = PERFORMANCE_HUD_BUCKETS;

int buckets = PERFORMANCE_HUD_BUCKETS;
// some stupid statistics to get a rough cutoff for outliers
// makes graph more readable
float faux_iqr_onTick = (std::min)(mean_onTick - min_onTick, max_onTick - mean_onTick) * 0.5f;
float faux_iqr_offTick = (std::min)(mean_offTick - min_offTick, max_onTick - mean_offTick) * 0.5f;
float faux_iqr = (std::max)(faux_iqr_onTick, faux_iqr_offTick);
max_onTick = (std::max)(mean_offTick, mean_onTick) + faux_iqr * 3.0f;
min_onTick = (std::min)(0.0f, (std::min)(mean_offTick, mean_onTick) - faux_iqr * 3.0f);

// some stupid statistics to get a rough cutoff for outliers
// makes graph more readable
float faux_iqr_onTick = (std::min)(mean_onTick - min_onTick, max_onTick - mean_onTick) * 0.5f;
float faux_iqr_offTick = (std::min)(mean_offTick - min_offTick, max_onTick - mean_offTick) * 0.5f;
float faux_iqr = (std::max)(faux_iqr_onTick, faux_iqr_offTick);
max_onTick = (std::max)(mean_offTick, mean_onTick) + faux_iqr * 3.0f;
min_onTick = (std::min)(0.0f, (std::min)(mean_offTick, mean_onTick) - faux_iqr * 3.0f);

std::vector<int> buckets_offTick(buckets);
std::vector<int> buckets_onTick(buckets);
for (auto &frametime : this->times_totalframe_offTick) {
int bucket = (int)((frametime - min_onTick) * buckets / (max_onTick - min_onTick));
if (bucket < 0) bucket = 0;
if (bucket >= buckets) bucket = buckets - 1;
buckets_offTick[bucket]++;
}
for (auto &frametime : this->times_totalframe_onTick) {
int bucket = (int)((frametime - min_onTick) * buckets / (max_onTick - min_onTick));
if (bucket < 0) bucket = 0;
if (bucket >= buckets) bucket = buckets - 1;
buckets_onTick[bucket]++;
}

std::vector<int> buckets_offTick(buckets);
std::vector<int> buckets_onTick(buckets);
for (auto &frametime : this->times_totalframe_offTick) {
int bucket = (int)((frametime - min_onTick) * buckets / (max_onTick - min_onTick));
if (bucket < 0) bucket = 0;
if (bucket >= buckets) bucket = buckets - 1;
buckets_offTick[bucket]++;
}
for (auto &frametime : this->times_totalframe_onTick) {
int bucket = (int)((frametime - min_onTick) * buckets / (max_onTick - min_onTick));
if (bucket < 0) bucket = 0;
if (bucket >= buckets) bucket = buckets - 1;
buckets_onTick[bucket]++;
// draw buckets below text
int width = surface->GetFontLength(font, "frametime (on tick): min 33.333ms"); // about half the length of the text
for (int i = 0; i < buckets; i++) {
int left = x + i * width / buckets;
int right = x + (i + 1) * width / buckets;
if (this->times_totalframe_onTick.size() != 0) {
int height_onTick = buckets_onTick[i] * 100 / this->times_totalframe_onTick.size();
surface->DrawRect({0, 255, 0, 255}, left, y, right, y + 2 + height_onTick);
}
if (this->times_totalframe_offTick.size() != 0) {
int height_offTick = buckets_offTick[i] * 100 / this->times_totalframe_offTick.size();
surface->DrawRect({255, 0, 0, 255}, left, y, right, y + 2 + height_offTick);
}
}
y += 100;
}

// draw buckets below text
int width = surface->GetFontLength(font, "frametime (on tick): min 33.333ms"); // about half the length of the text
for (int i = 0; i < buckets; i++) {
int left = x + i * width / buckets;
int right = x + (i + 1) * width / buckets;
if (this->times_totalframe_onTick.size() != 0) {
int height_onTick = buckets_onTick[i] * 100 / this->times_totalframe_onTick.size();
surface->DrawRect({0, 255, 0, 255}, left, y, right, y + 2 + height_onTick);
}
if (this->times_totalframe_offTick.size() != 0) {
int height_offTick = buckets_offTick[i] * 100 / this->times_totalframe_offTick.size();
surface->DrawRect({255, 0, 0, 255}, left, y, right, y + 2 + height_offTick);
if (this->times_vgui_elements.size() > 0) {
surface->DrawTxt(font, x, y, {255, 255, 255}, "vgui elements:");
y += lineHeight;
for (auto &elem : this->times_vgui_elements) {
// mean
float mean = 0;
for (auto &frametime : elem.second) {
mean += frametime;
}
mean /= elem.second.size();
surface->DrawTxt(font, x, y, {255, 255, 255}, "\t%s: %.3fms", elem.first.c_str(), mean * 1000);
y += lineHeight;
}
}
}
Expand Down Expand Up @@ -160,6 +177,19 @@ void PerformanceHud::AddMetric(std::vector<float> &type, float time) {
}
}

void PerformanceHud::AddVGuiMetric(std::string type, float time) {
if (!sar_performance_hud.GetBool()) {
if (this->times_vgui_elements.size() > 0) {
this->times_vgui_elements.clear();
}
return;
}
this->times_vgui_elements[type].push_back(time);
while (this->times_vgui_elements[type].size() > (unsigned)sar_performance_hud_duration_vgui.GetInt()) {
this->times_vgui_elements[type].erase(this->times_vgui_elements[type].begin());
}
}

static std::chrono::high_resolution_clock::time_point preTick;
static std::chrono::high_resolution_clock::time_point postTick;

Expand Down
4 changes: 4 additions & 0 deletions src/Features/Hud/PerformanceHud.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "Hud.hpp"
#include "Modules/Scheme.hpp"

#include <map>

class PerformanceHud : public Hud {
public:
PerformanceHud()
Expand All @@ -16,6 +18,7 @@ class PerformanceHud : public Hud {
}

void AddMetric(std::vector<float> &type, float frametime);
void AddVGuiMetric(std::string type, float frametime);
void OnFrame(float frametime);

unsigned accum_ticks = 0;
Expand All @@ -25,6 +28,7 @@ class PerformanceHud : public Hud {
std::vector<float> times_vgui;
std::vector<float> times_preTick;
std::vector<float> times_postTick;
std::map<std::string, std::vector<float>> times_vgui_elements;
};

extern PerformanceHud *performanceHud;
4 changes: 4 additions & 0 deletions src/Modules/VGui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ Variable sar_hud_orange_only("sar_hud_orange_only", "0", "Only display the SAR H

void VGui::Draw(Hud *const &hud) {
if (hud->ShouldDraw()) {
auto start = NOW();
hud->Paint(this->context.slot);
performanceHud->AddVGuiMetric(std::string("hud ") + typeid(*hud).name(), std::chrono::duration_cast<std::chrono::microseconds>(NOW() - start).count() / 1000000.0f);
}
}
void VGui::Draw(HudElement *const &element) {
if (element->ShouldDraw()) {
auto start = NOW();
element->Paint(&this->context);
performanceHud->AddVGuiMetric(std::string("element ") + element->ElementName(), std::chrono::duration_cast<std::chrono::microseconds>(NOW() - start).count() / 1000000.0f);
}
}

Expand Down

0 comments on commit 2b2f6f8

Please sign in to comment.