xo-umbrella2/xo-imgui/example/ex4/DrawState.cpp

1006 lines
36 KiB
C++

/* 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<xo::gc::GcCopyCallback>
DrawState::make_gc_copy_animation(AppState * app_state)
{
return std::make_unique<AnimateGcCopyCb>(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<const char *>(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;
}
auto
DrawState::write_gc_history_tooltip(gc_history_headline headline,
const GcStatisticsHistoryItem & stats)
-> TooltipText
{
xo::flatstring<512> retval;
xo::flatstring<256> headline_str;
switch (headline) {
case gc_history_headline::survive:
snprintf(headline_str.data(), headline_str.capacity(),
"survive: %lu: bytes surviving 1st GC after allocation",
stats.survive_z_);
break;
case gc_history_headline::promote:
snprintf(headline_str.data(), headline_str.capacity(),
"promote: %lu: bytes surviving 2nd GC; if nursery promote to tenured",
stats.promote_z_);
break;
case gc_history_headline::persist:
snprintf(headline_str.data(), headline_str.capacity(),
"persist: %lu: bytes surviving 3+ GCs. Only non-zero for full collections",
stats.persist_z_);
break;
case gc_history_headline::garbage0:
snprintf(headline_str.data(), headline_str.capacity(),
"garbage\u2080: %lu: bytes collected on 1st GC after allocation",
stats.garbage0_z_);
break;
case gc_history_headline::garbage1:
snprintf(headline_str.data(), headline_str.capacity(),
"garbage\u2081: %lu: bytes collected on 2nd GC after allocation",
stats.garbage1_z_);
break;
case gc_history_headline::garbageN:
snprintf(headline_str.data(), headline_str.capacity(),
"garbage\u2099: %lu: bytes collected on 3rd or later GC after allocation",
stats.garbageN_z_);
break;
case gc_history_headline::N:
assert(false);
break;
}
snprintf(retval.data(), retval.capacity(),
"%.*s\n"
"\n"
" gcseq: %lu\n"
" type: %s\n"
" alloc: %lu\n"
" survive: %lu\n"
" promote: %lu\n"
" persist: %lu\n"
" garbage\u2080: %lu\n" /*garbage0*/
" garbage\u2081: %lu\n" /*garbage1*/
" garbage\u2099: %lu\n" /*garbageN*/
" effort: %lu dt: %.1lfus\n"
" copy efficiency: %.1lf%% collection rate: %.0lf bytes/sec",
static_cast<int>(headline_str.capacity()), headline_str.c_str(),
stats.gc_seq_,
(stats.upto_ == generation::nursery) ? "incremental" : "FULL",
stats.new_alloc_z_,
stats.survive_z_,
stats.promote_z_,
stats.persist_z_,
stats.garbage0_z_,
stats.garbage1_z_,
stats.garbageN_z_,
stats.effort_z_,
1e-3 * stats.dt_.scale(),
100.0 * stats.efficiency(),
stats.collection_rate()
);
return retval.ensure_final_null();
} /*write_gc_history_tooltip*/
/** 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)
{
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 +--+
*/
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*/
/* 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));
{
xo::flatstring<512> tt = write_gc_history_tooltip(gc_history_headline::persist, stats);
ImRect::draw_filled_rect
(tt.c_str(),
ImRect::from_xy_span(x_span, ImVec2(ypsz_lo, y_zero)),
persist_color,
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));
{
xo::flatstring<512> tt = write_gc_history_tooltip(gc_history_headline::promote, stats);
ImRect::draw_filled_rect
(tt.c_str(),
ImRect::from_xy_span(x_span, ImVec2(yp_lo, yp_hi)),
promote_color,
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));
{
xo::flatstring<512> tt = write_gc_history_tooltip(gc_history_headline::survive, stats);
ImRect::draw_filled_rect
(tt.c_str(),
ImRect::from_xy_span(x_span, ImVec2(ys_lo, ys_hi)),
survive_color,
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));
{
xo::flatstring<512> tt = write_gc_history_tooltip(gc_history_headline::garbageN, stats);
ImRect::draw_filled_rect
(tt.c_str(),
ImRect::from_xy_span(x_span, ImVec2(ygN_lo, ygN_hi)),
garbageN_color,
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));
{
TooltipText tt = write_gc_history_tooltip(gc_history_headline::garbage1, stats);
ImRect::draw_filled_rect
(tt.c_str(),
ImRect(ImVec2(x_lo, yg1_lo), ImVec2(x_hi, yg1_hi)),
garbage1_color,
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));
{
TooltipText tt = write_gc_history_tooltip(gc_history_headline::garbage0, stats);
ImRect::draw_filled_rect
(tt.c_str(),
ImRect(ImVec2(x_lo, yg0_lo), ImVec2(x_hi, yg0_hi)),
garbage0_color,
draw_list);
}
} else {
/* draw nothing */
;
}
++i;
}
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<ImVec2> 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<ImVec2> 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<double>(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<double>(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<double>(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<double>(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<float>(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<int>(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 */