The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

st/gui: Add custom imgui element for timing graph

Features:
* draws 6 level lines with labels
* draws a line for a reference timing
* reference timing can be bottom of the graph, or center
* range around (up from) the reference timing can be specified
* optional automatic rescaling of the graph

+262
+25
src/external/imgui_monado/cimgui_monado.h
··· 1 + // Copyright 2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Custom imgui elements. 6 + * @author Christoph Haag <christoph.haag@collabora.com> 7 + */ 8 + 9 + #pragma once 10 + 11 + #ifdef __cplusplus 12 + extern "C" { 13 + #endif 14 + 15 + void igPlotTimings(const char *label, 16 + float (*values_getter)(void *data, int idx), void *data, 17 + int values_count, int values_offset, 18 + const char *overlay_text, float scale_min, float scale_max, 19 + ImVec2 frame_size, float reference_timing, 20 + bool center_reference_timing, float range, char *unit, 21 + bool dynamic_rescale); 22 + 23 + #ifdef __cplusplus 24 + } 25 + #endif
+237
src/external/imgui_monado/imgui_monado.cpp
··· 1 + // Copyright 2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Custom imgui elements. 6 + * @author Christoph Haag <christoph.haag@collabora.com> 7 + * 8 + * based on ImGui::PlotEx() from dear imgui, v1.76 WIP 9 + */ 10 + 11 + #include "../imgui/imgui.h" 12 + 13 + #ifndef IMGUI_DEFINE_MATH_OPERATORS 14 + #define IMGUI_DEFINE_MATH_OPERATORS 15 + #endif 16 + #include "../imgui/imgui_internal.h" 17 + 18 + #include "cimgui_monado.h" 19 + 20 + using namespace ImGui; 21 + 22 + static void _draw_line(ImGuiWindow *window, int values_count, float scale_min, 23 + float scale_max, float val, char *unit, 24 + const ImRect inner_bb, ImVec2 frame_size, ImU32 color) { 25 + const float inv_scale = 26 + (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); 27 + ImVec2 tp0 = ImVec2( 28 + 0.0f, 1.0f - ImSaturate((val - scale_min) * 29 + inv_scale)); // Point in the normalized space of 30 + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); 31 + const ImVec2 tp1 = 32 + ImVec2(1.0, 1.0f - ImSaturate((val - scale_min) * inv_scale)); 33 + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, tp1); 34 + window->DrawList->AddLine(pos0, pos1, color); 35 + 36 + char text[100]; 37 + snprintf(text, 60, "%.2f %s", val, unit); 38 + ImVec2 text_size = ImGui::CalcTextSize(text); 39 + ImVec2 text_pos = {pos1.x - text_size.x, pos1.y}; 40 + ImGui::PushStyleColor(ImGuiCol_Text, color); 41 + ImGui::RenderText(text_pos, text); 42 + ImGui::PopStyleColor(1); 43 + } 44 + static void _draw_grid(ImGuiWindow *window, int values_count, float scale_min, 45 + float scale_max, float reference_timing, char *unit, 46 + const ImRect inner_bb, ImVec2 frame_size) { 47 + 48 + ImVec4 target_color = ImVec4(1.0f, 1.0f, 0.0f, .75f); 49 + _draw_line(window, values_count, scale_min, scale_max, reference_timing, unit, 50 + inner_bb, frame_size, GetColorU32(target_color)); 51 + 52 + ImVec4 passive_color = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); 53 + 54 + // always draw ~5 lines 55 + float step = (scale_max - scale_min) / 5.; 56 + for (float i = scale_min; i < scale_max + step; i += step) { 57 + _draw_line(window, values_count, scale_min, scale_max, i, unit, inner_bb, 58 + frame_size, GetColorU32(passive_color)); 59 + } 60 + } 61 + 62 + static void PlotTimings(const char *label, 63 + float (*values_getter)(void *data, int idx), void *data, 64 + int values_count, int values_offset, 65 + const char *overlay_text, ImVec2 frame_size, 66 + float reference_timing, bool center_reference_timing, 67 + float range, char *unit, bool dynamic_rescale) { 68 + ImGuiWindow *window = GetCurrentWindow(); 69 + if (window->SkipItems) 70 + return; 71 + 72 + ImGuiContext &g = *GImGui; 73 + const ImGuiStyle &style = g.Style; 74 + const ImGuiID id = window->GetID(label); 75 + 76 + if (frame_size.x == 0.0f) 77 + frame_size.x = CalcItemWidth(); 78 + if (frame_size.y == 0.0f) 79 + frame_size.y = (style.FramePadding.y * 2); 80 + 81 + const ImRect frame_bb(window->DC.CursorPos, 82 + window->DC.CursorPos + frame_size); 83 + const ImRect inner_bb(frame_bb.Min + style.FramePadding, 84 + frame_bb.Max - style.FramePadding); 85 + const ImRect total_bb(frame_bb.Min, frame_bb.Max); 86 + ItemSize(total_bb, style.FramePadding.y); 87 + if (!ItemAdd(total_bb, 0, &frame_bb)) 88 + return; 89 + const bool hovered = ItemHoverable(frame_bb, id); 90 + 91 + float v_min = FLT_MAX; 92 + float v_max = -FLT_MAX; 93 + for (int i = 0; i < values_count; i++) { 94 + const float v = values_getter(data, i); 95 + if (v != v) // Ignore NaN values 96 + continue; 97 + v_min = ImMin(v_min, v); 98 + v_max = ImMax(v_max, v); 99 + } 100 + 101 + float scale_min = 102 + center_reference_timing ? reference_timing - range : reference_timing; 103 + float scale_max = reference_timing + range; 104 + 105 + if (dynamic_rescale) { 106 + if (v_max > scale_max) 107 + scale_max = v_max; 108 + scale_max = ((int)(scale_max / 10 + 1)) * 10; 109 + 110 + if (center_reference_timing) { 111 + if (v_min < scale_min) 112 + scale_min = v_min; 113 + scale_min = ((int)(scale_min / 10)) * 10; 114 + 115 + // make sure reference timing stays centered 116 + float lower_range = reference_timing - scale_min; 117 + float upper_range = scale_max - reference_timing; 118 + if (lower_range > upper_range) { 119 + scale_max = reference_timing + lower_range; 120 + } else if (upper_range > lower_range) { 121 + scale_min = reference_timing - upper_range; 122 + } 123 + } 124 + } 125 + 126 + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, 127 + style.FrameRounding); 128 + 129 + _draw_grid(window, values_count, scale_min, scale_max, reference_timing, unit, 130 + inner_bb, frame_size); 131 + 132 + ImGuiPlotType plot_type = ImGuiPlotType_Lines; 133 + const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1; 134 + if (values_count >= values_count_min) { 135 + int res_w = ImMin((int)frame_size.x, values_count) + 136 + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); 137 + int item_count = 138 + values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); 139 + 140 + // Tooltip on hover 141 + int v_hovered = -1; 142 + if (hovered && inner_bb.Contains(g.IO.MousePos)) { 143 + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / 144 + (inner_bb.Max.x - inner_bb.Min.x), 145 + 0.0f, 0.9999f); 146 + const int v_idx = (int)(t * item_count); 147 + IM_ASSERT(v_idx >= 0 && v_idx < values_count); 148 + 149 + const float v0 = 150 + values_getter(data, (v_idx + values_offset) % values_count); 151 + const float v1 = 152 + values_getter(data, (v_idx + 1 + values_offset) % values_count); 153 + if (plot_type == ImGuiPlotType_Lines) 154 + SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx + 1, v1); 155 + else if (plot_type == ImGuiPlotType_Histogram) 156 + SetTooltip("%d: %8.4g", v_idx, v0); 157 + v_hovered = v_idx; 158 + } 159 + 160 + const float t_step = 1.0f / (float)res_w; 161 + const float inv_scale = 162 + (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); 163 + 164 + float v0 = values_getter(data, (0 + values_offset) % values_count); 165 + float t0 = 0.0f; 166 + ImVec2 tp0 = ImVec2( 167 + t0, 1.0f - ImSaturate((v0 - scale_min) * 168 + inv_scale)); // Point in the normalized space of 169 + // our target rectangle 170 + float histogram_zero_line_t = 171 + (scale_min * scale_max < 0.0f) 172 + ? (-scale_min * inv_scale) 173 + : (scale_min < 0.0f ? 0.0f 174 + : 1.0f); // Where does the zero line stands 175 + 176 + const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) 177 + ? ImGuiCol_PlotLines 178 + : ImGuiCol_PlotHistogram); 179 + const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) 180 + ? ImGuiCol_PlotLinesHovered 181 + : ImGuiCol_PlotHistogramHovered); 182 + 183 + for (int n = 0; n < res_w; n++) { 184 + const float t1 = t0 + t_step; 185 + const int v1_idx = (int)(t0 * item_count + 0.5f); 186 + IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); 187 + const float v1 = 188 + values_getter(data, (v1_idx + values_offset + 1) % values_count); 189 + const ImVec2 tp1 = 190 + ImVec2(t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale)); 191 + 192 + // NB: Draw calls are merged together by the DrawList system. Still, we 193 + // should render our batch are lower level to save a bit of CPU. 194 + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); 195 + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, 196 + (plot_type == ImGuiPlotType_Lines) 197 + ? tp1 198 + : ImVec2(tp1.x, histogram_zero_line_t)); 199 + 200 + if (plot_type == ImGuiPlotType_Lines) { 201 + window->DrawList->AddLine(pos0, pos1, 202 + v_hovered == v1_idx ? col_hovered : col_base); 203 + } else if (plot_type == ImGuiPlotType_Histogram) { 204 + if (pos1.x >= pos0.x + 2.0f) 205 + pos1.x -= 1.0f; 206 + window->DrawList->AddRectFilled( 207 + pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); 208 + } 209 + 210 + t0 = t1; 211 + tp0 = tp1; 212 + } 213 + } 214 + 215 + // Text overlay 216 + if (overlay_text) 217 + RenderTextClipped( 218 + ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), 219 + frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f)); 220 + 221 + const float v = values_getter(data, (values_offset)); 222 + ImGui::LabelText(label, "%6.2f %s [%6.2f, %6.2f]", v, unit, v_min, v_max); 223 + } 224 + 225 + extern "C" { 226 + void igPlotTimings(const char *label, 227 + float (*values_getter)(void *data, int idx), void *data, 228 + int values_count, int values_offset, 229 + const char *overlay_text, float scale_min, float scale_max, 230 + ImVec2 frame_size, float reference_timing, 231 + bool center_reference_timing, float range, char *unit, 232 + bool dynamic_rescale) { 233 + PlotTimings(label, values_getter, data, values_count, values_offset, 234 + overlay_text, frame_size, reference_timing, 235 + center_reference_timing, range, unit, dynamic_rescale); 236 + } 237 + }