aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Oliver <contact@pauloliver.dev>2026-06-09 15:32:05 +0200
committerPaul Oliver <contact@pauloliver.dev>2026-06-11 05:49:12 +0200
commit5d46c96cd93460b4d00eadf3b0237824ca297284 (patch)
tree41b9b77be530c6420a63e392ecb73d55d3e10609
parentcf7daaf435538c9848dfb0fb8dd0e5c9a4c74af2 (diff)
Adds final refinements to data client & server
-rw-r--r--core/client.cpp738
-rw-r--r--core/server.c1
-rwxr-xr-xsalis.py5
3 files changed, 489 insertions, 255 deletions
diff --git a/core/client.cpp b/core/client.cpp
index 9883f50..f24b2dd 100644
--- a/core/client.cpp
+++ b/core/client.cpp
@@ -1,4 +1,29 @@
+// [SECTION] Includes
+// [SECTION] Defines
+// [SECTION] Status enum
+// [SECTION] String comparator declaration
+// [SECTION] Trace declarations
+// [SECTION] Plot declarations
+// [SECTION] Plots
+// [SECTION] Heatmap colormap
+// [SECTION] Global variables
+// [SECTION] String comparator definition
+// [SECTION] Trace (base) definition
+// [SECTION] TraceNamed definition
+// [SECTION] TraceHeatmap definition
+// [SECTION] Plot (base) definition
+// [SECTION] PlotLines definition
+// [SECTION] PlotStacked definition
+// [SECTION] PlotHeatmap definition
+// [SECTION] Data functions
+// [SECTION] GUI functions
+// [SECTION] Main functions
+
+// ----------------------------------------------------------------------------
+// [SECTION] Includes
+// ----------------------------------------------------------------------------
#include <arpa/inet.h>
+#include <GL/glu.h>
#include <GLFW/glfw3.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
@@ -16,7 +41,7 @@
#include "logger.c"
// ----------------------------------------------------------------------------
-// Defines
+// [SECTION] Defines
// ----------------------------------------------------------------------------
#define COLOR_BLACK ImVec4(0.f, 0.f, 0.f, 1.f)
#define FONT_SIZE 12.f
@@ -52,20 +77,27 @@
#define DEFVAL_HM_PIXEL_COUNT 0x400l // must equal HM_PIXEL_COUNT in server.c
// ----------------------------------------------------------------------------
-// State declaration
+// [SECTION] Status enum
// ----------------------------------------------------------------------------
enum Status {
STATUS_STOPPED,
STATUS_RUNNING,
+ STATUS_FETCHING,
STATUS_STOPPING,
};
// ----------------------------------------------------------------------------
-// Trace declarations
+// [SECTION] String comparator declaration
+// ----------------------------------------------------------------------------
+struct StringComparator {
+ bool operator()(const char *a, const char *b) const;
+};
+
+// ----------------------------------------------------------------------------
+// [SECTION] Trace declarations
// ----------------------------------------------------------------------------
template <class T>
-class Trace {
-public:
+struct Trace : public std::vector<T> {
Trace();
virtual ~Trace();
virtual size_t start_offset() const;
@@ -73,37 +105,28 @@ public:
#if !defined(NDEBUG)
virtual void validate() const;
#endif
-
- size_t size() const;
- T &back();
- T &operator[](size_t n);
+ void clear();
+ void push_back(T value);
T *start();
- T max() const;
+ T &operator[](size_t n);
T operator[](size_t n) const;
- void clear();
- void push_back(T);
-
protected:
- void trim_spec(int mult = 1);
-
- std::vector<T> m_data;
- size_t m_max_index;
+ void trim_spec(int64_t multiplier = 1);
};
template <class T>
-class TraceNamed : public Trace<T> {
-public:
- TraceNamed(const char *name, const char *name_fmt);
-
+struct TraceNamed : public Trace<T> {
+ TraceNamed(const char *name, const char *label);
+ const char *get_name() const;
+ const char *get_label() const;
+private:
const char *m_name;
- const char *m_name_fmt;
+ const char *m_label;
};
template <class T>
-class TraceHeatmap : public TraceNamed<T> {
-public:
- TraceHeatmap(const char *name, const char *name_fmt);
-
+struct TraceHeatmap : public TraceNamed<T> {
+ TraceHeatmap(const char *name, const char *label);
size_t start_offset() const;
void trim();
#if !defined(NDEBUG)
@@ -112,67 +135,71 @@ public:
};
// ----------------------------------------------------------------------------
-// Plot declarations
+// [SECTION] Plot declarations
// ----------------------------------------------------------------------------
typedef int (*AxisFormatter)(double value, char *buff, int size, void *data);
-class Plot {
-public:
+struct Plot {
Plot(const char *name, const char *section);
virtual ~Plot();
- void render(ImVec2 frame_size);
-
+ virtual void flag_for_reset();
+ const char *get_name() const;
+ const char *get_section() const;
+ void render(const ImVec2 &frame_size);
+ bool m_visible;
+private:
+ static int hex_formatter(double value, char *buff, int size, void *data);
+ virtual AxisFormatter x_formatter() const;
+ virtual AxisFormatter y_formatter() const;
+ virtual float right_margin() const;
+ virtual int flags() const;
+ virtual void render_internal(const ImVec2 &frame_size) = 0;
+ virtual void render_post(const ImVec2 &frame_size);
const char *m_name;
const char *m_section;
-
-private:
- static int hex_axis_formatter(double value, char *buff, int size, void *data);
- virtual AxisFormatter x_axis_formatter();
- virtual AxisFormatter y_axis_formatter();
- virtual float frame_right_margin();
- virtual int plot_flags();
- virtual void render_internal() = 0;
- virtual void render_post(ImVec2 frame_size);
};
-class PlotLines : public Plot {
-public:
+struct PlotLines : public Plot {
PlotLines(const char *name, const char *section, std::vector<const char *> trace_keys);
- std::vector<const char *> m_trace_keys;
-
private:
- void render_internal();
+ void render_internal(const ImVec2 &frame_size);
+ std::vector<const char *> m_trace_keys;
};
-class PlotStacked : public Plot {
-public:
+struct PlotStacked : public Plot {
PlotStacked(const char *name, const char *section, std::vector<const char *> trace_keys);
-
- std::vector<ImS64> m_totals;
- std::vector<double> m_old_trace;
- std::vector<double> m_new_trace;
- std::vector<const char *> m_trace_keys;
-
+ void flag_for_reset();
private:
- AxisFormatter y_axis_formatter();
- static int percent_axis_formatter(double value, char *buff, int size, void *data);
- void render_internal();
+ static int percent_formatter(double value, char *buff, int size, void *data);
+ AxisFormatter y_formatter() const;
+ void render_internal(const ImVec2 &frame_size);
+ std::vector<const char *> m_trace_keys;
+ std::vector<bool> m_trace_states;
+ Trace<ImS64> m_trace_totals;
+ std::vector<Trace<double>> m_trace_normals;
+ bool m_needs_reset;
};
-class PlotHeatmap : public Plot {
-public:
+struct PlotHeatmap : public Plot {
PlotHeatmap(const char *name, const char *section, const char *trace_key);
- const char *m_trace_key;
-
+ void flag_for_reset();
private:
- float frame_right_margin();
- int plot_flags();
- void render_internal();
- void render_post(ImVec2 frame_size);
+ float right_margin() const;
+ int flags() const;
+ void render_internal(const ImVec2 &frame_size);
+ void render_end(const ImVec2 &frame_size);
+ void render_post(const ImVec2 &frame_size);
+ const char *m_trace_key;
+ float m_tex_scale_high;
+ size_t m_tex_rendered_last;
+ size_t m_rows_rendered;
+ GLuint m_tex_id;
+ std::vector<uint8_t> m_tex_render;
+ bool m_needs_reset;
};
// ----------------------------------------------------------------------------
-// Plots
+// [SECTION] Plots
// ----------------------------------------------------------------------------
#include "arch_plots.cpp"
@@ -253,9 +280,9 @@ std::array g_core_plots_heatmaps = std::to_array<PlotHeatmap>({
});
// ----------------------------------------------------------------------------
-// Heatmap colormap
+// [SECTION] Heatmap colormap
// ----------------------------------------------------------------------------
-std::array g_hm_colormap_cols = std::to_array<ImVec4>({
+std::array g_hm_colormap = std::to_array<ImVec4>({
{0.000f, 0.000f, 0.016f, 1.f},
{0.106f, 0.047f, 0.255f, 1.f},
{0.290f, 0.047f, 0.420f, 1.f},
@@ -269,14 +296,13 @@ std::array g_hm_colormap_cols = std::to_array<ImVec4>({
});
// ----------------------------------------------------------------------------
-// Globals
+// [SECTION] Global variables
// ----------------------------------------------------------------------------
GLFWwindow *g_window;
ImGuiIO *g_imgui_io;
ImGuiStyle *g_imgui_style;
ImPlotStyle *g_implot_style;
-// Data
std::array g_x_axes = std::to_array<const char *>({
"rowid",
"step",
@@ -295,14 +321,12 @@ int64_t g_hm_left = DEFVAL_HM_LEFT;
int64_t g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT;
int64_t g_hm_pixel_pow; // calculate on init
int64_t g_x_current = -1l;
-
-int g_trace_len;
-int g_trace_offset;
+int64_t g_trace_len;
+int64_t g_trace_offset;
thrd_t g_fetching_thread;
mtx_t g_fetching_mutex;
-// Layout
bool g_data_col_visible = true;
bool g_plot_maximized;
bool g_plot_scroll;
@@ -316,133 +340,122 @@ std::vector<bool> g_plots_covered;
Plot *g_plot_selected;
Plot *g_plot_hovered;
int g_plot_cols = 2;
-int g_plot_col_selected;
-int g_plot_row_selected;
+size_t g_plot_col_selected;
+size_t g_plot_row_selected;
float g_plot_height = 300.f;
float g_hm_scale_pow = HM_SCALE_POW_MIN;
-ImPlotColormap g_hm_colormap;
+ImPlotColormap g_hm_colormap_id;
-// Plots
-struct CompStr {
- bool operator()(const char *a, const char *b) const {
- return strcmp(a, b) < 0;
- }
-};
-
-std::map<const char *, TraceNamed<ImS64> *, CompStr> g_trace_map;
+std::map<const char *, TraceNamed<ImS64> *, StringComparator> g_trace_map;
std::vector<TraceNamed<ImS64> *> g_traces;
std::vector<Plot *> g_plots;
-Trace<double> g_x_axis_normal;
+Trace<double> g_x_axis_double;
+Trace<double> g_zero_trace;
+
+PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData;
+
+// ----------------------------------------------------------------------------
+// [SECTION] String comparator definition
+// ----------------------------------------------------------------------------
+bool StringComparator::operator()(const char *a, const char *b) const {
+ return strcmp(a, b) < 0;
+}
// ----------------------------------------------------------------------------
-// Trace definitions
+// [SECTION] Trace (base) definition
// ----------------------------------------------------------------------------
template <class T>
-Trace<T>::Trace() : m_data({}), m_max_index(0) {}
+Trace<T>::Trace() : std::vector<T>() {}
template <class T>
Trace<T>::~Trace() {}
template <class T>
size_t Trace<T>::start_offset() const {
- return g_trace_offset;
+ return (size_t)g_trace_offset;
}
template <class T>
void Trace<T>::trim() {
- trim_spec(1);
+ trim_spec();
}
#if !defined(NDEBUG)
template <class T>
void Trace<T>::validate() const {
- assert(size() == g_traces[0]->size());
+ assert(this->size() == g_traces[0]->size());
}
#endif
template <class T>
-size_t Trace<T>::size() const {
- return m_data.size();
-}
-
-template <class T>
-T &Trace<T>::back() {
- return m_data.back();
+void Trace<T>::clear() {
+ std::vector<T>::clear();
}
template <class T>
-T &Trace<T>::operator[](size_t n) {
-#if !defined(NDEBUG)
- return m_data.at(n);
-#else
- return m_data[n];
-#endif
+void Trace<T>::push_back(T value) {
+ std::vector<T>::push_back(value);
}
template <class T>
T *Trace<T>::start() {
- return size() ? &operator[](start_offset()) : nullptr;
+ return this->size() ? &operator[](start_offset()) : nullptr;
}
template <class T>
-T Trace<T>::max() const {
- return size() ? operator[](m_max_index) : 0;
+T &Trace<T>::operator[](size_t n) {
+#if !defined(NDEBUG)
+ return this->at(n);
+#else
+ return std::vector<T>::operator[](n);
+#endif
}
template <class T>
T Trace<T>::operator[](size_t n) const {
#if !defined(NDEBUG)
- return m_data.at(n);
+ return this->at(n);
#else
- return m_data[n];
+ return std::vector<T>::operator[](n);
#endif
}
template <class T>
-void Trace<T>::clear() {
- m_data.clear();
- m_max_index = 0;
+void Trace<T>::trim_spec(int64_t multiplier) {
+ assert((int64_t)this->size() >= g_entries * multiplier * 2);
+ this->erase(this->begin(), this->end() - (g_entries * multiplier));
}
+// ----------------------------------------------------------------------------
+// [SECTION] TraceNamed definition
+// ----------------------------------------------------------------------------
template <class T>
-void Trace<T>::push_back(T value) {
- m_data.push_back(value);
-
- if (value > operator[](m_max_index)) {
- m_max_index = size() - 1;
- }
-}
+TraceNamed<T>::TraceNamed(const char *name, const char *label) : m_name(name), m_label(label) {}
template <class T>
-void Trace<T>::trim_spec(int mult) {
- assert((int64_t)size() >= g_entries * mult * 2);
- m_data.erase(m_data.begin(), m_data.end() - (g_entries * mult));
-
- for (size_t i = 0; i < size(); i++) {
- m_max_index = std::max(operator[](m_max_index), operator[](i));
- }
+const char *TraceNamed<T>::get_name() const {
+ return m_name;
}
-// ----------------------------------------------------------------------------
-// TraceNamed definitions
-// ----------------------------------------------------------------------------
template <class T>
-TraceNamed<T>::TraceNamed(const char *name, const char *name_fmt) : m_name(name), m_name_fmt(name_fmt) {}
+const char *TraceNamed<T>::get_label() const {
+ return m_label;
+}
// ----------------------------------------------------------------------------
-// TraceHeatmap definitions
+// [SECTION] TraceHeatmap definition
// ----------------------------------------------------------------------------
template <class T>
TraceHeatmap<T>::TraceHeatmap(const char *name, const char *name_fmt) : TraceNamed<T>(name, name_fmt) {}
template <class T>
size_t TraceHeatmap<T>::start_offset() const {
- return g_trace_offset * g_hm_pixel_count;
+ return (size_t)(g_trace_offset * g_hm_pixel_count);
}
template <class T>
void TraceHeatmap<T>::trim() {
- this->trim_spec(g_hm_pixel_count);
+ Trace<T>::trim_spec(g_hm_pixel_count);
}
#if !defined(NDEBUG)
@@ -453,152 +466,304 @@ void TraceHeatmap<T>::validate() const {
#endif
// ----------------------------------------------------------------------------
-// Plot definitions
+// [SECTION] Plot (base) definition
// ----------------------------------------------------------------------------
-Plot::Plot(const char *name, const char *section) : m_name(name), m_section(section) {}
+Plot::Plot(const char *name, const char *section) : m_visible(true), m_name(name), m_section(section) {}
Plot::~Plot() {}
-void Plot::render(ImVec2 frame_size) {
- if (ImPlot::BeginPlot(m_name, ImVec2(frame_size.x - frame_right_margin(), frame_size.y), plot_flags())) {
+void Plot::flag_for_reset() {}
+
+const char *Plot::get_name() const {
+ return m_name;
+}
+
+const char *Plot::get_section() const {
+ return m_section;
+}
+
+void Plot::render(const ImVec2 &frame_size) {
+ if (ImPlot::BeginPlot(m_name, ImVec2(frame_size.x - right_margin(), frame_size.y), flags())) {
int axis_flags = ImPlotAxisFlags_Foreground | (g_status != STATUS_STOPPED ? ImPlotAxisFlags_AutoFit : 0);
ImPlot::SetupAxes(nullptr, nullptr, axis_flags, axis_flags);
- ImPlot::SetupAxisFormat(ImAxis_X1, x_axis_formatter());
- ImPlot::SetupAxisFormat(ImAxis_Y1, y_axis_formatter());
+ ImPlot::SetupAxisFormat(ImAxis_X1, x_formatter());
+ ImPlot::SetupAxisFormat(ImAxis_Y1, y_formatter());
if (ImPlot::IsPlotHovered()) g_plot_hovered = this;
- render_internal();
+ render_internal(frame_size);
ImPlot::EndPlot();
}
render_post(frame_size);
}
-int Plot::hex_axis_formatter(double value, char *buff, int size, void *data) {
+int Plot::hex_formatter(double value, char *buff, int size, void *data) {
(void)data;
snprintf(buff, size, "%s%#lx", value < 0. ? "-" : "", abs((int64_t)value));
return 0;
}
-AxisFormatter Plot::x_axis_formatter() {
- return Plot::hex_axis_formatter;
+AxisFormatter Plot::x_formatter() const {
+ return Plot::hex_formatter;
}
-AxisFormatter Plot::y_axis_formatter() {
- return Plot::hex_axis_formatter;
+AxisFormatter Plot::y_formatter() const {
+ return Plot::hex_formatter;
}
-float Plot::frame_right_margin() {
+float Plot::right_margin() const {
return 0.f;
}
-int Plot::plot_flags() {
+int Plot::flags() const {
return 0;
}
-void Plot::render_post(ImVec2 frame_size) {
+void Plot::render_post(const ImVec2 &frame_size) {
(void)frame_size;
}
// ----------------------------------------------------------------------------
-// PlotLines definitions
+// [SECTION] PlotLines definition
// ----------------------------------------------------------------------------
PlotLines::PlotLines(const char *name, const char *section, std::vector<const char *> trace_keys) : Plot(name, section), m_trace_keys(trace_keys) {}
-void PlotLines::render_internal() {
+void PlotLines::render_internal(const ImVec2 &frame_size) {
+ (void)frame_size;
+
ImS64 *x = g_trace_map[g_x_axes[g_x_axis]]->start();
for (auto &trace : m_trace_keys) {
TraceNamed<ImS64> *trace_obj = g_trace_map[trace];
ImS64 *y = trace_obj->start();
- ImPlot::PlotLine(trace_obj->m_name_fmt, x, y, g_trace_len);
+ ImPlot::PlotLine(trace_obj->get_label(), x, y, g_trace_len);
}
}
// ----------------------------------------------------------------------------
-// PlotStacked definitions
+// [SECTION] PlotStacked definition
// ----------------------------------------------------------------------------
-PlotStacked::PlotStacked(const char *name, const char *section, std::vector<const char *> trace_keys) : Plot(name, section), m_trace_keys(trace_keys) {}
+PlotStacked::PlotStacked(const char *name, const char *section, std::vector<const char *> trace_keys) : Plot(name, section), m_trace_keys(trace_keys), m_trace_states(trace_keys.size(), true), m_trace_totals(), m_trace_normals(trace_keys.size()), m_needs_reset(true) {}
-AxisFormatter PlotStacked::y_axis_formatter() {
- return PlotStacked::percent_axis_formatter;
+void PlotStacked::flag_for_reset() {
+ m_needs_reset = true;
}
-int PlotStacked::percent_axis_formatter(double value, char *buff, int size, void *data) {
+int PlotStacked::percent_formatter(double value, char *buff, int size, void *data) {
(void)data;
snprintf(buff, size, "%3.0f%%", value * 100.);
return 0;
}
-void PlotStacked::render_internal() {
- m_totals.resize(g_trace_len);
- m_old_trace.resize(g_trace_len);
- m_new_trace.resize(g_trace_len);
+AxisFormatter PlotStacked::y_formatter() const {
+ return PlotStacked::percent_formatter;
+}
+
+void PlotStacked::render_internal(const ImVec2 &frame_size) {
+ (void)frame_size;
for (size_t i = 0; i < m_trace_keys.size(); i++) {
- ImPlot::PlotDummy(g_trace_map[m_trace_keys[i]]->m_name_fmt);
+ ImPlot::PlotDummy(g_trace_map[m_trace_keys[i]]->get_label());
}
- for (int i = 0; i < (int)m_trace_keys.size(); i++) {
- if (GImPlot->CurrentPlot->Items.GetLegendItem(i)->Show) {
- for (int j = 0; j < g_trace_len; j++) {
- m_totals[j] += g_trace_map[m_trace_keys[i]]->start()[j];
+ for (size_t i = 0; i < m_trace_keys.size(); i++) {
+ bool trace_visible = GImPlot->CurrentPlot->Items.GetLegendItem(i)->Show;
+
+ if (m_trace_states[i] != trace_visible) {
+ m_trace_states[i] = trace_visible;
+ m_needs_reset = true;
+ }
+ }
+
+ if (m_needs_reset) {
+ m_trace_totals.clear();
+ for (auto &trace_normal : m_trace_normals) trace_normal.clear();
+ m_needs_reset = false;
+ }
+
+ size_t trace_size = m_trace_totals.size();
+
+ for (size_t i = trace_size; i < g_traces[0]->size(); i++) {
+ m_trace_totals.push_back(0l);
+
+ for (size_t j = 0; j < m_trace_keys.size(); j++) {
+ if (GImPlot->CurrentPlot->Items.GetLegendItem(j)->Show) {
+ m_trace_totals.back() += g_trace_map[m_trace_keys[j]]->operator[](i);
}
}
}
- for (int i = 0; i < (int)m_trace_keys.size(); i++) {
- if (GImPlot->CurrentPlot->Items.GetLegendItem(i)->Show) {
- ImPlotSpec spec = ImPlotSpec(ImPlotProp_FillAlpha, GImPlot->CurrentPlot->Items.GetLegendItem(i)->LegendHovered ? 1.f : 0.9f);
- TraceNamed<ImS64> *trace = g_trace_map[m_trace_keys[i]];
- const char *trace_name = trace->m_name_fmt;
+ for (size_t i = trace_size; i < g_traces[0]->size(); i++) {
+ if (m_trace_totals[i]) {
+ double normal = 0.;
- for (int j = 0; j < g_trace_len; j++) {
- m_new_trace[j] = m_totals[j] ? (m_old_trace[j] + (double)trace->start()[j] / (double)m_totals[j]) : 0.;
+ for (size_t j = 0; j < m_trace_keys.size(); j++) {
+ if (GImPlot->CurrentPlot->Items.GetLegendItem(j)->Show) {
+ normal += (double)g_trace_map[m_trace_keys[j]]->operator[](i) / (double)m_trace_totals[i];
+ m_trace_normals[j].push_back(normal);
+ }
}
+ } else {
+ for (size_t j = 0; j < m_trace_keys.size(); j++) {
+ if (GImPlot->CurrentPlot->Items.GetLegendItem(j)->Show) {
+ m_trace_normals[j].push_back(0.);
+ }
+ }
+ }
+ }
+
+#if !defined(NDEBUG)
+ m_trace_totals.validate();
- ImPlot::PlotShaded(trace_name, g_x_axis_normal.start(), m_old_trace.data(), m_new_trace.data(), g_trace_len, spec);
- ImPlot::PlotLine(trace_name, g_x_axis_normal.start(), m_old_trace.data(), g_trace_len);
- ImPlot::PlotLine(trace_name, g_x_axis_normal.start(), m_new_trace.data(), g_trace_len);
- std::swap(m_old_trace, m_new_trace);
+ for (size_t i = 0; i < m_trace_keys.size(); i++) {
+ if (GImPlot->CurrentPlot->Items.GetLegendItem(i)->Show) {
+ m_trace_normals[i].validate();
+ } else {
+ assert(m_trace_normals[i].empty());
}
}
+#endif
+
+ Trace<double> *prev_trace = &g_zero_trace;
- m_totals.clear();
- m_old_trace.clear();
- m_new_trace.clear();
+ for (size_t i = 0; i < m_trace_keys.size(); i++) {
+ assert(m_trace_states[i] == GImPlot->CurrentPlot->Items.GetLegendItem(i)->Show);
+
+ if (m_trace_states[i]) {
+ ImPlotSpec spec = ImPlotSpec(ImPlotProp_FillAlpha, GImPlot->CurrentPlot->Items.GetLegendItem(i)->LegendHovered ? 1.f : 0.9f);
+ const char *trace_name = g_trace_map[m_trace_keys[i]]->get_label();
+ Trace<double> *current_trace = &m_trace_normals[i];
+ ImPlot::PlotShaded(trace_name, g_x_axis_double.start(), prev_trace->start(), current_trace->start(), g_trace_len, spec);
+ ImPlot::PlotLine(trace_name, g_x_axis_double.start(), prev_trace->start(), g_trace_len);
+ ImPlot::PlotLine(trace_name, g_x_axis_double.start(), current_trace->start(), g_trace_len);
+ prev_trace = current_trace;
+ }
+ }
}
// ----------------------------------------------------------------------------
-// PlotHeatmap definitions
+// [SECTION] PlotHeatmap definition
// ----------------------------------------------------------------------------
-PlotHeatmap::PlotHeatmap(const char *name, const char *section, const char *trace_key) : Plot(name, section), m_trace_key(trace_key) {}
+PlotHeatmap::PlotHeatmap(const char *name, const char *section, const char *trace_key) : Plot(name, section), m_trace_key(trace_key), m_tex_scale_high(0.f), m_tex_rendered_last(0), m_rows_rendered(0), m_tex_id(0), m_tex_render(), m_needs_reset(true) {}
-float PlotHeatmap::frame_right_margin() {
+void PlotHeatmap::flag_for_reset() {
+ m_needs_reset = true;
+}
+
+float PlotHeatmap::right_margin() const {
return HM_COLORSCALE_WIDTH;
}
-int PlotHeatmap::plot_flags() {
+int PlotHeatmap::flags() const {
return ImPlotFlags_NoLegend;
}
-void PlotHeatmap::render_internal() {
- TraceNamed<ImS64> *trace = g_trace_map[m_trace_key];
- ImPlot::PushColormap(g_hm_colormap);
- double scale_max = g_hm_scale_pow == HM_SCALE_POW_MIN ? 0. : pow(2., (double)g_hm_scale_pow);
- ImPlot::PlotHeatmap(trace->m_name_fmt, trace->start(), g_trace_len, g_hm_pixel_count, 0., scale_max, nullptr, ImPlotPoint(0, g_x_current), ImPlotPoint(MVEC_SIZE, 0));
- ImPlot::PopColormap();
+void PlotHeatmap::render_internal(const ImVec2 &frame_size) {
+ Trace<ImS64> *trace = g_trace_map[m_trace_key];
+
+ if (m_needs_reset) {
+ m_tex_scale_high = 0.f;
+ m_tex_rendered_last = trace->start_offset();
+ m_rows_rendered = 0;
+ glDeleteTextures(1, &m_tex_id);
+ m_tex_id = 0;
+ m_needs_reset = false;
+ }
+
+ if (m_tex_rendered_last == trace->size()) {
+ render_end(frame_size);
+ return;
+ }
+
+ assert(m_tex_render.empty());
+ assert((trace->size() - m_tex_rendered_last) % g_hm_pixel_count == 0);
+ size_t rows_to_append = (trace->size() - m_tex_rendered_last) / (size_t)g_hm_pixel_count;
+ size_t rows_to_chop_off = (m_rows_rendered + rows_to_append) > (size_t)g_trace_len ? (m_rows_rendered + rows_to_append) - (size_t)g_trace_len : 0;
+
+ if (rows_to_chop_off > m_rows_rendered) {
+ m_needs_reset = true;
+ render_internal(frame_size);
+ return;
+ }
+
+ if (g_hm_scale_pow == HM_SCALE_POW_MIN) {
+ ImS64 max = 0;
+
+ for (size_t i = trace->start_offset(); i < trace->size(); i++) {
+ max = std::max(max, trace->operator[](i));
+ }
+
+ m_tex_scale_high = (float)max;
+ } else {
+ m_tex_scale_high = pow(2.f, g_hm_scale_pow);
+ }
+
+ m_tex_render.reserve((trace->size() - m_tex_rendered_last) * 3);
+
+ for (size_t i = m_tex_rendered_last; i < trace->size(); i++) {
+ ImS64 value = trace->operator[](i);
+ float normal = std::min((float)value / m_tex_scale_high, 1.f);
+ assert(normal >= 0.f && normal <= 1.f);
+ ImVec4 color = ImPlot::SampleColormap(normal, g_hm_colormap_id);
+ m_tex_render.push_back((uint8_t)(color.x * 255.f)); // red
+ m_tex_render.push_back((uint8_t)(color.y * 255.f)); // green
+ m_tex_render.push_back((uint8_t)(color.z * 255.f)); // blue
+ }
+
+ GLuint new_tex_id;
+ glGenTextures(1, &new_tex_id);
+ glBindTexture(GL_TEXTURE_2D, new_tex_id);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, g_hm_pixel_count, g_trace_len, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+ assert(glGetError() == GL_NO_ERROR);
+ assert(new_tex_id);
+
+ if (m_tex_id) {
+ glCopyImageSubData(
+ m_tex_id, GL_TEXTURE_2D, 0, 0, rows_to_chop_off, 0,
+ new_tex_id, GL_TEXTURE_2D, 0, 0, 0, 0,
+ g_hm_pixel_count, m_rows_rendered - rows_to_chop_off, 1
+ );
+ assert(glGetError() == GL_NO_ERROR);
+ glDeleteTextures(1, &m_tex_id);
+ }
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, m_rows_rendered - rows_to_chop_off, g_hm_pixel_count, rows_to_append, GL_RGB, GL_UNSIGNED_BYTE, m_tex_render.data());
+ assert(glGetError() == GL_NO_ERROR);
+
+ render_end(frame_size);
+
+ m_tex_rendered_last = trace->size();
+ m_rows_rendered += rows_to_append - rows_to_chop_off;
+ assert(m_rows_rendered <= (size_t)g_trace_len);
+ m_tex_id = new_tex_id;
+ m_tex_render.clear();
}
-void PlotHeatmap::render_post(ImVec2 frame_size) {
+void PlotHeatmap::render_end(const ImVec2 &frame_size) {
+ if (g_x_axis_double.empty()) return;
+
+ ImVec2 bmin(g_hm_left, (float)*g_x_axis_double.start());
+ ImVec2 bmax(g_hm_left + g_hm_pixel_count * pow(2.f, g_hm_pixel_pow), (float)g_x_axis_double.back());
+ ImVec2 uv0(0.f, 1.f);
+ ImVec2 uv1(1.f, 0.f);
+ double scale_max = g_hm_scale_pow == HM_SCALE_POW_MIN ? (double)m_tex_scale_high : pow(2., (double)g_hm_scale_pow);
+
+ ImPlot::PlotImage(get_name(), (ImTextureID)m_tex_id, bmin, bmax, uv0, uv1);
ImGui::SameLine();
- ImPlot::PushColormap(g_hm_colormap);
- double scale_max = g_hm_scale_pow == HM_SCALE_POW_MIN ? (double)g_trace_map[m_trace_key]->max() : pow(2., (double)g_hm_scale_pow);
+
+ ImPlot::PushColormap(g_hm_colormap_id);
ImPlot::ColormapScale("##hm-scale", 0., scale_max, ImVec2(HM_COLORSCALE_WIDTH, frame_size.y), "%.1e");
ImPlot::PopColormap();
}
+void PlotHeatmap::render_post(const ImVec2 &frame_size) {
+ (void)frame_size;
+}
+
// ----------------------------------------------------------------------------
-// Data functions
+// [SECTION] Data functions
// ----------------------------------------------------------------------------
int64_t data_max_hm_pixel_pow(void) {
return (int64_t)floor(log2((double)(MVEC_SIZE - g_hm_left) / (double)g_hm_pixel_count));
@@ -616,11 +781,13 @@ void data_on_field_change(void) {
g_hm_pixel_pow = std::clamp(g_hm_pixel_pow, 0l, data_max_hm_pixel_pow());
g_x_current = -1l;
- g_trace_len = 0;
- g_trace_offset = 0;
+ g_trace_len = 0l;
+ g_trace_offset = 0;;
for (auto &trace : g_traces) trace->clear();
- g_x_axis_normal.clear();
+ for (auto &plot: g_plots) plot->flag_for_reset();
+ g_x_axis_double.clear();
+ g_zero_trace.clear();
}
void data_reset_fields(void) {
@@ -643,6 +810,9 @@ void data_reset_plot_cells(void) {
}
void data_fetch(void) {
+ assert(g_status == STATUS_RUNNING);
+ g_status = STATUS_FETCHING;
+
json_object *request = json_object_new_object();
json_object_object_add(request, "request", json_object_new_string("data"));
json_object_object_add(request, "entries", json_object_new_int64(g_entries));
@@ -679,7 +849,8 @@ void data_fetch(void) {
for (size_t i = 0; i < new_rows; i++) {
ImS64 point = json_object_get_int64(json_object_array_get_idx(value, i));
- g_x_axis_normal.push_back((double)point);
+ g_x_axis_double.push_back((double)point);
+ g_zero_trace.push_back(0.);
}
}
@@ -697,15 +868,22 @@ void data_fetch(void) {
#if !defined(NDEBUG)
for (auto &trace : g_traces) trace->validate();
+ g_x_axis_double.validate();
+ g_zero_trace.validate();
#endif
if ((int64_t)g_traces[0]->size() >= g_entries * 2) {
+ log_info("Trimming traces & flagging plots for reset");
for (auto &trace : g_traces) trace->trim();
- g_x_axis_normal.trim();
+ for (auto &plot : g_plots) plot->flag_for_reset();
+ g_x_axis_double.trim();
+ g_zero_trace.trim();
}
#if !defined(NDEBUG)
for (auto &trace : g_traces) trace->validate();
+ g_x_axis_double.validate();
+ g_zero_trace.validate();
#endif
int64_t current_size = g_traces[0]->size();
@@ -713,13 +891,14 @@ void data_fetch(void) {
g_trace_offset = current_size > g_entries ? current_size - g_entries : 0l;
mtx_unlock(&g_fetching_mutex);
+ g_status = STATUS_RUNNING;
}
int data_fetching_thread(void *data) {
(void)data;
assert(!data);
- assert(g_status == STATUS_RUNNING || g_status == STATUS_STOPPING);
+ assert(g_status == STATUS_RUNNING);
while (g_status == STATUS_RUNNING) {
data_fetch();
@@ -741,14 +920,13 @@ void data_start_fetching(void) {
}
void data_stop_fetching(void) {
- assert(g_status == STATUS_RUNNING);
+ assert(g_status == STATUS_RUNNING || g_status == STATUS_FETCHING);
log_info("Stopping data fetching thread");
g_status = STATUS_STOPPING;
- thrd_join(g_fetching_thread, nullptr);
}
// ----------------------------------------------------------------------------
-// GUI functions
+// [SECTION] GUI functions
// ----------------------------------------------------------------------------
void gui_render_data_input(const char *label, int64_t *target) {
assert(target);
@@ -808,6 +986,7 @@ void gui_render_data_col(void) {
gui_render_data_input("hm-pxl-pow", &g_hm_pixel_pow);
break;
case STATUS_RUNNING:
+ case STATUS_FETCHING:
case STATUS_STOPPING:
ImGui::LabelText("entries", "%#lx", g_entries);
ImGui::LabelText("nth", "%#lx", g_nth);
@@ -821,23 +1000,21 @@ void gui_render_data_col(void) {
switch (g_status) {
case STATUS_STOPPED:
- if (ImGui::Button("Run", ImVec2(-1.f, 0.f))) {
- data_start_fetching();
- }
-
- if (ImGui::Button("Reset", ImVec2(-1.f, 0.f))) {
- data_reset_fields();
- }
-
+ if (ImGui::Button("Run", ImVec2(-1.f, 0.f))) data_start_fetching();
+ if (ImGui::Button("Reset", ImVec2(-1.f, 0.f))) data_reset_fields();
break;
case STATUS_RUNNING:
- if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) {
- data_stop_fetching();
- }
-
+ if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) data_stop_fetching();
ImGui::LabelText("##", "Running");
break;
+ case STATUS_FETCHING:
+ if (ImGui::Button("Stop", ImVec2(-1.f, 0.f))) data_stop_fetching();
+ ImGui::LabelText("##", "Fetching ...");
+ break;
case STATUS_STOPPING:
+ ImGui::BeginDisabled();
+ ImGui::Button("Stop", ImVec2(-1.f, 0.f));
+ ImGui::EndDisabled();
ImGui::LabelText("##", "Stopping");
break;
}
@@ -847,19 +1024,30 @@ void gui_render_data_col(void) {
ImGui::DragFloat("plot-height", &g_plot_height, PLOT_HEIGHT_INTERVAL, PLOT_MIN_HEIGHT, PLOT_MAX_HEIGHT, "%.0f");
ImGui::DragFloat("hm-pow", &g_hm_scale_pow, HM_SCALE_POW_INTERVAL, HM_SCALE_POW_MIN, HM_SCALE_POW_MAX, "%.0f");
+ ImGui::SeparatorText("Plots");
+ if (ImGui::Button("Show all", ImVec2(-1.f, 0.f))) for (auto &plot : g_plots) plot->m_visible = true;
+ if (ImGui::Button("Hide all", ImVec2(-1.f, 0.f))) for (auto &plot : g_plots) plot->m_visible = false;
+ ImGui::BeginTable("plot-visibility-table", 3);
+
+ for (auto &plot : g_plots) {
+ ImGui::TableNextColumn();
+ ImGui::Checkbox(plot->get_name(), &plot->m_visible);
+ }
+
+ ImGui::EndTable();
ImGui::End();
}
-int gui_plot_cell_index(int row, int col) {
- int index = row * PLOT_MAX_COLS + col;
- assert(index < (int)g_plot_cells.size());
- assert(index < (int)g_plot_cells_top.size());
- assert(index < (int)g_plot_cells_bottom.size());
+size_t gui_plot_cell_index(size_t row, size_t col) {
+ size_t index = row * PLOT_MAX_COLS + col;
+ assert(index < g_plot_cells.size());
+ assert(index < g_plot_cells_top.size());
+ assert(index < g_plot_cells_bottom.size());
return index;
}
-int gui_plot_cell_row_up() {
- for (int row = g_plot_row_selected - 1; row >= 0; row--) {
+size_t gui_plot_cell_row_up() {
+ for (size_t row = g_plot_row_selected - 1; row < g_plot_row_selected; row--) {
if (g_plot_cells[gui_plot_cell_index(row, g_plot_col_selected)]) {
return row;
}
@@ -868,8 +1056,8 @@ int gui_plot_cell_row_up() {
return g_plot_row_selected;
}
-int gui_plot_cell_row_down() {
- for (int row = g_plot_row_selected + 1; row < (int)g_plots.size(); row++) {
+size_t gui_plot_cell_row_down() {
+ for (size_t row = g_plot_row_selected + 1; row < g_plots.size(); row++) {
if (g_plot_cells[gui_plot_cell_index(row, g_plot_col_selected)]) {
return row;
}
@@ -879,7 +1067,7 @@ int gui_plot_cell_row_down() {
}
void gui_render_plots(void) {
- const char *section_current = g_plots[0]->m_section;
+ const char *section_current = g_plots[0]->get_section();
const char *section_next = nullptr;
g_plots_covered.clear();
@@ -901,8 +1089,20 @@ void gui_render_plots(void) {
g_plot_scroll_current = ImGui::GetScrollY();
g_plot_hovered = nullptr;
- int row = 0;
- int col = 0;
+ if (!g_plot_selected->m_visible) {
+ g_plot_selected = g_plots[0];
+ g_plot_col_selected = 0;
+ g_plot_row_selected = 0;
+
+ for (auto &plot : g_plots) {
+ if (plot->m_visible) {
+ g_plot_selected = plot;
+ }
+ }
+ }
+
+ size_t row = 0;
+ size_t col = 0;
mtx_lock(&g_fetching_mutex);
@@ -911,31 +1111,33 @@ void gui_render_plots(void) {
ImGui::BeginTable("plots-table", g_plot_cols);
for (size_t i = 0; i < g_plots.size(); i++) {
- if (strcmp(g_plots[i]->m_section, section_current)) {
- section_next = (!section_next && !g_plots_covered[i]) ? g_plots[i]->m_section : section_next;
+ if (strcmp(g_plots[i]->get_section(), section_current)) {
+ section_next = (!section_next && !g_plots_covered[i]) ? g_plots[i]->get_section() : section_next;
continue;
}
- ImGui::TableNextColumn();
- ImVec2 frame_size = ImVec2(ImGui::GetContentRegionAvail().x, g_plot_height);
- g_plot_cells[gui_plot_cell_index(row, col)] = g_plots[i];
- g_plot_cells_top[gui_plot_cell_index(row, col)] = ImGui::GetCursorPosY();
+ if (g_plots[i]->m_visible) {
+ ImGui::TableNextColumn();
+ ImVec2 frame_size = ImVec2(ImGui::GetContentRegionAvail().x, g_plot_height);
+ g_plot_cells[gui_plot_cell_index(row, col)] = g_plots[i];
+ g_plot_cells_top[gui_plot_cell_index(row, col)] = ImGui::GetCursorPosY();
- if (g_plots[i] == g_plot_selected) {
- g_plot_col_selected = col;
- g_plot_row_selected = row;
- g_implot_style->Colors[ImPlotCol_FrameBg] = g_imgui_style->Colors[ImGuiCol_FrameBg];
- }
+ if (g_plots[i] == g_plot_selected) {
+ g_plot_col_selected = col;
+ g_plot_row_selected = row;
+ g_implot_style->Colors[ImPlotCol_FrameBg] = g_imgui_style->Colors[ImGuiCol_FrameBg];
+ }
- g_plots[i]->render(frame_size);
+ g_plots[i]->render(frame_size);
- if (g_plots[i] == g_plot_selected) {
- g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK;
- }
+ if (g_plots[i] == g_plot_selected) {
+ g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK;
+ }
- g_plot_cells_bottom[gui_plot_cell_index(row, col)] = ImGui::GetCursorPosY();
- col = (col + 1) % g_plot_cols;
- row += col ? 0 : 1;
+ g_plot_cells_bottom[gui_plot_cell_index(row, col)] = ImGui::GetCursorPosY();
+ col = (col + 1) % g_plot_cols;
+ row += col ? 0 : 1;
+ }
g_plots_covered[i] = true;
}
@@ -948,7 +1150,6 @@ void gui_render_plots(void) {
}
mtx_unlock(&g_fetching_mutex);
-
ImGui::End();
}
@@ -963,17 +1164,18 @@ void gui_render_plot_maximized(void) {
viewport->Size.y - g_imgui_style->WindowPadding.y * 2,
};
+ mtx_lock(&g_fetching_mutex);
g_plot_selected->render(frame_size);
-
+ mtx_unlock(&g_fetching_mutex);
ImGui::End();
}
void gui_plot_queue_scroll_to_position(bool increased_plot_height) {
- int row = 0;
+ size_t row = 0;
float selected_plot_top = 0.f;
- for (row = 0; row < (int)g_plots.size(); row++) {
- for (int col = 0; col < PLOT_MAX_COLS; col++) {
+ for (row = 0; row < g_plots.size(); row++) {
+ for (size_t col = 0; col < PLOT_MAX_COLS; col++) {
if (g_plot_selected == g_plot_cells[gui_plot_cell_index(row, col)]) {
selected_plot_top = g_plot_cells_top[gui_plot_cell_index(row, col)];
goto loop_exit;
@@ -1014,14 +1216,14 @@ void gui_render(void) {
}
// ----------------------------------------------------------------------------
-// Main functions
+// [SECTION] Main functions
// ----------------------------------------------------------------------------
void app_sig_handler(int signo) {
(void)signo;
log_warn("Signal received, will stop SALIS data client...");
- if (g_status == STATUS_RUNNING) {
+ if (g_status == STATUS_RUNNING || g_status == STATUS_FETCHING) {
data_stop_fetching();
}
@@ -1038,6 +1240,7 @@ void app_toggle_state() {
data_start_fetching();
break;
case STATUS_RUNNING:
+ case STATUS_FETCHING:
data_stop_fetching();
break;
}
@@ -1052,9 +1255,18 @@ void app_key_callback_plot_maximized(int key, int mods) {
break;
case GLFW_KEY_COMMA:
g_hm_scale_pow = std::max(g_hm_scale_pow - HM_SCALE_POW_INTERVAL, HM_SCALE_POW_MIN);
+ for (auto &heatmap : g_core_plots_heatmaps) heatmap.flag_for_reset();
+ for (auto &heatmap : g_arch_plots_heatmaps) heatmap.flag_for_reset();
break;
case GLFW_KEY_PERIOD:
g_hm_scale_pow = std::min(g_hm_scale_pow + HM_SCALE_POW_INTERVAL, HM_SCALE_POW_MAX);
+ for (auto &heatmap : g_core_plots_heatmaps) heatmap.flag_for_reset();
+ for (auto &heatmap : g_arch_plots_heatmaps) heatmap.flag_for_reset();
+ break;
+ case GLFW_KEY_0:
+ g_hm_scale_pow = HM_SCALE_POW_MIN;
+ for (auto &heatmap : g_core_plots_heatmaps) heatmap.flag_for_reset();
+ for (auto &heatmap : g_arch_plots_heatmaps) heatmap.flag_for_reset();
break;
}
@@ -1118,9 +1330,18 @@ void app_key_callback(GLFWwindow* window, int key, int scancode, int action, int
break;
case GLFW_KEY_COMMA:
g_hm_scale_pow = std::max(g_hm_scale_pow - HM_SCALE_POW_INTERVAL, HM_SCALE_POW_MIN);
+ for (auto &heatmap : g_core_plots_heatmaps) heatmap.flag_for_reset();
+ for (auto &heatmap : g_arch_plots_heatmaps) heatmap.flag_for_reset();
break;
case GLFW_KEY_PERIOD:
g_hm_scale_pow = std::min(g_hm_scale_pow + HM_SCALE_POW_INTERVAL, HM_SCALE_POW_MAX);
+ for (auto &heatmap : g_core_plots_heatmaps) heatmap.flag_for_reset();
+ for (auto &heatmap : g_arch_plots_heatmaps) heatmap.flag_for_reset();
+ break;
+ case GLFW_KEY_0:
+ g_hm_scale_pow = HM_SCALE_POW_MIN;
+ for (auto &heatmap : g_core_plots_heatmaps) heatmap.flag_for_reset();
+ for (auto &heatmap : g_arch_plots_heatmaps) heatmap.flag_for_reset();
break;
}
@@ -1129,7 +1350,7 @@ void app_key_callback(GLFWwindow* window, int key, int scancode, int action, int
case 0:
switch (key) {
case GLFW_KEY_LEFT:
- g_plot_col_selected = std::max(g_plot_col_selected - 1, 0);
+ g_plot_col_selected -= g_plot_col_selected ? 1 : 0;
g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected)];
break;
case GLFW_KEY_RIGHT:
@@ -1147,7 +1368,7 @@ void app_key_callback(GLFWwindow* window, int key, int scancode, int action, int
gui_plot_queue_scroll_to_selected();
break;
case GLFW_KEY_F:
- g_plot_maximized = !g_plot_maximized;
+ if (g_plot_selected->m_visible) g_plot_maximized = !g_plot_maximized;
break;
case GLFW_KEY_SPACE:
app_toggle_state();
@@ -1188,7 +1409,14 @@ void init() {
glfwSetKeyCallback(g_window, app_key_callback);
glfwSetMouseButtonCallback(g_window, app_mouse_button_callback);
glfwMakeContextCurrent(g_window);
- glfwSwapInterval(1); // enable vsync
+#if defined(VSYNC)
+ glfwSwapInterval(1);
+#else
+ glfwSwapInterval(0);
+#endif
+
+ glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)glfwGetProcAddress("glCopyImageSubData");
+ assert(glCopyImageSubData);
log_info("Initializing ImGui");
IMGUI_CHECKVERSION();
@@ -1208,7 +1436,7 @@ void init() {
g_implot_style = &ImPlot::GetStyle();
g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK;
- g_hm_colormap = ImPlot::AddColormap("Inferno", g_hm_colormap_cols.data(), g_hm_colormap_cols.size(), false);
+ g_hm_colormap_id = ImPlot::AddColormap("heatmap", g_hm_colormap.data(), g_hm_colormap.size(), false);
ImGui_ImplGlfw_InitForOpenGL(g_window, true);
ImGui_ImplOpenGL3_Init(GLSL_VERSION);
@@ -1222,7 +1450,7 @@ void init() {
for (auto &plot : g_arch_plots_stacked) g_plots.push_back(&plot);
for (auto &plot : g_core_plots_heatmaps) g_plots.push_back(&plot);
for (auto &plot : g_arch_plots_heatmaps) g_plots.push_back(&plot);
- for (auto &i : g_traces) g_trace_map[i->m_name] = i;
+ for (auto &i : g_traces) g_trace_map[i->get_name()] = i;
g_plot_cells = std::vector<Plot *>(g_plots.size() * PLOT_MAX_COLS);
g_plot_cells_top = std::vector<float>(g_plots.size() * PLOT_MAX_COLS);
diff --git a/core/server.c b/core/server.c
index 292c3d9..2c52ea4 100644
--- a/core/server.c
+++ b/core/server.c
@@ -303,6 +303,7 @@ int main(void) {
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
+ signal(SIGPIPE, SIG_IGN); // ignore broken pipes
log_info("Creating response header");
g_response_header = json_object_new_object();
diff --git a/salis.py b/salis.py
index 1feb041..34795db 100755
--- a/salis.py
+++ b/salis.py
@@ -77,6 +77,7 @@ options = {
(("T", "keep-temp-dir"), (new, load, server, client), fmt_id): {"action": "store_true", "help": "keep temporary directory on exit", "required": False},
(("t", "thread-gap"), (new, load), fmt_hex): {"metavar": "N", "help": "memory gap between core elements in bytes; may help reduce cache misses", "default": 0x100, "required": False, "type": nat},
(("u", "ui"), (new, load), fmt_id): {"choices": uis, "help": "user interface", "default": "curses", "required": False, "type": str},
+ (("v", "no-vsync"), (client,), fmt_id): {"action": "store_true", "help": "disable vsync", "required": False},
(("x", "cpp-compiler"), (client,), fmt_id): {"metavar": "CXX", "help": "C++ compiler to use", "default": "g++", "required": False, "type": str},
(("X", "cpp-compiler-flags"), (client,), fmt_id): {"metavar": "FLAGS", "help": "base set of flags to pass to C++ compiler", "default": "-Wall -Wextra -Werror -pedantic -std=c++20 -lstdc++", "required": False, "type": str},
(("y", "sync-pow"), (new,), fmt_id): {"metavar": "POW", "help": "core sync interval exponent; sync events occur every N steps, where N = 2^{POW}", "default": 20, "required": False, "type": pos},
@@ -412,6 +413,10 @@ if args.command == "client":
ns.b = Build("core/client.cpp", log, cpp=True)
pop_net_vars()
pop_general()
+
+ if not args.no_vsync:
+ ns.b.defines.add("-DVSYNC")
+
ns.b.defines.add(f"-DIP=\"{args.ip}\"")
ns.b.links.add("-lGL")
ns.b.links.add("-lglfw")