// [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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "logger.c" // ---------------------------------------------------------------------------- // [SECTION] Defines // ---------------------------------------------------------------------------- #define COLOR_BLACK ImVec4(0.f, 0.f, 0.f, 1.f) #define FONT_SIZE 12.f #define GLSL_VERSION "#version 130" #define PLOT_MIN_COLS 1 #define PLOT_MAX_COLS 8 #define PLOT_MIN_HEIGHT 100.f #define PLOT_MAX_HEIGHT 800.f #define PLOT_HEIGHT_INTERVAL 50.f #define PLOT_SCROLL_MARGIN 28.f #define HM_SCALE_POW_MIN -1.f #define HM_SCALE_POW_MAX 64.f #define HM_SCALE_POW_INTERVAL 1.f #define HM_COLORSCALE_WIDTH 80.f #define IMGUI_WINDOW_FLAGS ( \ ImGuiWindowFlags_NoBackground | \ ImGuiWindowFlags_NoDecoration | \ ImGuiWindowFlags_NoMove | \ ImGuiWindowFlags_NoSavedSettings \ ) #define DATA_FETCH_INTERVAL 10 #define DATA_FETCH_INTERVAL_SUBDIV 1000 #define DEFVAL_ENTRIES 0x800l #define DEFVAL_NTH 1l #define DEFVAL_X_AXIS 0 #define DEFVAL_X_LOW 0l #define DEFVAL_X_HIGH INT64_MAX #define DEFVAL_HM_LEFT 0l #define DEFVAL_HM_PIXEL_COUNT 0x400l // must equal HM_PIXEL_COUNT in server.c // ---------------------------------------------------------------------------- // [SECTION] Status enum // ---------------------------------------------------------------------------- enum Status { STATUS_STOPPED, STATUS_RUNNING, STATUS_FETCHING, STATUS_STOPPING, }; // ---------------------------------------------------------------------------- // [SECTION] String comparator declaration // ---------------------------------------------------------------------------- struct StringComparator { bool operator()(const char *a, const char *b) const; }; // ---------------------------------------------------------------------------- // [SECTION] Trace declarations // ---------------------------------------------------------------------------- template struct Trace : public std::vector { Trace(); virtual ~Trace(); virtual size_t start_offset() const; virtual void trim(); #if !defined(NDEBUG) virtual void validate() const; #endif void clear(); void push_back(T value); T *start(); T &operator[](size_t n); T operator[](size_t n) const; protected: void trim_spec(int64_t multiplier = 1); }; template struct TraceNamed : public Trace { 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_label; }; template struct TraceHeatmap : public TraceNamed { TraceHeatmap(const char *name, const char *label); size_t start_offset() const; void trim(); #if !defined(NDEBUG) void validate() const; #endif }; // ---------------------------------------------------------------------------- // [SECTION] Plot declarations // ---------------------------------------------------------------------------- typedef int (*AxisFormatter)(double value, char *buff, int size, void *data); struct Plot { Plot(const char *name, const char *section); virtual ~Plot(); virtual void handle_input(int key, int mods); 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; }; struct PlotLines : public Plot { PlotLines(const char *name, const char *section, std::vector trace_keys); private: void render_internal(const ImVec2 &frame_size); std::vector m_trace_keys; }; struct PlotStacked : public Plot { PlotStacked(const char *name, const char *section, std::vector trace_keys); void flag_for_reset(); private: 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 m_trace_keys; std::vector m_trace_states; Trace m_trace_totals; std::vector> m_trace_normals; bool m_needs_reset; }; struct PlotHeatmap : public Plot { PlotHeatmap(const char *name, const char *section, const char *trace_key); void handle_input(int key, int mods); void flag_for_reset(); private: 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_pow; float m_tex_scale_high; size_t m_tex_rendered_last; size_t m_rows_rendered; GLuint m_tex_id; std::vector m_tex_render; bool m_needs_reset; }; // ---------------------------------------------------------------------------- // [SECTION] Plots // ---------------------------------------------------------------------------- #include "plots.cpp" std::array g_core_traces = std::to_array>({ {"rowid", "rowid"}, {"step", "step"}, #define FOR_CORE(i) \ {"cycl_" #i, "cycl_" #i}, \ {"mall_" #i, "mall_" #i}, \ {"pnum_" #i, "pnum_" #i}, \ {"pfst_" #i, "pfst_" #i}, \ {"plst_" #i, "plst_" #i}, \ {"amb0_" #i, "amb0_" #i}, \ {"amb1_" #i, "amb1_" #i}, \ {"emb0_" #i, "emb0_" #i}, \ {"emb1_" #i, "emb1_" #i}, \ {"eliv_" #i, "eliv_" #i}, \ {"edea_" #i, "edea_" #i}, FOR_CORES #undef FOR_CORE }); std::array g_core_traces_heatmaps = std::to_array>({ #define FOR_CORE(i) \ {"aev_" #i, "aev_" #i}, \ {"eev_" #i, "eev_" #i}, \ {"bev_" #i, "bev_" #i}, FOR_CORES #undef FOR_CORE }); std::array g_core_plots = std::to_array({ {"cycl", "general", { #define FOR_CORE(i) "cycl_" #i, FOR_CORES #undef FOR_CORE }}, {"mall", "general", { #define FOR_CORE(i) "mall_" #i, FOR_CORES #undef FOR_CORE }}, {"pnum", "general", { #define FOR_CORE(i) "pnum_" #i, FOR_CORES #undef FOR_CORE }}, {"ppop", "general", { #define FOR_CORE(i) "pfst_" #i, "plst_" #i, FOR_CORES #undef FOR_CORE }}, {"ambs", "general", { #define FOR_CORE(i) "amb0_" #i, "amb1_" #i, FOR_CORES #undef FOR_CORE }}, {"eevs", "general", { #define FOR_CORE(i) "emb0_" #i, "emb1_" #i, "eliv_" #i, "edea_" #i, FOR_CORES #undef FOR_CORE }}, }); std::array g_core_plots_heatmaps = std::to_array({ #define FOR_CORE(i) \ {"aev_" #i, "heatmaps", "aev_" #i}, FOR_CORES #undef FOR_CORE #define FOR_CORE(i) \ {"eev_" #i, "heatmaps", "eev_" #i}, FOR_CORES #undef FOR_CORE #define FOR_CORE(i) \ {"bev_" #i, "heatmaps", "bev_" #i}, FOR_CORES #undef FOR_CORE }); // ---------------------------------------------------------------------------- // [SECTION] Heatmap colormap // ---------------------------------------------------------------------------- std::array g_hm_colormap = std::to_array({ {0.000f, 0.000f, 0.016f, 1.f}, {0.106f, 0.047f, 0.255f, 1.f}, {0.290f, 0.047f, 0.420f, 1.f}, {0.471f, 0.110f, 0.427f, 1.f}, {0.647f, 0.173f, 0.376f, 1.f}, {0.812f, 0.267f, 0.275f, 1.f}, {0.929f, 0.412f, 0.145f, 1.f}, {0.984f, 0.608f, 0.024f, 1.f}, {0.969f, 0.820f, 0.239f, 1.f}, {0.988f, 1.000f, 0.643f, 1.f}, }); // ---------------------------------------------------------------------------- // [SECTION] Global variables // ---------------------------------------------------------------------------- GLFWwindow *g_window; ImGuiIO *g_imgui_io; ImGuiStyle *g_imgui_style; ImPlotStyle *g_implot_style; std::array g_x_axes = std::to_array({ "rowid", "step", #define FOR_CORE(i) "cycl_" #i, FOR_CORES #undef FOR_CORE }); int g_status; int g_x_axis = DEFVAL_X_AXIS; int64_t g_entries = DEFVAL_ENTRIES; int64_t g_nth = DEFVAL_NTH; int64_t g_x_low = DEFVAL_X_LOW; int64_t g_x_high = DEFVAL_X_HIGH; 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; int64_t g_trace_len; int64_t g_trace_offset; thrd_t g_fetching_thread; mtx_t g_fetching_mutex; bool g_data_col_visible = true; bool g_plot_maximized; bool g_plot_scroll; float g_plot_scroll_current; float g_plot_scroll_to; float g_data_col_width; std::vector g_plot_cells; std::vector g_plot_cells_top; std::vector g_plot_cells_bottom; std::vector g_plots_covered; Plot *g_plot_selected; Plot *g_plot_hovered; int g_plot_stride; int g_downsampled_trace_len; int g_plot_cols = 2; size_t g_plot_col_selected; size_t g_plot_row_selected; float g_plot_height = 300.f; ImPlotColormap g_hm_colormap_id; std::map *, StringComparator> g_trace_map; std::vector *> g_traces; std::vector g_plots; Trace g_x_axis_double; Trace g_zero_trace; PFNGLCOPYIMAGESUBDATAPROC glCopyImageSubData; // ---------------------------------------------------------------------------- // [SECTION] String comparator definition // ---------------------------------------------------------------------------- bool StringComparator::operator()(const char *a, const char *b) const { return strcmp(a, b) < 0; } // ---------------------------------------------------------------------------- // [SECTION] Trace (base) definition // ---------------------------------------------------------------------------- template Trace::Trace() : std::vector() {} template Trace::~Trace() {} template size_t Trace::start_offset() const { return (size_t)g_trace_offset; } template void Trace::trim() { trim_spec(); } #if !defined(NDEBUG) template void Trace::validate() const { assert(this->size() == g_traces[0]->size()); } #endif template void Trace::clear() { std::vector::clear(); } template void Trace::push_back(T value) { std::vector::push_back(value); } template T *Trace::start() { return this->size() ? &operator[](start_offset()) : nullptr; } template T &Trace::operator[](size_t n) { #if !defined(NDEBUG) return this->at(n); #else return std::vector::operator[](n); #endif } template T Trace::operator[](size_t n) const { #if !defined(NDEBUG) return this->at(n); #else return std::vector::operator[](n); #endif } template void Trace::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 TraceNamed::TraceNamed(const char *name, const char *label) : m_name(name), m_label(label) {} template const char *TraceNamed::get_name() const { return m_name; } template const char *TraceNamed::get_label() const { return m_label; } // ---------------------------------------------------------------------------- // [SECTION] TraceHeatmap definition // ---------------------------------------------------------------------------- template TraceHeatmap::TraceHeatmap(const char *name, const char *name_fmt) : TraceNamed(name, name_fmt) {} template size_t TraceHeatmap::start_offset() const { return (size_t)(g_trace_offset * g_hm_pixel_count); } template void TraceHeatmap::trim() { Trace::trim_spec(g_hm_pixel_count); } #if !defined(NDEBUG) template void TraceHeatmap::validate() const { assert(this->size() == g_traces[0]->size() * g_hm_pixel_count); } #endif // ---------------------------------------------------------------------------- // [SECTION] Plot (base) definition // ---------------------------------------------------------------------------- Plot::Plot(const char *name, const char *section) : m_visible(true), m_name(name), m_section(section) {} Plot::~Plot() {} void Plot::handle_input(int key, int mods) { (void)key; (void)mods; } 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_formatter()); ImPlot::SetupAxisFormat(ImAxis_Y1, y_formatter()); if (ImPlot::IsPlotHovered()) g_plot_hovered = this; render_internal(frame_size); ImPlot::EndPlot(); } render_post(frame_size); } 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_formatter() const { return Plot::hex_formatter; } AxisFormatter Plot::y_formatter() const { return Plot::hex_formatter; } float Plot::right_margin() const { return 0.f; } int Plot::flags() const { return 0; } void Plot::render_post(const ImVec2 &frame_size) { (void)frame_size; } // ---------------------------------------------------------------------------- // [SECTION] PlotLines definition // ---------------------------------------------------------------------------- PlotLines::PlotLines(const char *name, const char *section, std::vector trace_keys) : Plot(name, section), m_trace_keys(trace_keys) {} void PlotLines::render_internal(const ImVec2 &frame_size) { (void)frame_size; ImS64 *x = g_trace_map[g_x_axes[g_x_axis]]->start(); ImPlotSpec spec = ImPlotSpec(ImPlotProp_Stride, sizeof(ImS64) * g_plot_stride); for (auto &trace : m_trace_keys) { TraceNamed *trace_obj = g_trace_map[trace]; ImS64 *y = trace_obj->start(); ImPlot::PlotLine(trace_obj->get_label(), x, y, g_downsampled_trace_len, spec); } } // ---------------------------------------------------------------------------- // [SECTION] PlotStacked definition // ---------------------------------------------------------------------------- PlotStacked::PlotStacked(const char *name, const char *section, std::vector 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) {} void PlotStacked::flag_for_reset() { m_needs_reset = true; } int PlotStacked::percent_formatter(double value, char *buff, int size, void *data) { (void)data; snprintf(buff, size, "%3.0f%%", value * 100.); return 0; } 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]]->get_label()); } 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 (size_t i = trace_size; i < g_traces[0]->size(); i++) { if (m_trace_totals[i]) { double normal = 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(); 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 *prev_trace = &g_zero_trace; 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]) { bool hovered = GImPlot->CurrentPlot->Items.GetLegendItem(i)->LegendHovered; ImPlotSpec spec = ImPlotSpec(ImPlotProp_FillAlpha, hovered ? 1.f : 0.9f, ImPlotProp_Stride, sizeof(double) * g_plot_stride); const char *trace_name = g_trace_map[m_trace_keys[i]]->get_label(); Trace *current_trace = &m_trace_normals[i]; ImPlot::PlotShaded(trace_name, g_x_axis_double.start(), prev_trace->start(), current_trace->start(), g_downsampled_trace_len, spec); ImPlot::PlotLine(trace_name, g_x_axis_double.start(), prev_trace->start(), g_downsampled_trace_len, spec); if (hovered) ImPlot::PlotLine(trace_name, g_x_axis_double.start(), current_trace->start(), g_downsampled_trace_len, spec); prev_trace = current_trace; } } } // ---------------------------------------------------------------------------- // [SECTION] PlotHeatmap definition // ---------------------------------------------------------------------------- PlotHeatmap::PlotHeatmap(const char *name, const char *section, const char *trace_key) : Plot(name, section), m_trace_key(trace_key), m_tex_scale_pow(-1.f), 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) {} void PlotHeatmap::handle_input(int key, int mods) { switch (mods) { case GLFW_MOD_CONTROL: switch (key) { case GLFW_KEY_K: m_tex_scale_pow = std::clamp(m_tex_scale_pow - 1, HM_SCALE_POW_MIN, HM_SCALE_POW_MAX); m_needs_reset = true; break; case GLFW_KEY_L: m_tex_scale_pow = std::clamp(m_tex_scale_pow + 1, HM_SCALE_POW_MIN, HM_SCALE_POW_MAX); m_needs_reset = true; break; } } } void PlotHeatmap::flag_for_reset() { m_needs_reset = true; } float PlotHeatmap::right_margin() const { return HM_COLORSCALE_WIDTH; } int PlotHeatmap::flags() const { return ImPlotFlags_NoLegend; } void PlotHeatmap::render_internal(const ImVec2 &frame_size) { Trace *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 (m_tex_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, m_tex_scale_pow); } m_tex_render.reserve((trace->size() - m_tex_rendered_last) * 3); if (m_tex_scale_high != 0.f) { 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 } } else { for (size_t i = m_tex_rendered_last; i < trace->size(); i++) { m_tex_render.push_back(0); m_tex_render.push_back(0); m_tex_render.push_back(0); } } 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_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 = m_tex_scale_pow == HM_SCALE_POW_MIN ? (double)m_tex_scale_high : pow(2., (double)m_tex_scale_pow); ImPlot::PlotImage(get_name(), (ImTextureID)m_tex_id, bmin, bmax, uv0, uv1); ImGui::SameLine(); 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; } // ---------------------------------------------------------------------------- // [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)); } void data_update_stride(void) { if (g_plot_maximized) { g_plot_stride = 1; g_downsampled_trace_len = g_trace_len; return; } int win_height = 0; glfwGetWindowSize(g_window, nullptr, &win_height); int max_stride = g_plot_cols * ((win_height / (int)g_plot_height) + 1); int max_points = DEFVAL_ENTRIES / max_stride; g_plot_stride = 1; while (g_trace_len / g_plot_stride > max_points) { g_plot_stride++; } g_downsampled_trace_len = g_trace_len / g_plot_stride; log_info("Updated plot stride: %d", g_plot_stride); log_info("Updated downsampled trace-len: %d", g_downsampled_trace_len); } void data_on_field_change(void) { g_entries = std::clamp(g_entries, 1l, DEFVAL_ENTRIES); g_nth = std::clamp(g_nth, DEFVAL_NTH, INT64_MAX); g_x_low = std::clamp(g_x_low, DEFVAL_X_LOW, INT64_MAX); g_x_high = std::clamp(g_x_high, g_x_low + 1l, DEFVAL_X_HIGH); #if !defined(MVEC_LOOP) g_hm_left = std::clamp(g_hm_left, DEFVAL_HM_LEFT, (int64_t)MVEC_SIZE); #endif g_hm_pixel_count = std::clamp(g_hm_pixel_count, 1l, DEFVAL_HM_PIXEL_COUNT); g_hm_pixel_pow = std::clamp(g_hm_pixel_pow, 0l, data_max_hm_pixel_pow()); g_x_current = -1l; g_trace_len = 0l; g_trace_offset = 0;; g_plot_stride = 0; g_downsampled_trace_len = 0; for (auto &trace : g_traces) trace->clear(); for (auto &plot: g_plots) plot->flag_for_reset(); g_x_axis_double.clear(); g_zero_trace.clear(); } void data_reset_fields(void) { g_entries = DEFVAL_ENTRIES; g_nth = DEFVAL_NTH; g_x_axis = DEFVAL_X_AXIS; g_x_low = DEFVAL_X_LOW; g_x_high = DEFVAL_X_HIGH; g_hm_left = DEFVAL_HM_LEFT; g_hm_pixel_count = DEFVAL_HM_PIXEL_COUNT; g_hm_pixel_pow = data_max_hm_pixel_pow(); data_on_field_change(); } void data_reset_plot_cells(void) { std::fill(g_plot_cells.begin(), g_plot_cells.end(), nullptr); std::fill(g_plot_cells_top.begin(), g_plot_cells_top.end(), 0.f); std::fill(g_plot_cells_bottom.begin(), g_plot_cells_bottom.end(), 0.f); } 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)); json_object_object_add(request, "nth", json_object_new_int64(g_nth)); json_object_object_add(request, "x-axis", json_object_new_string(g_x_axes[g_x_axis])); json_object_object_add(request, "x-low", json_object_new_int64(g_x_low)); json_object_object_add(request, "x-high", json_object_new_int64(g_x_high)); json_object_object_add(request, "hm-left", json_object_new_int64(g_hm_left)); json_object_object_add(request, "hm-pixel-count", json_object_new_int64(g_hm_pixel_count)); json_object_object_add(request, "hm-pixel-pow", json_object_new_int64(g_hm_pixel_pow)); json_object_object_add(request, "x-current", json_object_new_int64(g_x_current)); const char *request_str = json_object_to_json_string(request); log_info("Sending request to server: %s", request_str); int socket_fd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in socket_addr; memset(&socket_addr, 0, sizeof(sockaddr_in)); socket_addr.sin_family = AF_INET; socket_addr.sin_port = htons(PORT); inet_pton(AF_INET, IP, &socket_addr.sin_addr); if (connect(socket_fd, (sockaddr *)&socket_addr, sizeof(sockaddr_in))) assert(false); json_object_to_fd(socket_fd, request, 0); shutdown(socket_fd, SHUT_WR); json_object *response = json_object_from_fd(socket_fd); mtx_lock(&g_fetching_mutex); json_object_object_foreach(response, key, value) { size_t new_rows = json_object_array_length(value); if (!strcmp(key, g_x_axes[g_x_axis])) { log_info("Received %lu rows of data from server", new_rows); 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_double.push_back((double)point); g_zero_trace.push_back(0.); } } if (g_trace_map.contains(key)) { for (size_t i = 0; i < new_rows; i++) { ImS64 point = json_object_get_int64(json_object_array_get_idx(value, i)); g_trace_map[key]->push_back(point); } } } g_x_current = g_trace_map[g_x_axes[g_x_axis]]->back(); json_object_put(request); json_object_put(response); #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(); 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(); g_trace_len = std::min(current_size, g_entries); g_trace_offset = current_size > g_entries ? current_size - g_entries : 0l; data_update_stride(); mtx_unlock(&g_fetching_mutex); g_status = STATUS_RUNNING; } int data_fetching_thread(void *data) { (void)data; assert(!data); assert(g_status == STATUS_RUNNING); while (g_status == STATUS_RUNNING) { data_fetch(); for (int i = 0; i < DATA_FETCH_INTERVAL_SUBDIV && g_status == STATUS_RUNNING; i++) { usleep((DATA_FETCH_INTERVAL * 1000000) / DATA_FETCH_INTERVAL_SUBDIV); } } assert(g_status == STATUS_STOPPING); g_status = STATUS_STOPPED; return 0; } void data_start_fetching(void) { log_info("Starting data fetching thread"); g_status = STATUS_RUNNING; thrd_create(&g_fetching_thread, (thrd_start_t)data_fetching_thread, nullptr); } void data_stop_fetching(void) { assert(g_status == STATUS_RUNNING || g_status == STATUS_FETCHING); log_info("Stopping data fetching thread"); g_status = STATUS_STOPPING; } // ---------------------------------------------------------------------------- // [SECTION] GUI functions // ---------------------------------------------------------------------------- void gui_render_data_input(const char *label, int64_t *target) { assert(target); if (ImGui::InputScalar(label, ImGuiDataType_U64, target, nullptr, nullptr, "%#lx")) { data_on_field_change(); } } void gui_render_data_col(void) { const ImGuiViewport *viewport = ImGui::GetMainViewport(); const ImVec2 win_pos = viewport->Pos; const ImVec2 win_size = ImVec2(-1.f, viewport->Size.y); ImGui::SetNextWindowPos(win_pos); ImGui::SetNextWindowSize(win_size); ImGui::Begin("data-col", nullptr, IMGUI_WINDOW_FLAGS); g_data_col_width = ImGui::GetWindowWidth(); ImGui::SeparatorText("SALIS data client"); ImGui::LabelText("name", NAME); ImGui::LabelText("seed", "%#lx", SEED); ImGui::LabelText("server", IP ":" PORT_STR); ImGui::LabelText("arch", ARCH); ImGui::LabelText("cores", "%d", CORES); ImGui::LabelText("mvec-size", "%#lx", MVEC_SIZE); #if defined(MVEC_LOOP) ImGui::LabelText("mvec-loop", "true"); #else ImGui::LabelText("mvec-loop", "false"); #endif ImGui::LabelText("data-push", "%#lx", DATA_PUSH_INTERVAL); ImGui::LabelText("fps", "%.1f", g_imgui_io->Framerate); ImGui::SeparatorText("Data fields"); switch (g_status) { case STATUS_STOPPED: gui_render_data_input("entries", &g_entries); gui_render_data_input("nth", &g_nth); if (ImGui::BeginCombo("x-axis", g_x_axes[g_x_axis])) { for (int i = 0; i < CORES + 2; i++) { if (ImGui::Selectable(g_x_axes[i], g_x_axis == i)) { data_reset_fields(); g_x_axis = i; } } ImGui::EndCombo(); } gui_render_data_input("x-low", &g_x_low); gui_render_data_input("x-high", &g_x_high); gui_render_data_input("hm-left", &g_hm_left); gui_render_data_input("hm-pxl-count", &g_hm_pixel_count); 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); ImGui::LabelText("x-axis", "%s", g_x_axes[g_x_axis]); ImGui::LabelText("x-low", "%#lx", g_x_low); ImGui::LabelText("x-high", "%#lx", g_x_high); ImGui::LabelText("hm-left", "%#lx", g_hm_left); ImGui::LabelText("hm-pxl-count", "%#lx", g_hm_pixel_count); ImGui::LabelText("hm-pxl-pow", "%#lx", g_hm_pixel_pow); } 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(); break; case STATUS_RUNNING: 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; } ImGui::SeparatorText("Layout"); ImGui::DragInt("cols", &g_plot_cols, 1, PLOT_MIN_COLS, PLOT_MAX_COLS); ImGui::DragFloat("plot-height", &g_plot_height, PLOT_HEIGHT_INTERVAL, PLOT_MIN_HEIGHT, PLOT_MAX_HEIGHT, "%.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(); } 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; } 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; } } return g_plot_row_selected; } 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; } } return g_plot_row_selected; } void gui_render_plots(void) { const char *section_current = g_plots[0]->get_section(); const char *section_next = nullptr; g_plots_covered.clear(); g_plots_covered.resize(g_plots.size(), false); const ImGuiViewport *viewport = ImGui::GetMainViewport(); const ImVec2 win_pos = g_data_col_visible ? ImVec2(g_data_col_width, viewport->Pos.y) : viewport->Pos; const ImVec2 win_size = g_data_col_visible ? ImVec2(viewport->Size.x - g_data_col_width, -1.f) : ImVec2(viewport->Size.x, -1.f); if (g_plot_scroll) { ImGui::SetNextWindowScroll(ImVec2(-1.f, g_plot_scroll_to)); g_plot_scroll = false; g_plot_scroll_to = 0.f; } ImGui::SetNextWindowPos(win_pos); ImGui::SetNextWindowSize(win_size); ImGui::Begin("plots", nullptr, IMGUI_WINDOW_FLAGS); g_plot_scroll_current = ImGui::GetScrollY(); g_plot_hovered = nullptr; 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); while (section_current) { ImGui::SeparatorText(section_current); ImGui::BeginTable("plots-table", g_plot_cols); for (size_t i = 0; i < g_plots.size(); i++) { 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; } 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]; } g_plots[i]->render(frame_size); 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_plots_covered[i] = true; } section_current = section_next; section_next = nullptr; ImGui::EndTable(); row += col ? 1 : 0; col = 0; } mtx_unlock(&g_fetching_mutex); ImGui::End(); } void gui_render_plot_maximized(void) { const ImGuiViewport *viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::Begin("plot-fullscreen", nullptr, IMGUI_WINDOW_FLAGS); ImVec2 frame_size = { viewport->Size.x - g_imgui_style->WindowPadding.x * 2, 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) { size_t row = 0; float selected_plot_top = 0.f; 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; } } } loop_exit: g_plot_scroll_to = selected_plot_top + (PLOT_HEIGHT_INTERVAL * (float)row * (increased_plot_height ? 1.f : -1.f)) - PLOT_SCROLL_MARGIN; g_plot_scroll = true; } void gui_plot_queue_scroll_to_selected() { const ImGuiViewport *viewport = ImGui::GetMainViewport(); float plot_top = g_plot_cells_top[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected)]; float plot_bottom = g_plot_cells_bottom[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected)]; float win_bottom = g_plot_scroll_current + viewport->Size.y; if (plot_bottom > win_bottom) { g_plot_scroll_to = g_plot_scroll_current + (plot_bottom - win_bottom); g_plot_scroll = true; } if (plot_top < g_plot_scroll_current) { g_plot_scroll_to = plot_top - PLOT_SCROLL_MARGIN; g_plot_scroll = true; } } void gui_render(void) { if (g_plot_maximized) { gui_render_plot_maximized(); return; } if (g_data_col_visible) gui_render_data_col(); gui_render_plots(); } // ---------------------------------------------------------------------------- // [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 || g_status == STATUS_FETCHING) { data_stop_fetching(); } glfwSetWindowShouldClose(g_window, GLFW_TRUE); } void app_error_callback(int error, const char* description) { log_warn("GLFW error %d: %s", error, description); } void app_toggle_state() { switch (g_status) { case STATUS_STOPPED: data_start_fetching(); break; case STATUS_RUNNING: case STATUS_FETCHING: data_stop_fetching(); break; } } void app_key_callback_plot_maximized(int key, int mods) { switch (mods) { case GLFW_MOD_CONTROL: switch (key) { case GLFW_KEY_C: glfwSetWindowShouldClose(g_window, GLFW_TRUE); break; } break; case 0: switch (key) { case GLFW_KEY_F: g_plot_maximized = false; data_update_stride(); break; case GLFW_KEY_SPACE: app_toggle_state(); break; } break; } if (g_plot_selected->m_visible) { g_plot_selected->handle_input(key, mods); } } void app_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { (void)window; (void)scancode; if (ImGui::IsAnyItemActive()) { return; } if (action != GLFW_PRESS && action != GLFW_REPEAT) { return; } if (g_plot_maximized) { app_key_callback_plot_maximized(key, mods); return; } switch (mods) { case GLFW_MOD_CONTROL: switch (key) { case GLFW_KEY_C: glfwSetWindowShouldClose(g_window, GLFW_TRUE); break; case GLFW_KEY_N: g_data_col_visible = !g_data_col_visible; break; case GLFW_KEY_LEFT: g_plot_cols = std::max(g_plot_cols - 1, PLOT_MIN_COLS); data_reset_plot_cells(); data_update_stride(); break; case GLFW_KEY_RIGHT: g_plot_cols = std::min(g_plot_cols + 1, PLOT_MAX_COLS); data_reset_plot_cells(); data_update_stride(); break; case GLFW_KEY_UP: g_plot_height = std::min(g_plot_height + PLOT_HEIGHT_INTERVAL, PLOT_MAX_HEIGHT); gui_plot_queue_scroll_to_position(true); data_update_stride(); break; case GLFW_KEY_DOWN: g_plot_height = std::max(g_plot_height - PLOT_HEIGHT_INTERVAL, PLOT_MIN_HEIGHT); gui_plot_queue_scroll_to_position(false); data_update_stride(); break; } break; case 0: switch (key) { case GLFW_KEY_LEFT: 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: g_plot_col_selected += (g_plot_col_selected < PLOT_MAX_COLS - 1 && g_plot_cells[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected + 1)]) ? 1 : 0; g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected)]; break; case GLFW_KEY_UP: g_plot_row_selected = gui_plot_cell_row_up(); g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected)]; gui_plot_queue_scroll_to_selected(); break; case GLFW_KEY_DOWN: g_plot_row_selected = gui_plot_cell_row_down(); g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_row_selected, g_plot_col_selected)]; gui_plot_queue_scroll_to_selected(); break; case GLFW_KEY_F: if (g_plot_selected->m_visible) { g_plot_maximized = !g_plot_maximized; data_update_stride(); } break; case GLFW_KEY_SPACE: app_toggle_state(); break; } break; } if (g_plot_selected->m_visible) { g_plot_selected->handle_input(key, mods); } } void app_window_size_callback(GLFWwindow* window, int width, int height) { (void)window; (void)width; (void)height; data_update_stride(); } void app_mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { (void)window; (void)mods; if (button != GLFW_MOUSE_BUTTON_LEFT) return; switch (action) { case GLFW_PRESS: g_plot_selected = g_plot_hovered ? g_plot_hovered : g_plot_selected; break; } } void init() { signal(SIGINT, app_sig_handler); signal(SIGTERM, app_sig_handler); log_info("Starting SALIS data client"); log_info("Initializing GLFW"); glfwSetErrorCallback(app_error_callback); glfwInitHint(GLFW_WAYLAND_LIBDECOR, GLFW_WAYLAND_DISABLE_LIBDECOR); if (!glfwInit()) assert(false); float scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); g_window = glfwCreateWindow((int)(800 * scale), (int)(600 * scale), "SALIS data client", nullptr, nullptr); assert(g_window); glfwSetKeyCallback(g_window, app_key_callback); glfwSetMouseButtonCallback(g_window, app_mouse_button_callback); glfwSetWindowSizeCallback(g_window, app_window_size_callback); glfwMakeContextCurrent(g_window); #if defined(VSYNC) glfwSwapInterval(1); #else glfwSwapInterval(0); #endif glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)glfwGetProcAddress("glCopyImageSubData"); assert(glCopyImageSubData); log_info("Initializing ImGui"); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImPlot::CreateContext(); g_imgui_io = &ImGui::GetIO(); g_imgui_io->Fonts->AddFontFromFileTTF(FONT_SOURCE, FONT_SIZE); g_imgui_io->IniFilename = nullptr; g_imgui_style = &ImGui::GetStyle(); g_imgui_style->Colors[ImGuiCol_WindowBg] = COLOR_BLACK; g_imgui_style->FontScaleDpi = scale; g_imgui_style->FontSizeBase = FONT_SIZE; g_imgui_style->ItemSpacing = ImVec2(g_imgui_style->ItemSpacing.x, 2.f); g_imgui_style->ScaleAllSizes(scale); g_implot_style = &ImPlot::GetStyle(); g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK; 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); for (auto &trace : g_core_traces) g_traces.push_back(&trace); for (auto &trace : g_arch_traces) g_traces.push_back(&trace); for (auto &trace : g_core_traces_heatmaps) g_traces.push_back(&trace); for (auto &trace : g_arch_traces_heatmaps) g_traces.push_back(&trace); for (auto &plot : g_core_plots) g_plots.push_back(&plot); for (auto &plot : g_arch_plots) g_plots.push_back(&plot); 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->get_name()] = i; g_plot_cells = std::vector(g_plots.size() * PLOT_MAX_COLS); g_plot_cells_top = std::vector(g_plots.size() * PLOT_MAX_COLS); g_plot_cells_bottom = std::vector(g_plots.size() * PLOT_MAX_COLS); g_plot_selected = g_plots[0]; g_hm_pixel_pow = data_max_hm_pixel_pow(); data_update_stride(); mtx_init(&g_fetching_mutex, mtx_plain); } void exec() { while (!glfwWindowShouldClose(g_window)) { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); gui_render(); ImGui::Render(); int display_w; int display_h; glfwGetFramebufferSize(g_window, &display_w, &display_h); glViewport(0, 0, display_w, display_h); glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(g_window); } } void quit() { mtx_destroy(&g_fetching_mutex); ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImPlot::DestroyContext(); ImGui::DestroyContext(); log_info("Stopping SALIS data client"); glfwDestroyWindow(g_window); glfwTerminate(); } int main(int argc, char **argv) { (void)argc; (void)argv; init(); exec(); quit(); return 0; }