#include #include #include #include #include #include #include #include #include #include #include #include #include "logger.c" // ---------------------------------------------------------------------------- // Defines // ---------------------------------------------------------------------------- #define COLOR_BLACK ImVec4(0.f, 0.f, 0.f, 1.f) #define FONT_SIZE 12.f #define FONT_SOURCE "/usr/share/fonts/droid/DroidSansMono.ttf" #define GLSL_VERSION "#version 130" #define PLOT_MAX_COLS 8 #define PLOT_MIN_COLS 1 #define PLOT_SCROLL_OFFSET 28.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 // ---------------------------------------------------------------------------- // Declarations // ---------------------------------------------------------------------------- enum Status { STATUS_STOPPED, STATUS_RUNNING, STATUS_STOPPING, }; struct Trace { const char *name; const char *name_fmt; std::vector data; Trace(const char *_name, const char *_name_fmt); }; struct Plot { const char *name; const char *section; std::vector traces; Plot(const char *_name, const char *_section, std::vector_traces); virtual void reset(); virtual void render(); }; struct PlotStacked : public Plot { std::vector> stacks; std::vector> normals; std::vector totals; PlotStacked(const char *_name, const char *_section, std::vector_traces); void reset(); void push_stack(ImS64 index); void push_normal(ImS64 index); void render(); }; struct CompString { bool operator()(const char *a, const char *b) const; }; // ---------------------------------------------------------------------------- // Plots // ---------------------------------------------------------------------------- #include "arch_plots.cpp" std::vector g_core_traces = { {"rowid", nullptr}, {"step", nullptr}, #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::vector g_core_plots = { {"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 }}, }; // ---------------------------------------------------------------------------- // Globals // ---------------------------------------------------------------------------- GLFWwindow *g_window; ImGuiIO *g_imgui_io; ImGuiStyle *g_imgui_style; ImPlotStyle *g_implot_style; // Data const char *g_x_axes[] = { "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; int g_trace_len; int 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; 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; int g_plot_cols = 2; int g_plot_col_selected; int g_plot_row_selected; // Plots std::map g_trace_map; std::vector g_traces; std::vector g_plots; std::vector g_x_axis_normal; // ---------------------------------------------------------------------------- // 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_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; for (auto &trace : g_traces) { trace->data.clear(); } for (auto &plot : g_plots) { plot->reset(); } g_x_axis_normal.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(); g_trace_len = 0; g_trace_offset = 0; 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) { 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); const char *response_str = json_object_to_json_string(response); log_info("Server responded with: %s", response_str); mtx_lock(&g_fetching_mutex); json_object_object_foreach(response, key, value) { if (g_trace_map.contains(key)) { for (size_t i = 0; i < json_object_array_length(value); i++) { ImS64 point = json_object_get_int64(json_object_array_get_idx(value, i)); g_trace_map[key]->data.push_back(point); } } } for (auto &val : g_trace_map[g_x_axes[g_x_axis]]->data) { g_x_axis_normal.push_back((double)val); } g_x_current = g_trace_map[g_x_axes[g_x_axis]]->data.back(); json_object_put(request); json_object_put(response); g_trace_len = g_traces[0]->data.size(); if (g_trace_len >= g_entries * 2) { for (auto &[key, trace] : g_trace_map) { trace->data.erase(trace->data.begin(), trace->data.end() - g_entries); } g_trace_len = g_traces[0]->data.size(); } g_trace_offset = 0; if (g_trace_len > g_entries) { g_trace_offset = g_trace_len - g_entries; g_trace_len = g_entries; } mtx_unlock(&g_fetching_mutex); } int data_fetching_thread(void *data) { (void)data; assert(!data); assert(g_status == STATUS_RUNNING || g_status == STATUS_STOPPING); 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); log_info("Stopping data fetching thread"); g_status = STATUS_STOPPING; thrd_join(g_fetching_thread, nullptr); } // ---------------------------------------------------------------------------- // GUI functions // ---------------------------------------------------------------------------- void gui_render_data_input(const char *label, int64_t *target) { assert(label); 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::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_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_STOPPING: ImGui::LabelText("##", "Stopping"); break; } ImGui::SeparatorText("Layout"); ImGui::SliderInt("cols", &g_plot_cols, PLOT_MIN_COLS, PLOT_MAX_COLS); ImGui::End(); } int gui_plot_cell_index(int col, int row) { int index = col * PLOT_MAX_COLS + row; assert(index < (int)g_plot_cells.size()); assert(index < (int)g_plot_cells_top.size()); assert(index < (int)g_plot_cells_bottom.size()); return index; } void gui_render_plot(Plot *plot, const ImVec2 &frame_size=ImVec2(-1.f, 0.f)) { assert(plot); int plot_flags = ImPlotFlags_NoMenus; int axis_flags = ImPlotAxisFlags_NoMenus; if (g_status != STATUS_STOPPED) { ImPlot::SetNextAxesToFit(); plot_flags |= ImPlotFlags_NoInputs | ImPlotFlags_NoMouseText; axis_flags |= ImPlotAxisFlags_NoHighlight; } if (ImPlot::BeginPlot(plot->name, frame_size, plot_flags)) { ImPlot::SetupAxes(nullptr, nullptr, axis_flags, axis_flags); plot->render(); ImPlot::EndPlot(); } } void gui_render_plots(void) { const char *section_current = g_plots[0]->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(); int col = 0; int row = 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 (g_plots[i]->section != section_current) { section_next = (!section_next && !g_plots_covered[i]) ? g_plots[i]->section : section_next; continue; } ImGui::TableNextColumn(); g_plot_cells[gui_plot_cell_index(col, row)] = g_plots[i]; g_plot_cells_top[gui_plot_cell_index(col, row)] = 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]; } gui_render_plot(g_plots[i]); if (g_plots[i] == g_plot_selected) { g_implot_style->Colors[ImPlotCol_FrameBg] = COLOR_BLACK; } g_plot_cells_bottom[gui_plot_cell_index(col, row)] = 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); gui_render_plot(g_plot_selected, viewport->Size); ImGui::End(); } void gui_plot_queue_scroll(int col_selected, int row_selected) { const ImGuiViewport *viewport = ImGui::GetMainViewport(); float plot_top = g_plot_cells_top[gui_plot_cell_index(col_selected, row_selected)]; float plot_bottom = g_plot_cells_bottom[gui_plot_cell_index(col_selected, row_selected)]; float win_top = g_plot_scroll_current; float win_bottom = win_top + viewport->Size.y; if (plot_bottom > win_bottom) { g_plot_scroll_to = win_top + (plot_bottom - win_bottom); g_plot_scroll = true; } if (plot_top < win_top) { g_plot_scroll_to = plot_top - PLOT_SCROLL_OFFSET; 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(); } // ---------------------------------------------------------------------------- // Main functions // ---------------------------------------------------------------------------- void app_sig_handler(int signo) { (void)signo; log_warn("Signal received, will stop SALIS data client..."); if (g_status == STATUS_RUNNING) { 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_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { (void)window; (void)scancode; if (action != GLFW_PRESS || ImGui::IsAnyItemActive()) { return; } if (g_plot_maximized) { if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_C) { glfwSetWindowShouldClose(g_window, GLFW_TRUE); } if (mods == 0 && key == GLFW_KEY_F) { g_plot_maximized = !g_plot_maximized; } 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 -= g_plot_cols > 1 ? 1 : 0; data_reset_plot_cells(); break; case GLFW_KEY_RIGHT: g_plot_cols += g_plot_cols < PLOT_MAX_COLS ? 1 : 0; data_reset_plot_cells(); 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_col_selected, g_plot_row_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_col_selected + 1, g_plot_row_selected)]) ? 1 : 0; g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)]; break; case GLFW_KEY_UP: g_plot_row_selected -= (g_plot_row_selected && g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected - 1)]) ? 1 : 0; g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)]; gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected); break; case GLFW_KEY_DOWN: g_plot_row_selected += (g_plot_row_selected < (int)g_plots.size() - 1 && g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected + 1)]) ? 1 : 0; g_plot_selected = g_plot_cells[gui_plot_cell_index(g_plot_col_selected, g_plot_row_selected)]; gui_plot_queue_scroll(g_plot_col_selected, g_plot_row_selected); break; case GLFW_KEY_F: g_plot_maximized = !g_plot_maximized; break; case GLFW_KEY_SPACE: switch (g_status) { case STATUS_STOPPED: data_start_fetching(); break; case STATUS_RUNNING: data_stop_fetching(); break; } break; } 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); glfwMakeContextCurrent(g_window); glfwSwapInterval(1); // enable vsync log_info("Initializing ImGui"); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImPlot::CreateContext(); g_imgui_io = &ImGui::GetIO(); g_imgui_io->Fonts->AddFontFromFileTTF(FONT_SOURCE); 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; 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 &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 &i : g_traces) g_trace_map[i->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(); 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; } // ---------------------------------------------------------------------------- // Member definitions // ---------------------------------------------------------------------------- Trace::Trace(const char *_name, const char *_name_fmt) { name = _name; name_fmt = _name_fmt; } // Regular line plot Plot::Plot(const char *_name, const char *_section, std::vector_traces) { name = _name; section = _section; traces = _traces; } void Plot::reset() {} void Plot::render() { ImS64 *x = g_trace_map[g_x_axes[g_x_axis]]->data.data() + g_trace_offset; for (auto &trace : traces) { Trace *trace_obj = g_trace_map[trace]; ImS64 *y = trace_obj->data.data() + g_trace_offset; ImPlot::PlotLine(trace_obj->name_fmt, x, y, g_trace_len); } } // Stacked percentage plot PlotStacked::PlotStacked(const char *_name, const char *_section, std::vector_traces) : Plot(_name, _section, _traces) { stacks.resize(_traces.size()); normals.resize(_traces.size()); } void PlotStacked::reset() { for (auto &stack : stacks) stack.clear(); for (auto &normal : normals) normal.clear(); totals.clear(); } void PlotStacked::push_stack(ImS64 index) { ImS64 new_total = 0; for (int i = 0; i < (int)traces.size(); i++) { ImS64 trace_val = g_trace_map[traces[i]]->data[index]; stacks[i].push_back(trace_val + new_total); new_total += trace_val; } totals.push_back(new_total); } void PlotStacked::push_normal(ImS64 index) { if (totals[index]) { for (int i = 0; i < (int)traces.size(); i++) { double stack_val = (double)stacks[i][index]; double total_val = (double)totals[index]; normals[i].push_back(stack_val / total_val); } } else { for (int i = 0; i < (int)traces.size(); i++) { normals[i].push_back(0.); } } } void PlotStacked::render() { int totals_size = totals.size(); if (totals_size < g_trace_len) { for (int i = totals_size; i < g_trace_len; i++) { push_stack(i); } for (int i = totals_size; i < g_trace_len; i++) { push_normal(i); } } Trace *trace_0 = g_trace_map[traces[0]]; double *x = g_x_axis_normal.data() + g_trace_offset; double *y_0 = normals[0].data() + g_trace_offset; ImPlot::PlotShaded(trace_0->name_fmt, x, y_0, g_trace_len, 0); for (int i = 1; i < (int)traces.size(); i++) { Trace *trace_i = g_trace_map[traces[i]]; double *y_im1 = normals[i - 1].data() + g_trace_offset; double *y_i = normals[i].data() + g_trace_offset; ImPlot::PlotShaded(trace_i->name_fmt, x, y_im1, y_i, g_trace_len); } ImPlot::PlotLine(trace_0->name_fmt, x, y_0, g_trace_len); for (int i = 1; i < (int)traces.size(); i++) { Trace *trace_i = g_trace_map[traces[i]]; double *y_i = normals[i].data() + g_trace_offset; double *y_im1 = normals[i - 1].data() + g_trace_offset; ImPlot::PlotLine(trace_i->name_fmt, x, y_im1, g_trace_len); ImPlot::PlotLine(trace_i->name_fmt, x, y_i, g_trace_len); } } bool CompString::operator()(const char *a, const char *b) const { return strcmp(a, b) < 0; }