/* DrawState.cpp */ #include "DrawState.hpp" #include "AnimateGcCopyCb.hpp" #include "GcStatistics.hpp" #include "xo/indentlog/scope.hpp" using xo::gc::GcStatisticsHistory; using xo::gc::generation; using xo::scope; using xo::xtag; xo::up DrawState::make_gc_copy_animation(AppState * app_state) { return std::make_unique(app_state, this); } /** * @p polarity 0 -> draw from-space above to-space; 1 -> draw from-space below to-space * @p p_x1 On exit *p_x1 contains x-coord of right-hand edge of rectangle * depicting potential memory range **/ void DrawState::draw_generation(const GenerationLayout & layout, ImDrawList * draw_list) { //scope log(XO_DEBUG(with_labels)); using xo::gc::generation; /* next GC trigges when G0_to_size reaches this threshold */ std::size_t G_to_gc_threshold = 0; G_to_gc_threshold = layout.gendescr_.gc_threshold_; std::size_t G_to_scale = layout.gendescr_.scale(); /* * committed: G_to_committed * G1: G1_to_size * G0: G0_to_size * ckp: G1_to_size * ngc: G_to_gc_threshold * * <----------------------------- committed ---------------------------> * <------------------ used ------------------> <-------- free --------> * <------- G1 --------> <-------- G0 --------> * |NNNNNNNNNNNNNNNNNNNNN|nnnnnnnnnnnnnnnnnnnnnn|________________________| * ^ ^ * ckp ngc * * in screen coords: * * horizontally: * * rect.x_lo rect.x_hi * v v * * <--------------------------- display_w -----------------------------> <-+-> * <------------------------ ngc_w ----------------------> \- rh_text_dx * <------- G1_w ------> <-------- G0_w ------> * ^ ^ ^ ^ ^ * x0 G0_x0 G0_x1 ngc_w x1 * *p_x0 *p_g0_x1 *p_x1 * * vertically: * * <- rect.y_lo * ^ * * v * <- rect.y_hi */ /* e.g. N1: 34511 bytes */ char g1_buf[255]; ImU32 label_color = IM_COL32(255, 255, 192, 255); /*super pale yellow*/ if (layout.with_labels_) { snprintf(g1_buf, sizeof(g1_buf), "reserved: %lu bytes; committed: %lu bytes; %s\u2081: %lu bytes; %s\u2080: %lu bytes", layout.gendescr_.reserved_, layout.gendescr_.committed_, layout.mnemonic(), layout.gendescr_.before_checkpoint_, layout.mnemonic(), layout.gendescr_.after_checkpoint_); draw_list->AddText(layout.bounding_rect_.top_left(), label_color, g1_buf); } if (layout.with_labels_) { auto textz = ImGui::CalcTextSize(layout.rh_text_.c_str()); draw_list->AddText(ImVec2(layout.chart_withlabel_rect_.x_hi() - textz.x, layout.chart_withlabel_rect_.y_mid() - 0.5 * textz.y), label_color, layout.rh_text_.c_str()); } ImU32 outline_color = IM_COL32(255, 255, 255, 255); /*white*/ /* chart rectangle */ draw_list->AddRect(layout.mem_rect_from_.top_left(), layout.mem_rect_from_.bottom_right(), outline_color); draw_list->AddRect(layout.mem_rect_to_.top_left(), layout.mem_rect_to_.bottom_right(), outline_color); float display_w = layout.mem_rect_from_.width(); float G1_w = display_w * layout.to_G1_size() / layout.to_scale(); float G1_x1 = layout.mem_rect_from_.x_lo() + G1_w; ImRect G1_rect = layout.to_g1_rect(); //ImRect G1_rect = layout.mem_rect_to_.left_fraction(layout.to_G1_size() / layout.to_scale()); /* G1 (i.e. N1 or T1) */ { ImU32 G1_color = IM_COL32( 0, 128, 0, 255); ImU32 text_color = IM_COL32(255, 255, 255, 255); char buf[255]; if (layout.with_labels_) snprintf(buf, sizeof(buf), "%s\u2081: %luk", layout.mnemonic(), layout.to_G1_size() / 1024); /* N1 / T1 */ char tooltip[255]; snprintf(tooltip, sizeof(tooltip), "%s\u2081: %lu - %s survivor size in bytes", layout.mnemonic(), layout.to_G1_size(), layout.name()); ImRect::draw_filled_rect_with_label (layout.with_labels_ ? buf : nullptr, tooltip, G1_rect, G1_color, text_color, draw_list); } ImRect G0_rect = layout.to_g0_rect(); /* G0 (i.e. N0 or T0) */ { ImU32 G0_color = IM_COL32( 32, 192, 32, 255); ImU32 text_color = IM_COL32( 0, 0, 0, 255); char buf[255]; if (layout.with_labels_) snprintf(buf, sizeof(buf), "%s\u2080: %luk", layout.mnemonic(), layout.to_G0_size() / 1024); /* N(0) */ char tooltip[255]; snprintf(tooltip, sizeof(tooltip), "%s\u2080: %lu - %s new alloc size in bytes", layout.mnemonic(), layout.to_G0_size(), layout.name()); ImRect::draw_filled_rect_with_label (layout.with_labels_ ? buf : nullptr, tooltip, G0_rect, G0_color, text_color, draw_list); } /* mark where next gc will trigger */ if (layout.with_labels_) { const char * uparrow = reinterpret_cast(u8"\u25b3"); float ngc_w = (display_w * layout.gendescr_.gc_threshold_) / G_to_scale; auto tmp = ImGui::CalcTextSize(uparrow); std::size_t uparrow_w = tmp.x; double ngc_x = layout.chart_withlabel_rect_.x_lo() + ngc_w - uparrow_w/2.0; ImVec2 marker_pos(ngc_x, layout.chart_withlabel_rect_.y_hi()); draw_list->AddText(marker_pos, IM_COL32(255, 128, 128, 255) /*red*/, uparrow); ImGui::SetCursorScreenPos(marker_pos); ImGui::InvisibleButton("mkbutton", tmp); if (ImGui::IsItemHovered()) { char marker_tt_buf[255]; snprintf(marker_tt_buf, sizeof(marker_tt_buf), "Next %s GC when size(%s) >= %lu bytes", layout.gc_type(), layout.name(), layout.gendescr_.gc_threshold_); ImGui::SetTooltip("%s", marker_tt_buf); } } } /*draw_generation*/ void DrawState::draw_nursery(const GcStateDescription & gcstate, bool with_labels, const ImRect & rect, ImDrawList * draw_list, GenerationLayout * p_layout) { using xo::gc::generation; const GcGenerationDescription & gendescr = gcstate.get_gendescr(generation::nursery); GenerationLayout layout(gendescr, rect, with_labels); draw_generation(layout, draw_list); if (p_layout) *p_layout = layout; } void DrawState::draw_tenured(const GcStateDescription & gcstate, bool with_labels, const ImRect & rect, ImDrawList * draw_list, GenerationLayout * p_layout) { using xo::gc::generation; const GcGenerationDescription & gendescr = gcstate.get_gendescr(generation::tenured); GenerationLayout layout(gendescr, rect, with_labels); draw_generation(layout, draw_list); if (p_layout) *p_layout = layout; } #ifdef nope ImRect::draw_filled_rect (tt.c_str(), ImRect::from_xy_span(x_span, ImVec2(ypsz_lo, y_zero)), persist_color, draw_list); #endif ImU32 DrawState::headline_color(gc_history_headline headline) { constexpr ImU32 sentinel_color = IM_COL32(255, 255, 255, 255); constexpr ImU32 persist_color = IM_COL32( 0, 64, 192, 255); /*darker blue*/ ImU32 promote_color = IM_COL32( 0, 128, 0, 255); /*darker green*/ ImU32 survive_color = IM_COL32( 32, 192, 32, 255); /*lighter green*/ ImU32 garbageN_color = IM_COL32(255, 128, 64, 255); /*darker orange*/ ImU32 garbage1_color = IM_COL32(255, 192, 128, 255); /*medium orange*/ ImU32 garbage0_color = IM_COL32(255, 255, 192, 255); /*pale yellow*/ switch(headline) { case gc_history_headline::survive: return survive_color; case gc_history_headline::promote: return promote_color; case gc_history_headline::persist: return persist_color; case gc_history_headline::garbage0: return garbage0_color; case gc_history_headline::garbage1: return garbage1_color; case gc_history_headline::garbageN: return garbageN_color; case gc_history_headline::N: assert(false); break; } return sentinel_color; } void DrawState::draw_text_bullet(ImU32 fill_color, ImDrawList * draw_list) { float tx_h = ImGui::GetTextLineHeight(); ImVec2 cursor_pos = ImGui::GetCursorScreenPos(); ImVec2 circle_pos = cursor_pos; float circle_radius = 0.2 * tx_h; draw_list->AddCircleFilled(ImVec2(circle_pos.x, circle_pos.y + 0.5 * tx_h), circle_radius, fill_color); ImGui::SetCursorScreenPos(ImVec2(cursor_pos.x + circle_radius, cursor_pos.y)); } void DrawState::write_gc_history_bar(const char * idname, gc_history_headline headline, const GcStatisticsHistoryItem & stats, const ImRect & bar_rect, ImDrawList * draw_list) { ImU32 fill_color = headline_color(headline); draw_list->AddRectFilled(bar_rect.top_left(), bar_rect.bottom_right(), fill_color); if (bar_rect.is_nonempty()) { ImGui::SetCursorScreenPos(bar_rect.top_left()); ImGui::InvisibleButton(idname, ImVec2(bar_rect.width(), bar_rect.height())); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); ImDrawList * tt_draw_list = ImGui::GetWindowDrawList(); draw_text_bullet(fill_color, tt_draw_list); /* choose text here */ switch(headline) { case gc_history_headline::survive: ImGui::Text("survive: %lu: bytes surviving 1st GC after allocation", stats.survive_z_); break; case gc_history_headline::promote: ImGui::Text("promote: %lu: bytes surviving 2nd GC; if nursery promote to tenured", stats.promote_z_); break; case gc_history_headline::persist: ImGui::Text("persist: %lu: bytes surviving 3+ GCs. Only non-zero for full collections", stats.persist_z_); break; case gc_history_headline::garbage0: ImGui::Text("garbage\u2080: %lu: bytes collected on 1st GC after allocation", stats.garbage0_z_); break; case gc_history_headline::garbage1: ImGui::Text("garbage\u2081: %lu: bytes collected on 2nd GC after allocation", stats.garbage1_z_); break; case gc_history_headline::garbageN: ImGui::Text("garbage\u2099: %lu: bytes collected on 3rd or later GC after allocation", stats.garbageN_z_); break; case gc_history_headline::N: assert(false); break; } ImGui::Separator(); ImGui::Text(" gcseq: %lu\n" " type: %s\n" " alloc: %lu\n", stats.gc_seq_, (stats.upto_ == generation::nursery) ? "incremental" : "FULL", stats.new_alloc_z_); { draw_text_bullet(headline_color(gc_history_headline::survive), tt_draw_list); ImGui::Text(" survive: %lu - survived 1 gc cycle", stats.survive_z_); } { draw_text_bullet(headline_color(gc_history_headline::promote), tt_draw_list); ImGui::Text(" promote: %lu - survived 2 gc cycles", stats.promote_z_); } { draw_text_bullet(headline_color(gc_history_headline::persist), tt_draw_list); ImGui::Text(" persist: %lu - survived 3+ gc cycles\n", stats.persist_z_); } { draw_text_bullet(headline_color(gc_history_headline::garbage0), tt_draw_list); /* "garbage0", but with "0" subscripted */ ImGui::Text(" garbage\u2080: %lu - survived 0 gc cycles", stats.garbage0_z_); } { draw_text_bullet(headline_color(gc_history_headline::garbage1), tt_draw_list); /* "garbage1" but with "1" subscripted */ ImGui::Text(" garbage\u2081: %lu - survived 1 gc cycles", stats.garbage1_z_); } { draw_text_bullet(headline_color(gc_history_headline::garbageN), tt_draw_list); /* "garbageN" but with "N" subscripted */ ImGui::Text(" garbage\u2099: %lu - survived 2+ gc cycles", stats.garbageN_z_); } ImGui::Text(" effort: %lu dt: %.1lfus\n" " copy efficiency: %.1lf%% collection rate: %.0lf bytes/sec", stats.effort_z_, 1e-3 * stats.dt_.scale(), 100.0 * stats.efficiency(), stats.collection_rate() ); ImGui::EndTooltip(); } } } /*write_gc_history_bar*/ /** stacked bar chart * * @param gen if @ref generation::nursery, only display nursery collections. * otherwise display both **/ void DrawState::draw_gc_history(const GcStateDescription & gcstate, generation gen, const GcStatisticsHistory & gc_history, const ImRect & bounding_rect, bool debug_flag, ImDrawList * draw_list) { scope log(XO_DEBUG(debug_flag)); float lm = 10; float tm = 25; /* we're going to make a bar chart */ /* x_scale,y_scale in GC units (i.e. bytes) */ size_t x_scale = gc_history.capacity(); size_t yplus_scale = 0; size_t yminus_scale = 0; float display_w = bounding_rect.width() - lm; float display_h = bounding_rect.height() - tm; /* 1st loop: figure out max y scale */ for (const GcStatisticsHistoryItem & stats : gc_history) { if ((gen == stats.upto_) || (gen == generation::tenured)) { //size_t na = stats.new_alloc_z_ - stats.survive_z_; /*new allocs, but dont' double-count survive_z*/ size_t sz = stats.survive_z_; /*survive 1st gc */ size_t pz = stats.promote_z_; /*survive 2nd gc */ size_t psz = stats.persist_z_; /*survive 3+ gc */ size_t g0z = stats.garbage0_z_; size_t g1z = stats.garbage1_z_; size_t gNz = stats.garbageN_z_; if (yplus_scale < sz + pz + psz) yplus_scale = sz + pz + psz; if (yminus_scale < g0z + g1z + gNz) yminus_scale = g0z + g1z + gNz; } else { ; } } /* y-coord of x-axis */ float y_zero = bounding_rect.y_lo() + tm + (display_h * yplus_scale) / (yplus_scale + yminus_scale); float y_scale = yplus_scale + yminus_scale; /* width of 1 bar in screen coords */ constexpr float c_min_bar_w = 5.0; float bar_w = std::max(c_min_bar_w, display_w / gc_history.capacity()); /* 2nd loop: draw bars */ std::size_t i = 0; for (const GcStatisticsHistoryItem & stats : gc_history) { ImGui::PushID(i); if ((gen == stats.upto_) || (gen == generation::tenured)) { /* * ys_lo +--+ * | | survive_z (survived 1st GC) * | | * yp_lo +--+ * | | promote_z (sruvived 2nd GC) * | | * ypsz_lo +--+ * | | persist_z (survived 3+ GCs) * | | * y_zero +--+ * | | gN (killed on 3+ GC) * | | * ygN_hi +--+ * | | g1 (killed on 2nd GC) * | | * yg1_hi +--+ * | | g0 (killed on 1st GC) * | | * yg0_hi +--+ */ /* x-coordinates of bar */ float x_lo = bounding_rect.x_lo() + lm + i * bar_w; float x_hi = x_lo + bar_w - 1; ImVec2 x_span{x_lo, x_hi}; /* y-coordinates of persist bar (survived 3+ GCs) */ float ypsz_lo = (y_zero - (display_h * stats.persist_z_ / y_scale)); { write_gc_history_bar("##persist", gc_history_headline::persist, stats, ImRect::from_xy_span(x_span, ImVec2(ypsz_lo, y_zero)), draw_list); } /* y-coordinates of promote bar (survived 2nd GC) */ float yp_hi = ypsz_lo; float yp_lo = (yp_hi - (display_h * stats.promote_z_ / y_scale)); { write_gc_history_bar("##promote", gc_history_headline::promote, stats, ImRect::from_xy_span(x_span, ImVec2(yp_lo, yp_hi)), draw_list); } /* y-coordinates of survivor bar (survived 1st GC) */ float ys_hi = yp_lo; float ys_lo = (ys_hi - (display_h * stats.survive_z_ / y_scale)); { write_gc_history_bar("##survivor", gc_history_headline::survive, stats, ImRect::from_xy_span(x_span, ImVec2(ys_lo, ys_hi)), draw_list); } // ----------------------------------------------------------- /* y-coordinates of garbageN bar (killed on 3+ GC) */ float ygN_lo = y_zero; float ygN_hi = (y_zero + (display_h * stats.garbageN_z_ / y_scale)); { write_gc_history_bar("##garbageN", gc_history_headline::garbageN, stats, ImRect::from_xy_span(x_span, ImVec2(ygN_lo, ygN_hi)), draw_list); } /* y-coordinates of garbage1 bar (killed on 2nd GC) */ float yg1_lo = ygN_hi; float yg1_hi = (yg1_lo + (display_h * stats.garbage1_z_ / y_scale)); { write_gc_history_bar("##garbage1", gc_history_headline::garbage1, stats, ImRect(ImVec2(x_lo, yg1_lo), ImVec2(x_hi, yg1_hi)), draw_list); } /* y-coordinates of garbage0 bar (killed on 1st GC) */ float yg0_lo = yg1_hi; float yg0_hi = (yg0_lo + (display_h * stats.garbage0_z_ / y_scale)); { write_gc_history_bar("##garbage0", gc_history_headline::garbage0, stats, ImRect(ImVec2(x_lo, yg0_lo), ImVec2(x_hi, yg0_hi)), draw_list); } } else { /* draw nothing */ ; } ++i; ImGui::PopID(); } log && log(xtag("i", i)); } void DrawState::draw_gc_efficiency(const GcStateDescription & gcstate, //generation gen, const GcStatisticsHistory & gc_history, const ImRect & bounding_rect, bool debug_flag, ImDrawList * draw_list) { scope log(XO_DEBUG(debug_flag)); float lm = 10; float tm = 25; /* we're going to make a level chart */ /* x_scale,y_scale in GC units (i.e. bytes) */ size_t x_scale = gc_history.capacity(); size_t yplus_scale = 1; size_t yminus_scale = 0; float display_w = bounding_rect.width() - lm; float display_h = bounding_rect.height() - tm; #ifdef NOPE // don't need this. y-scale is [0.0, 1.0] /* 1st loop: figure out max y scale */ for (const GcStatisticsHistoryItem & stats : gc_history) { if ((gen == stats.upto_) || (gen == generation::tenured)) { //size_t na = stats.new_alloc_z_ - stats.survive_z_; /*new allocs, but dont' double-count survive_z*/ size_t sz = stats.survive_z_; /*survive 1st gc */ size_t pz = stats.promote_z_; /*survive 2nd gc */ size_t psz = stats.persist_z_; /*survive 3+ gc */ size_t g0z = stats.garbage0_z_; size_t g1z = stats.garbage1_z_; size_t gNz = stats.garbageN_z_; if (yplus_scale < sz + pz + psz) yplus_scale = sz + pz + psz; if (yminus_scale < g0z + g1z + gNz) yminus_scale = g0z + g1z + gNz; } else { ; } } #endif /* y-coord of x-axis */ float y_zero = bounding_rect.y_lo() + tm + display_h; //float y_zero = bounding_rect.y_lo() + tm + (display_h * yplus_scale) / (yplus_scale + yminus_scale); float y_scale = 1.0; /* width of 1 bar in screen coords */ constexpr float c_min_bar_w = 5.0; float bar_w = std::max(c_min_bar_w, display_w / gc_history.capacity()); /* TODO: use temporary arena */ std::vector line_points; line_points.reserve(gc_history.size()); ImU32 average_color = IM_COL32(255, 255, 64, 255); /*solid yellow*/ ImU32 sample_color = IM_COL32(255, 255, 255, 255); /*white*/ /* 2nd loop: draw levels */ std::size_t i = 0; for (const GcStatisticsHistoryItem & stats : gc_history) { //std::vector line_points = { /* your points */ }; //draw_list->AddPolyline(line_points.data(), line_points.size(), // IM_COL32(255, 255, 0, 255), false, 2.0f); float y = y_zero - display_h * stats.efficiency(); float y_mean = y_zero - display_h * stats.average_efficiency(); /* x-coordinates of point */ float x = bounding_rect.x_lo() + lm + i * bar_w + 0.5 * bar_w; line_points.push_back(ImVec2(x, y_mean)); draw_list->AddCircleFilled(ImVec2(x, y), 2.0f, sample_color); ++i; } draw_list->AddPolyline(line_points.data(), line_points.size(), average_color, false, 1.0f /*line width?*/); log && log(xtag("i", i)); } /*draw_gc_efficiency*/ void DrawState::draw_gc_alloc_state(const GcStateDescription & gcstate, const ImRect & canvas_rect, ImDrawList * draw_list, GenerationLayout * p_nursery_layout, GenerationLayout * p_tenured_layout) { constexpr float c_est_chart_text_height = 14.0; constexpr float c_min_h = 7; // chart bar height constexpr float c_max_h = 40; // chart bar height /* bounding rectange for nursery display */ ImRect N_space_rect = canvas_rect.top_fraction(0.5, c_min_h + c_est_chart_text_height, c_max_h + c_est_chart_text_height); //assert(N_space_rect.height() >= c_min_h + c_est_chart_text_height); //assert(N_space_rect.height() <= c_max_h + c_est_chart_text_height); /* rectangle representing allocated nursery range */ //float N_x1 = 0.0; GenerationLayout N_layout; draw_nursery(gcstate, true /*with_labels*/, N_space_rect, draw_list, &N_layout); float N_x1 = N_layout.chart_nolabel_rect_.x_hi(); if (p_nursery_layout) *p_nursery_layout = N_layout; // if (p_nursery_alloc_rect) // *p_nursery_alloc_rect = N_layout.to_alloc_rect(); /* N0_to_size..N_to_scale: in bytes */ std::size_t N_to_scale = gcstate.gen_state_v_[gen2int(generation::nursery)].tospace_scale_; /* display_w .. N0_h : viewportcoords */ std::size_t display_w = canvas_rect.width(); std::size_t x0 = canvas_rect.x_lo(); std::size_t x1 = canvas_rect.x_hi(); // now turn to Tenured space std::size_t T_to_scale = gcstate.gen_state_v_[gen2int(generation::tenured)].tospace_scale_; /* want to put to-scale image of nursery next to to-scale image of tenured; * but also want space between them. */ float TplusN_to_scale = N_to_scale + T_to_scale; /* space between T, N images */ float TplusN_spacer = 10; /* bounding rectange for tenured display */ ImRect T_space_rect = (canvas_rect .within_bottom_margin(c_est_chart_text_height) .bottom_fraction(0.5, c_min_h + c_est_chart_text_height, c_max_h + c_est_chart_text_height)); if (N_space_rect.y_hi() > T_space_rect.y_lo()) { T_space_rect = T_space_rect.translate(ImVec2(0, N_space_rect.y_hi() - T_space_rect.y_lo())); } assert(T_space_rect.y_lo() >= N_space_rect.y_hi()); /* for smaller image of nursery */ //std::size_t t_y0 = canvas_rect.y_lo() + 70 + alloc_height + 20; /* for side-by-side tenured + nursery, with both on same scale * 2nd term is horiz space used for N label like 'Mem: 28k' */ std::size_t adj_display_w = display_w - (N_space_rect.x_hi() - N_x1); /* bounding rectangle for secondary nursery display */ ImRect np_rect(ImVec2(x0 + (adj_display_w * (T_to_scale/TplusN_to_scale)), T_space_rect.y_lo() + c_est_chart_text_height), ImVec2(x0 + adj_display_w, T_space_rect.y_hi())); // redraw nursery to same scale as tenured { draw_list->AddLine(N_space_rect.bottom_left(), np_rect.top_left(), IM_COL32(128, 128, 128, 255) /*grey*/); draw_list->AddLine(ImVec2(N_x1, N_space_rect.y_hi()), np_rect.top_right(), IM_COL32(128, 128, 128, 255) /*grey*/); draw_nursery(gcstate, false /*no labels*/, np_rect, draw_list, nullptr); } /* rectangle representing allocated tenured range */ GenerationLayout T_layout; draw_tenured(gcstate, true /*with labels*/, ImRect(ImVec2(x0, T_space_rect.y_lo()), ImVec2(x0 + (adj_display_w * (T_to_scale/TplusN_to_scale)) - TplusN_spacer, T_space_rect.y_hi())), draw_list, &T_layout); if (p_tenured_layout) *p_tenured_layout = T_layout; // if (p_tenured_alloc_rect) // *p_tenured_alloc_rect = T_layout.to_alloc_rect(); } /*draw_gc_alloc_state*/ void DrawState::draw_gc_state(const AppState & app_state, const GcStateDescription & gcstate, const ImRect & canvas_rect, ImDrawList * draw_list, GenerationLayout * p_nursery_layout, GenerationLayout * p_tenured_layout, ImRect * p_history_rect) { // draw stuff draw_list->AddRect(canvas_rect.top_left(), canvas_rect.bottom_right(), IM_COL32(255, 255, 255, 255)); /* TODO: does this reset coord space? */ ImRect alloc_rect; { ImGui::BeginChild("top pane", ImVec2(0, 200), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY); alloc_rect = ImRect(canvas_rect.top_left() + ImGui::GetWindowContentRegionMin(), canvas_rect.top_left() + ImGui::GetWindowContentRegionMax()); ImRect draw_rect = alloc_rect.within_margin(ImRect(50, 10, 70, 10)); draw_list->PushClipRect(draw_rect.top_left(), draw_rect.bottom_right()); draw_gc_alloc_state(gcstate, draw_rect, draw_list, p_nursery_layout, p_tenured_layout //p_nursery_alloc_rect, //p_tenured_alloc_rect ); draw_list->PopClipRect(); ImGui::EndChild(); } ImRect history_rect; { ImGui::BeginChild("left pane", ImVec2(800, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX); /* history below alloc area */ history_rect = ImRect(alloc_rect.bottom_left() + ImGui::GetWindowContentRegionMin(), alloc_rect.bottom_left() + ImGui::GetWindowContentRegionMax()); if (p_history_rect) *p_history_rect = history_rect; draw_list->PushClipRect(history_rect.top_left(), history_rect.bottom_right()); float lm = 50; float rm = 70; float tm = 10; std::size_t x0 = history_rect.x_lo() + lm; std::size_t x1 = history_rect.x_hi() - rm; std::size_t h_y0 = history_rect.y_lo() + tm; /* just incremental (nursery) collections */ ImRect incremental_rect = history_rect.top_fraction(0.33); draw_gc_history(gcstate, generation::nursery, app_state.gc_->gc_history(), incremental_rect, false /*debug_flag*/, draw_list); /* just full (nursery+tenured) collections */ ImRect full_rect = history_rect.bottom_fraction(0.67).top_fraction(0.5); /* both nursery + full collections */ draw_gc_history(gcstate, generation::tenured, app_state.gc_->gc_history(), full_rect, false /*debug_flag*/, draw_list); ImRect efficiency_rect = history_rect.bottom_fraction(0.67).bottom_fraction(0.5); draw_gc_efficiency(gcstate, app_state.gc_->gc_history(), efficiency_rect, false /*debug_flag*/, draw_list); draw_list->PopClipRect(); ImGui::EndChild(); } //ImGui::Text("placeholder text"); // appears below history_rect /* BeginChild() again ? */ #ifdef NOPE draw_list->AddCircleFilled(ImVec2(canvas_p0.x + 50, canvas_p0.y + 50), 30.0f, IM_COL32(255, 0, 0, 255)); draw_list->AddText(ImVec2(canvas_p0.x + 10, canvas_p0.y + 10), IM_COL32(255, 255, 255, 255), "Hello 2D!"); #endif } ImRect DrawState::map_src_alloc_to_screen(const GcCopyDetail & copy_detail, const ImRect & space_rect) { // TODO: methods on copy_detail / and/or ImPoint auto [x_coord_lo, x_coord_hi] = space_rect.x_span(); double w0 = copy_detail.src_offset_ / static_cast(copy_detail.src_space_z_); float src0_x = ((1.0 - w0) * x_coord_lo) + (w0 * x_coord_hi); double w1 = ((copy_detail.src_offset_ + copy_detail.z_) / static_cast(copy_detail.src_space_z_)); float src1_x = ((1.0 - w1) * x_coord_lo) + (w1 * x_coord_hi); return space_rect.with_x_span(src0_x, src1_x); } ImRect DrawState::map_dest_alloc_to_screen(const GcCopyDetail & copy_detail, const ImRect & space_rect) { /* for from-space, want to use full width of memory space */ auto [x_coord_lo, x_coord_hi] = space_rect.x_span(); // dest_space_z_ ? double w0 = copy_detail.dest_offset_ / static_cast(copy_detail.dest_z_); float dest0_x = ((1.0 - w0) * x_coord_lo) + (w0 * x_coord_hi); double w1 = ((copy_detail.dest_offset_ + copy_detail.z_) / static_cast(copy_detail.dest_z_)); float dest1_x = ((1.0 - w1) * x_coord_lo) + (w1 * x_coord_hi); return space_rect.with_x_span(dest0_x, dest1_x); } /* editor bait: animate_copy() */ void DrawState::animate_gc_copy(const AppState & app_state, const DrawState & draw_state, ImDrawList * draw_list) { /* NOTE: this only runs during GC copy. * draw_state.gcw_nursery_layout_ and gcw_tenured_layout_ * are taken from snapshots made before GC began, * ergo before to/from spaces were swapped */ ImRect nursery_src_rect = draw_state.gcw_nursery_layout_.to_alloc_rect(); ImRect tenured_src_rect = draw_state.gcw_tenured_layout_.to_alloc_rect(); ImRect nursery_dest_rect = draw_state.gcw_nursery_layout_.mem_rect_from_; ImRect tenured_dest_rect; if (app_state.upto_ == generation::nursery) { tenured_dest_rect = draw_state.gcw_tenured_layout_.mem_rect_to_; } else { tenured_dest_rect = draw_state.gcw_tenured_layout_.mem_rect_from_; } std::size_t n_copy = app_state.copy_detail_v_.size(); /* grade from black-to-white between lo_copy and hi_copy. * Note we allow animate_copy_hi_pct_ > 100.0 */ float lo_copy = 0.01 * std::max(0.0, draw_state.animate_copy_hi_pct_ - 14.0) * n_copy; float hi_copy = 0.01 * draw_state.animate_copy_hi_pct_ * n_copy; /* remember max copy offset seen in {nursery, tenured} space respectively, * so we can label it */ std::size_t last_nursery_dest_offset = 0; ImRect last_nursery_dest_rect; std::size_t first_tenured_dest_offset = 0; ImRect first_tenured_dest_rect; std::size_t last_tenured_dest_offset = 0; ImRect last_tenured_dest_rect; std::size_t i_copy = 0; for (const auto & copy_detail : app_state.copy_detail_v_) { /* cutout for each copied object */ { float wt = (i_copy > lo_copy) ? static_cast(i_copy) / hi_copy : 0.0; /* grey fading to black */ //ImU32 color = IM_COL32(wt*128, 64+wt*64, wt*128, 255); ImU32 color = IM_COL32( 96, 224, 255, 255); ImRect src_rect; if (copy_detail.src_gen_ == generation::nursery) { src_rect = map_src_alloc_to_screen(copy_detail, nursery_src_rect); } else { src_rect = map_src_alloc_to_screen(copy_detail, tenured_src_rect); } draw_list->AddRectFilled(src_rect.top_left(), src_rect.bottom_right(), color); } if (copy_detail.dest_gen_ == generation::nursery) { last_nursery_dest_rect = map_dest_alloc_to_screen(copy_detail, nursery_dest_rect); last_nursery_dest_offset = copy_detail.dest_offset_; } else if (copy_detail.dest_gen_ == generation::tenured) { last_tenured_dest_rect = map_dest_alloc_to_screen(copy_detail, tenured_dest_rect); last_tenured_dest_offset = copy_detail.dest_offset_; if (first_tenured_dest_rect.width() == 0) { first_tenured_dest_rect = last_tenured_dest_rect; first_tenured_dest_offset = copy_detail.dest_offset_; } } if (++i_copy >= hi_copy) { break; } } if (last_nursery_dest_rect.width() > 0.0) { //ImU32 color = IM_COL32(64, 255, static_cast(64 + (128 * wt)), 255); ImU32 color = IM_COL32( 0, 96, 192, 255); draw_list->AddRectFilled(nursery_dest_rect.top_left(), last_nursery_dest_rect.bottom_right(), color); char buf[255]; snprintf(buf, sizeof(buf), "N\u2081: %luk", last_nursery_dest_offset / 1024); auto textz = ImGui::CalcTextSize(buf); ImU32 text_color = IM_COL32(255, 255, 255, 255); /*black*/ ImVec2 text_pos = ImVec2(0.5 * (nursery_dest_rect.x_lo() + last_nursery_dest_rect.x_hi() - textz.x), nursery_dest_rect.y_mid() - 0.5 * textz.y); if (text_pos.x < nursery_dest_rect.x_lo()) text_pos.x = nursery_dest_rect.x_lo() + 2; else if (text_pos.x < nursery_dest_rect.x_lo() + 0.5 * textz.x) text_pos.x = nursery_dest_rect.x_lo() + 0.5 * textz.x; draw_list->AddText(text_pos, text_color, buf); } if (last_tenured_dest_rect.width() > 0.0) { ImU32 color = IM_COL32( 0, 96, 192, 255); draw_list->AddRectFilled(first_tenured_dest_rect.top_left(), last_tenured_dest_rect.bottom_right(), color); char buf[255]; snprintf(buf, sizeof(buf), "+%luk", (last_tenured_dest_offset - first_tenured_dest_offset) / 1024); auto textz = ImGui::CalcTextSize(buf); ImU32 text_color = IM_COL32(255, 255, 255, 255); /*black*/ float x0 = first_tenured_dest_rect.x_lo(); float x1 = last_tenured_dest_rect.x_hi(); ImVec2 text_pos = ImVec2(0.5 * (x0 + x1 - textz.x), tenured_dest_rect.y_mid() - 0.5 * textz.y); if (text_pos.x < x0 + 2) text_pos.x = x0 + 2; else if (text_pos.x < x0 + 0.5 * textz.x) text_pos.x = x0 + 0.5 * textz.x; draw_list->AddText(text_pos, text_color, buf); } } /* end DrawState.cpp */