xo-imgui: imgui_ex4 builds. font uploading works
This commit is contained in:
parent
b3ab4d2cf7
commit
ff6fe6b5aa
19 changed files with 2165 additions and 73 deletions
60
xo-imgui/example/ex4/AnimateGcCopyCb.cpp
Normal file
60
xo-imgui/example/ex4/AnimateGcCopyCb.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* AnimateGcCopyCb.cpp */
|
||||
|
||||
#include "AnimateGcCopyCb.hpp"
|
||||
|
||||
void
|
||||
AnimateGcCopyCb::notify_gc_copy(std::size_t z,
|
||||
const void * src_addr,
|
||||
const void * dest_addr,
|
||||
generation src_gen,
|
||||
generation dest_gen)
|
||||
{
|
||||
using xo::scope;
|
||||
using xo::xtag;
|
||||
using xo::gc::generation_result;
|
||||
using xo::gc::generation;
|
||||
using xo::gc::role;
|
||||
|
||||
scope log(XO_DEBUG(false),
|
||||
xtag("z", z),
|
||||
xtag("src", src_addr),
|
||||
xtag("dest", dest_addr),
|
||||
xtag("src_gen", src_gen),
|
||||
xtag("dest_gen", dest_gen));
|
||||
|
||||
auto [src_gen2, src_offset, src_alloc, src_size] = p_app_state_->gc_->fromspace_location_of(src_addr);
|
||||
|
||||
if (src_gen2 == generation_result::not_found) {
|
||||
auto [lo, hi] = p_app_state_->gc_->nursery_span(role::from_space);
|
||||
|
||||
log && log(xtag("N.from.lo", (void*)lo), xtag("N.from.hi", (void*)hi));
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
generation src_valid_gen = xo::gc::valid_genresult2gen(src_gen2);
|
||||
|
||||
auto [dest_gen2, dest_offset, _, dest_size] = p_app_state_->gc_->tospace_location_of(dest_addr);
|
||||
|
||||
generation dest_valid_gen = xo::gc::valid_genresult2gen(dest_gen2);
|
||||
|
||||
p_app_state_->copy_detail_v_.push_back(GcCopyDetail(z,
|
||||
src_valid_gen, src_offset, src_alloc,
|
||||
dest_valid_gen, dest_offset, dest_size));
|
||||
|
||||
if (dest_valid_gen == generation::nursery) {
|
||||
p_app_state_->copy_detail_max_nursery_dest_offset_
|
||||
= std::max(p_app_state_->copy_detail_max_nursery_dest_offset_, dest_offset);
|
||||
p_app_state_->copy_detail_nursery_dest_size_
|
||||
= std::max(p_app_state_->copy_detail_nursery_dest_size_, dest_size);
|
||||
} else if (dest_valid_gen == generation::tenured) {
|
||||
p_app_state_->copy_detail_max_tenured_dest_offset_
|
||||
= std::max(p_app_state_->copy_detail_max_tenured_dest_offset_, dest_offset);
|
||||
p_app_state_->copy_detail_tenured_dest_size_
|
||||
= std::max(p_app_state_->copy_detail_tenured_dest_size_, dest_size);
|
||||
}
|
||||
|
||||
/* will be animated across frames, see animate_gc_copy() */
|
||||
}
|
||||
|
||||
/* AnimateGcCopyCb.cp */
|
||||
25
xo-imgui/example/ex4/AnimateGcCopyCb.hpp
Normal file
25
xo-imgui/example/ex4/AnimateGcCopyCb.hpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* AnimateGcCopyCb.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/alloc/GC.hpp"
|
||||
#include "AppState.hpp"
|
||||
#include "DrawState.hpp"
|
||||
|
||||
struct DrawState;
|
||||
|
||||
struct AnimateGcCopyCb : public xo::gc::GcCopyCallback {
|
||||
using generation = xo::gc::generation;
|
||||
|
||||
explicit AnimateGcCopyCb(AppState * appstate, DrawState * drawstate)
|
||||
: p_app_state_{appstate}, p_draw_state_{drawstate} {}
|
||||
|
||||
virtual void notify_gc_copy(std::size_t z,
|
||||
const void * src_addr, const void * dest_addr,
|
||||
generation src_gen, generation dest_gen);
|
||||
|
||||
AppState * p_app_state_ = nullptr;
|
||||
DrawState * p_draw_state_ = nullptr;
|
||||
};
|
||||
|
||||
/* end AnimateGcCopyCb.hpp */
|
||||
143
xo-imgui/example/ex4/AppState.cpp
Normal file
143
xo-imgui/example/ex4/AppState.cpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/* AppState.cpp */
|
||||
|
||||
#include "AppState.hpp"
|
||||
#include "xo/object/Integer.hpp"
|
||||
#include "xo/object/List.hpp"
|
||||
|
||||
using xo::obj::Integer;
|
||||
using xo::obj::List;
|
||||
using xo::gp;
|
||||
|
||||
AppState::AppState()
|
||||
{
|
||||
this->gc_ = (GC::make
|
||||
(
|
||||
{.initial_nursery_z_ = 1024*1024,
|
||||
.initial_tenured_z_ = 1024*1024*1024,
|
||||
.incr_gc_threshold_ = 16*1024,
|
||||
.full_gc_threshold_ = 128*1024,
|
||||
.stats_flag_ = true,
|
||||
.debug_flag_ = false}));
|
||||
|
||||
Object::mm = gc_.get();
|
||||
|
||||
for (auto & x: gc_root_v_)
|
||||
gc_->add_gc_root(x.ptr_address());
|
||||
|
||||
gc_->disable_gc();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
AppState::nursery_tospace_scale() const {
|
||||
std::size_t N1_to_size = gc_->nursery_before_checkpoint();
|
||||
std::size_t N_to_committed = gc_->nursery_to_committed();
|
||||
std::size_t N_to_incr_gc_threshold = N1_to_size + gc_->config().incr_gc_threshold_;
|
||||
std::size_t N_to_scale = std::max(N_to_committed, N_to_incr_gc_threshold);
|
||||
|
||||
return N_to_scale;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
AppState::tenured_tospace_scale() const {
|
||||
std::size_t T1_to_size = gc_->tenured_before_checkpoint();
|
||||
std::size_t T_to_committed = gc_->tenured_to_committed();
|
||||
std::size_t T_to_full_gc_threshold = T1_to_size + gc_->config().full_gc_threshold_;
|
||||
std::size_t T_to_scale = std::max(T_to_committed, T_to_full_gc_threshold);
|
||||
|
||||
return T_to_scale;
|
||||
}
|
||||
|
||||
GcStateDescription
|
||||
AppState::snapshot_gc_state() const {
|
||||
/** NOTE: this gets invoked before GC gets opportunity to run.
|
||||
* in the event that GC does run, from- and to- spaces will
|
||||
* have been reversed (near beginning of GC phase)
|
||||
*
|
||||
* This means that nursery_to_reserved() etc. actually refer to from-space
|
||||
* *during gc*
|
||||
**/
|
||||
|
||||
// TOOD: may want to use GC::get_gc_statistics() to replace multiple round trips
|
||||
|
||||
return GcStateDescription(GcGenerationDescription
|
||||
(generation::nursery,
|
||||
"nursery",
|
||||
"N",
|
||||
"incremental",
|
||||
gc_->nursery_polarity(),
|
||||
this->nursery_tospace_scale(),
|
||||
gc_->nursery_before_checkpoint(),
|
||||
gc_->nursery_after_checkpoint(),
|
||||
gc_->nursery_to_reserved(),
|
||||
gc_->nursery_to_committed(),
|
||||
gc_->nursery_before_checkpoint() + gc_->config().incr_gc_threshold_),
|
||||
GcGenerationDescription
|
||||
(generation::tenured,
|
||||
"tenured",
|
||||
"T",
|
||||
"full",
|
||||
gc_->tenured_polarity(),
|
||||
this->tenured_tospace_scale(),
|
||||
gc_->tenured_before_checkpoint(),
|
||||
gc_->tenured_after_checkpoint(),
|
||||
gc_->tenured_to_reserved(),
|
||||
gc_->tenured_to_committed(),
|
||||
gc_->tenured_before_checkpoint() + gc_->config().full_gc_threshold_),
|
||||
|
||||
gc_->size(),
|
||||
gc_->committed(),
|
||||
gc_->allocated(),
|
||||
gc_->available(),
|
||||
gc_->mlog_size(),
|
||||
gc_->native_gc_statistics().total_promoted_,
|
||||
gc_->native_gc_statistics().n_mutation_
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
AppState::generate_random_mutation() {
|
||||
if (rng_() % 1000 > (5 * 1000) / 7) {
|
||||
/* p=16% integer */
|
||||
gc_root_v_[next_root_++] = Integer::make(next_int_);
|
||||
} else if (rng_() % 1000 > (3 * 1000) / 7) {
|
||||
/* p=16% cons */
|
||||
gp<Object> random_car = gc_root_v_.at(rng_() % gc_root_v_.size());
|
||||
if (random_car.is_null())
|
||||
random_car = List::nil;
|
||||
|
||||
/* this will always incorporate existing list as tail of new list */
|
||||
gp<List> random_cdr = List::from(gc_root_v_[next_root_]);
|
||||
if (random_cdr.is_null())
|
||||
random_cdr = List::nil;
|
||||
|
||||
gp<List> random_cons = List::cons(random_car, random_cdr);
|
||||
|
||||
gc_root_v_[next_root_++] = random_cons;
|
||||
} else if (rng_() % 1000 > (0 * 1000) / 7) {
|
||||
/* p=24% mutation */
|
||||
gp<List> random_list = List::from(gc_root_v_.at(rng_() % gc_root_v_.size()));
|
||||
if (!random_list.is_null()) {
|
||||
if (rng_() % 2 == 0) {
|
||||
/* pick up some random object, assign as head */
|
||||
gp<Object> random_car = gc_root_v_.at(rng_() % gc_root_v_.size());
|
||||
random_list->assign_head(random_car);
|
||||
} else {
|
||||
/* pick up some random object; if List, assign tail as tail */
|
||||
gp<List> random_cdr = List::from(gc_root_v_.at(rng_() % gc_root_v_.size()));
|
||||
if (!random_cdr.is_null() && !random_cdr->is_nil())
|
||||
random_list->assign_rest(random_cdr->rest());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_root_ >= gc_root_v_.size())
|
||||
this->next_root_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
AppState::generate_random_mutations() {
|
||||
for (int i = 0; i < this->alloc_per_cycle_; ++i) {
|
||||
this->generate_random_mutation();
|
||||
}
|
||||
}
|
||||
|
||||
/* end AppState.cpp */
|
||||
50
xo-imgui/example/ex4/AppState.hpp
Normal file
50
xo-imgui/example/ex4/AppState.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* AppState.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GcStateDescription.hpp"
|
||||
#include "GcCopyDetail.hpp"
|
||||
#include "xo/alloc/GC.hpp"
|
||||
#include "xo/alloc/Object.hpp"
|
||||
#include "xo/randomgen/xoshiro256.hpp"
|
||||
#include "xo/randomgen/random_seed.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
struct AppState {
|
||||
using GC = xo::gc::GC;
|
||||
using generation = xo::gc::generation;
|
||||
using Object = xo::Object;
|
||||
using xoshiro256ss = xo::rng::xoshiro256ss;
|
||||
template <typename Rng>
|
||||
using Seed = xo::rng::Seed<Rng>;
|
||||
|
||||
public:
|
||||
AppState();
|
||||
|
||||
std::size_t nursery_tospace_scale() const;
|
||||
std::size_t tenured_tospace_scale() const;
|
||||
GcStateDescription snapshot_gc_state() const;
|
||||
|
||||
void generate_random_mutation();
|
||||
void generate_random_mutations();
|
||||
|
||||
public:
|
||||
int alloc_per_cycle_ = 1;
|
||||
/** if gc triggered, remembers which whether incremental or full **/
|
||||
generation upto_ = generation::nursery;
|
||||
xo::up<GC> gc_;
|
||||
std::size_t next_int_ = 0;
|
||||
std::size_t next_root_ = 0;
|
||||
std::vector<xo::gp<Object>> gc_root_v_{100};
|
||||
Seed<xoshiro256ss> seed_;
|
||||
xoshiro256ss rng_{seed_};
|
||||
/** remember details for each object copied by GC, so we can animate **/
|
||||
std::vector<GcCopyDetail> copy_detail_v_;
|
||||
/** max offset for destination, given copied to nursery **/
|
||||
std::size_t copy_detail_max_nursery_dest_offset_ = 0;
|
||||
std::size_t copy_detail_nursery_dest_size_ = 0;
|
||||
std::size_t copy_detail_max_tenured_dest_offset_ = 0;
|
||||
std::size_t copy_detail_tenured_dest_size_ = 0;
|
||||
};
|
||||
|
||||
/* AppState.cpp */
|
||||
|
|
@ -15,15 +15,20 @@ if (XO_ENABLE_EXAMPLES)
|
|||
|
||||
set(SELF_EXE imgui_ex4)
|
||||
add_executable(${SELF_EXE} imgui_ex4.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui_widgets.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui_tables.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_sdl2.cpp
|
||||
AppState.cpp
|
||||
DrawState.cpp
|
||||
GcStateDescription.cpp
|
||||
AnimateGcCopyCb.cpp
|
||||
GenerationLayout.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/imgui_widgets.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/imgui_tables.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_sdl2.cpp
|
||||
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_opengl3.cpp
|
||||
#${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_glfw.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_vulkan.cpp
|
||||
# ${IMGUI_INCLUDE_DIR}/imgui/backends/imgui_impl_vulkan.cpp
|
||||
)
|
||||
xo_include_options2(${SELF_EXE})
|
||||
|
||||
|
|
|
|||
1008
xo-imgui/example/ex4/DrawState.cpp
Normal file
1008
xo-imgui/example/ex4/DrawState.cpp
Normal file
File diff suppressed because it is too large
Load diff
116
xo-imgui/example/ex4/DrawState.hpp
Normal file
116
xo-imgui/example/ex4/DrawState.hpp
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* DrawState.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GcStatistics.hpp"
|
||||
#include "xo/alloc/GC.hpp"
|
||||
#include "GenerationLayout.hpp"
|
||||
#include "GcStateDescription.hpp"
|
||||
#include "GcCopyDetail.hpp"
|
||||
#include "imgui.h"
|
||||
#include <chrono>
|
||||
|
||||
struct AppState;
|
||||
|
||||
enum class draw_state_type {
|
||||
alloc,
|
||||
animate_gc
|
||||
};
|
||||
|
||||
/** for history tooltip, choose which statistic to headline **/
|
||||
enum class gc_history_headline {
|
||||
survive,
|
||||
promote,
|
||||
persist,
|
||||
garbage0,
|
||||
garbage1,
|
||||
garbageN,
|
||||
N
|
||||
};
|
||||
|
||||
struct DrawState {
|
||||
using GcStatisticsHistory = xo::gc::GcStatisticsHistory;
|
||||
using generation = xo::gc::generation;
|
||||
|
||||
using TooltipText = xo::flatstring<512>;
|
||||
using GcStatisticsHistoryItem = xo::gc::GcStatisticsHistoryItem;
|
||||
|
||||
static void draw_generation(const GenerationLayout & layout,
|
||||
ImDrawList * draw_list);
|
||||
static void draw_nursery(const GcStateDescription & gcstate,
|
||||
bool with_labels,
|
||||
const ImRect & rect,
|
||||
ImDrawList * draw_list,
|
||||
GenerationLayout * p_layout);
|
||||
static void draw_tenured(const GcStateDescription & gcstate,
|
||||
bool with_labels,
|
||||
const ImRect & rect,
|
||||
ImDrawList * draw_list,
|
||||
GenerationLayout * p_layout);
|
||||
static TooltipText write_gc_history_tooltip(gc_history_headline headline,
|
||||
const GcStatisticsHistoryItem & stats);
|
||||
static void draw_gc_history(const GcStateDescription & gcstate,
|
||||
generation gen,
|
||||
const GcStatisticsHistory & gc_history,
|
||||
const ImRect & bounding_rect,
|
||||
bool debug_flag,
|
||||
ImDrawList * draw_list);
|
||||
static void draw_gc_efficiency(const GcStateDescription & gcstate,
|
||||
const GcStatisticsHistory & gc_history,
|
||||
const ImRect & bounding_rect,
|
||||
bool debug_flag,
|
||||
ImDrawList * draw_list);
|
||||
static void draw_gc_alloc_state(const GcStateDescription & gcstate,
|
||||
const ImRect & canvas_rect,
|
||||
ImDrawList * draw_list,
|
||||
GenerationLayout * p_nursery_layout,
|
||||
GenerationLayout * p_tenured_layout);
|
||||
static void 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);
|
||||
xo::up<xo::gc::GcCopyCallback> make_gc_copy_animation(AppState * app_state);
|
||||
static ImRect map_src_alloc_to_screen(const GcCopyDetail & copy_detail,
|
||||
const ImRect & space_rect);
|
||||
static ImRect map_dest_alloc_to_screen(const GcCopyDetail & copy_detail,
|
||||
const ImRect & space_rect);
|
||||
|
||||
static void animate_gc_copy(const AppState & app_state,
|
||||
const DrawState & draw_state,
|
||||
ImDrawList * draw_list);
|
||||
|
||||
public:
|
||||
draw_state_type state_type_ = draw_state_type::alloc;
|
||||
|
||||
/** note: during gc copy animation,
|
||||
* this records state _before_ gc was triggered
|
||||
**/
|
||||
GcStateDescription gcstate_;
|
||||
/** budgeted time period over which to animate gc copy **/
|
||||
int animate_copy_budget_ms_ = 2000;
|
||||
/** start time of current copy animation **/
|
||||
std::chrono::steady_clock::time_point animate_copy_t0_;
|
||||
/** when animating copy step, display objects from AppState::copy_detail_v_[i]
|
||||
* where i < .animate_copy_hi_ / 100 * AppState::copy_detail_v_.size()
|
||||
**/
|
||||
float animate_copy_hi_pct_ = 0;
|
||||
|
||||
ImDrawList * gcw_draw_list_ = nullptr;
|
||||
|
||||
/** draw area **/
|
||||
ImVec2 gcw_canvas_p0_;
|
||||
ImVec2 gcw_canvas_p1_;
|
||||
|
||||
/** layout for nursery display **/
|
||||
GenerationLayout gcw_nursery_layout_;
|
||||
/** layout for tenured display **/
|
||||
GenerationLayout gcw_tenured_layout_;
|
||||
|
||||
/** rect displaying gc history (strip charts) **/
|
||||
ImRect gcw_history_rect_;
|
||||
};
|
||||
|
||||
/* end DrawState.hpp */
|
||||
39
xo-imgui/example/ex4/GcCopyDetail.hpp
Normal file
39
xo-imgui/example/ex4/GcCopyDetail.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* GcCopyDetail.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/alloc/generation.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
/** details of a single copy event performed by GC **/
|
||||
struct GcCopyDetail {
|
||||
using generation = xo::gc::generation;
|
||||
|
||||
public:
|
||||
GcCopyDetail(std::size_t z,
|
||||
generation src, std::size_t src_offset, std::size_t src_space_z,
|
||||
generation dest, std::size_t dest_offset, std::size_t dest_z)
|
||||
: z_{z},
|
||||
src_gen_{src}, src_offset_{src_offset}, src_space_z_{src_space_z},
|
||||
dest_gen_{dest}, dest_offset_{dest_offset}, dest_z_{dest_z}
|
||||
{}
|
||||
|
||||
public:
|
||||
/** object size in bytes **/
|
||||
std::size_t z_ = 0;
|
||||
/** source location **/
|
||||
generation src_gen_;
|
||||
/** offset from start of allocator **/
|
||||
std::size_t src_offset_ = 0;
|
||||
/** size of source space. could store this separately **/
|
||||
std::size_t src_space_z_ = 0;
|
||||
|
||||
/** destination location **/
|
||||
generation dest_gen_;
|
||||
/** offset from start of allocator **/
|
||||
std::size_t dest_offset_ = 0;
|
||||
/** size of destination space. (could store this separately). **/
|
||||
std::size_t dest_z_ = 0;
|
||||
};
|
||||
|
||||
/* end GcCopyDetail.hpp */
|
||||
59
xo-imgui/example/ex4/GcGenerationDescription.hpp
Normal file
59
xo-imgui/example/ex4/GcGenerationDescription.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* GcGenerationDescription.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/alloc/GcStatistics.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
/* We need GUI to be able to fall behind true GC state, so we can animate transitions.
|
||||
* To help make this work, provide a model for GC state sufficient to drive rendering.
|
||||
*/
|
||||
struct GcGenerationDescription {
|
||||
using generation = xo::gc::generation;
|
||||
|
||||
GcGenerationDescription() = default;
|
||||
GcGenerationDescription(generation gen,
|
||||
const char * name, const char * mnemonic, const char * gc_type,
|
||||
std::uint8_t polarity,
|
||||
std::size_t tospace_scale,
|
||||
std::size_t before_ckp, std::size_t after_ckp,
|
||||
std::size_t reserved, std::size_t committed, std::size_t gc_threshold)
|
||||
: name_{name}, mnemonic_{mnemonic}, gc_type_{gc_type}, polarity_{polarity},
|
||||
tospace_scale_{tospace_scale},
|
||||
before_checkpoint_{before_ckp}, after_checkpoint_{after_ckp},
|
||||
reserved_{reserved}, committed_{committed},
|
||||
gc_threshold_{gc_threshold} {}
|
||||
|
||||
/** scale (in bytes) for drawing space **/
|
||||
std::size_t scale() const { return std::max(committed_, gc_threshold_); }
|
||||
|
||||
/** nursery or tenured **/
|
||||
generation generation_;
|
||||
|
||||
/** "nursery" or "tenured" **/
|
||||
const char * name_ = nullptr;
|
||||
|
||||
/** "N" or "T" **/
|
||||
const char * mnemonic_ = nullptr;
|
||||
|
||||
/** "incremental" or "full" **/
|
||||
const char * gc_type_ = nullptr;
|
||||
|
||||
/** alternates between {0, 1} on each GC **/
|
||||
std::uint8_t polarity_ = 0;
|
||||
|
||||
/** size of to-space in bytes represented on screen.
|
||||
* (note however when we animate GC, space roles have already reversed,
|
||||
* so then this will refer to old to-space = new from-space)
|
||||
**/
|
||||
std::size_t tospace_scale_ = 0;
|
||||
|
||||
std::size_t before_checkpoint_ = 0;
|
||||
std::size_t after_checkpoint_ = 0;
|
||||
std::size_t reserved_ = 0;
|
||||
std::size_t committed_ = 0;
|
||||
// G_to_gc_threshold = G1_to_size + gc->config().incr_gc_threshold_;
|
||||
std::size_t gc_threshold_ = 0;
|
||||
};
|
||||
|
||||
/* end GcGenerationDescription.hpp */
|
||||
26
xo-imgui/example/ex4/GcStateDescription.cpp
Normal file
26
xo-imgui/example/ex4/GcStateDescription.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* GcStateDescription.hpp */
|
||||
|
||||
#include "GcStateDescription.hpp"
|
||||
|
||||
GcStateDescription::GcStateDescription(const GcGenerationDescription & nursery,
|
||||
const GcGenerationDescription & tenured,
|
||||
std::size_t gc_size,
|
||||
std::size_t gc_committed,
|
||||
std::size_t gc_allocated,
|
||||
std::size_t gc_available,
|
||||
std::size_t gc_mlog_size,
|
||||
std::size_t total_promoted,
|
||||
std::size_t total_n_mutation)
|
||||
: gc_size_{gc_size},
|
||||
gc_committed_{gc_committed},
|
||||
gc_allocated_{gc_allocated},
|
||||
gc_available_{gc_available},
|
||||
gc_mlog_size_{gc_mlog_size},
|
||||
total_promoted_{total_promoted},
|
||||
total_n_mutation_{total_n_mutation}
|
||||
{
|
||||
gen_state_v_[gen2int(generation::nursery)] = nursery;
|
||||
gen_state_v_[gen2int(generation::tenured)] = tenured;
|
||||
}
|
||||
|
||||
/* end GcStateDescription.cpp */
|
||||
46
xo-imgui/example/ex4/GcStateDescription.hpp
Normal file
46
xo-imgui/example/ex4/GcStateDescription.hpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* GcStateDescription.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GcGenerationDescription.hpp"
|
||||
|
||||
/* We need GUI to be able to fall behind true GC state, so we can animate transitions.
|
||||
* To help make this work, provide a model for GC state sufficient to drive rendering.
|
||||
*/
|
||||
struct GcStateDescription {
|
||||
using generation = xo::gc::generation;
|
||||
|
||||
GcStateDescription() = default;
|
||||
GcStateDescription(const GcGenerationDescription & nursery,
|
||||
const GcGenerationDescription & tenured,
|
||||
std::size_t gc_size,
|
||||
std::size_t gc_committed,
|
||||
std::size_t gc_allocated,
|
||||
std::size_t gc_available,
|
||||
std::size_t gc_mlog_size,
|
||||
std::size_t total_promoted,
|
||||
std::size_t total_n_mutation
|
||||
);
|
||||
|
||||
const GcGenerationDescription & get_gendescr(generation g) const { return gen_state_v_[gen2int(g)]; }
|
||||
|
||||
std::array<GcGenerationDescription, static_cast<std::size_t>(generation::N)> gen_state_v_;
|
||||
|
||||
/** see @ref GC::size **/
|
||||
std::size_t gc_size_ = 0;
|
||||
/** see @ref GC::committed **/
|
||||
std::size_t gc_committed_ = 0;
|
||||
/** see @ref GC::allocated **/
|
||||
std::size_t gc_allocated_ = 0;
|
||||
/** see @ref GC::available **/
|
||||
std::size_t gc_available_ = 0;
|
||||
/** see @ref GC::mlog_size **/
|
||||
std::size_t gc_mlog_size_ = 0;
|
||||
|
||||
/** see @ref GcStatistics::total_promoted_ **/
|
||||
std::size_t total_promoted_ = 0;
|
||||
/** see @ref GcStatistics::n_mutation_ **/
|
||||
std::size_t total_n_mutation_ = 0;
|
||||
};
|
||||
|
||||
/* end GcStateDescription.hpp */
|
||||
42
xo-imgui/example/ex4/GenerationLayout.cpp
Normal file
42
xo-imgui/example/ex4/GenerationLayout.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* GenerationLayout.cpp */
|
||||
|
||||
#include "GenerationLayout.hpp"
|
||||
|
||||
GenerationLayout::GenerationLayout(const GcGenerationDescription & gendescr,
|
||||
const ImRect & br,
|
||||
bool with_labels)
|
||||
: gendescr_{gendescr}, bounding_rect_{br}, with_labels_{with_labels}
|
||||
{
|
||||
this->text_dy_ = ImGui::CalcTextSize("SAMPLE TEXT").y;
|
||||
|
||||
if (with_labels_) {
|
||||
snprintf(this->rh_text_.data(), rh_text_.capacity(),
|
||||
"%s: %luk",
|
||||
gendescr_.mnemonic_,
|
||||
std::max(gendescr_.gc_threshold_, gendescr_.committed_) / 1024);
|
||||
rh_text_.ensure_final_null();
|
||||
|
||||
auto textz = ImGui::CalcTextSize(rh_text_.c_str());
|
||||
|
||||
/* allow margin between rh edge of mem range and beginning of label */
|
||||
this->rh_text_dx_ = 5 + textz.x;
|
||||
} else {
|
||||
this->rh_text_dx_ = 0.0;
|
||||
}
|
||||
|
||||
if (with_labels_) {
|
||||
this->chart_withlabel_rect_ = bounding_rect_.within_top_margin(text_dy_ + 2);
|
||||
this->chart_nolabel_rect_ = chart_withlabel_rect_.within_right_margin(rh_text_dx_);
|
||||
} else {
|
||||
this->chart_withlabel_rect_ = bounding_rect_;
|
||||
this->chart_nolabel_rect_ = bounding_rect_;
|
||||
}
|
||||
|
||||
this->mem_rect_from_ = chart_nolabel_rect_.top_fraction(0.45);
|
||||
this->mem_rect_to_ = chart_nolabel_rect_.bottom_fraction(0.45);
|
||||
|
||||
if (gendescr_.polarity_ == 1)
|
||||
std::swap(this->mem_rect_from_, this->mem_rect_to_);
|
||||
}
|
||||
|
||||
/* GenerationLayout.cpp */
|
||||
93
xo-imgui/example/ex4/GenerationLayout.hpp
Normal file
93
xo-imgui/example/ex4/GenerationLayout.hpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/* GenerationLayout.hpp */
|
||||
|
||||
#include "GcGenerationDescription.hpp"
|
||||
#include "xo/imgui/ImRect.hpp"
|
||||
|
||||
/** @class GenerationLayout
|
||||
* @brief layout for displaying a single collector generation.
|
||||
*
|
||||
* @text
|
||||
* reserved: xxx committed: xxxx G1: xxx bytes G0: xxx bytes
|
||||
* +--------------------------+--------------------+-------------------------+
|
||||
* to | G1 | G0 | |
|
||||
* +--------------------------+--------------------+-------------------------+
|
||||
* Mem: 28k
|
||||
* +-------------------------------------------------------------------------+
|
||||
* from | |
|
||||
* +-------------------------------------------------------------------------+
|
||||
* ^
|
||||
* layout elements:
|
||||
*
|
||||
* <-a-><------------------------------ mem_w ------------------------------------><---b---->
|
||||
* <------------------------- ngc_w ---------------------------->
|
||||
* <--------- G1_w ----------> <------- G0_w ----->
|
||||
*
|
||||
* a (lh_text_dx): width for left-hand-side text
|
||||
* b (rh_text_dx): width for right-hand-side text
|
||||
* mem_w: width for contiguous committed memory
|
||||
* ngc_w: location (relative to start of GC memory range) of next-collection trigger
|
||||
* G1_w: width for occupied memory that has survived one GC in this space
|
||||
* G0_w: width for memory allocated since last GC
|
||||
*
|
||||
* @endtext
|
||||
*
|
||||
**/
|
||||
struct GenerationLayout {
|
||||
GenerationLayout() = default;
|
||||
GenerationLayout(const GcGenerationDescription & gendescr, const ImRect & br, bool with_labels);
|
||||
|
||||
const char * name() const { return gendescr_.name_; }
|
||||
const char * mnemonic() const { return gendescr_.mnemonic_; }
|
||||
const char * gc_type() const { return gendescr_.gc_type_; }
|
||||
std::size_t to_G1_size() const { return gendescr_.before_checkpoint_; }
|
||||
std::size_t to_G0_size() const { return gendescr_.after_checkpoint_; }
|
||||
std::size_t to_gc_threhsold() const { return gendescr_.gc_threshold_; }
|
||||
float to_scale() const {
|
||||
/** note: deliberate size_t->float conversion here **/
|
||||
return gendescr_.scale();
|
||||
}
|
||||
ImRect to_g1_rect() const {
|
||||
return mem_rect_to_.left_fraction(this->to_G1_size() / this->to_scale());
|
||||
}
|
||||
ImRect to_g0_rect() const {
|
||||
return mem_rect_to_.mid_x_fraction(this->to_G1_size() / this->to_scale(),
|
||||
(this->to_G1_size() + this->to_G0_size()) / this->to_scale());
|
||||
}
|
||||
ImRect to_alloc_rect() const {
|
||||
return mem_rect_to_.left_fraction((this->to_G1_size() + this->to_G0_size()) / this->to_scale());
|
||||
}
|
||||
|
||||
ImRect from_alloc_rect() const {
|
||||
/* use the same sizing as for source generation */
|
||||
return mem_rect_from_.left_fraction((this->to_G1_size() + this->to_G0_size()) / this->to_scale());
|
||||
}
|
||||
|
||||
/** size-related statistics for generation to be displayed **/
|
||||
GcGenerationDescription gendescr_;
|
||||
|
||||
/** bounding rectangle. all drawing for generation display will be inside this rectanglge **/
|
||||
ImRect bounding_rect_;
|
||||
|
||||
/** true iff text labels enabled **/
|
||||
bool with_labels_ = false;
|
||||
|
||||
/** text height in screen units **/
|
||||
float text_dy_ = 0.0;
|
||||
|
||||
/** chart rectangle. bounding rectangle less room for headline text **/
|
||||
ImRect chart_withlabel_rect_;
|
||||
ImRect chart_nolabel_rect_;
|
||||
|
||||
/** text for RH label. something like "N: 28k/40k" **/
|
||||
xo::flatstring<80> rh_text_;
|
||||
|
||||
/** width of .rh_text in screen units **/
|
||||
float rh_text_dx_ = 0.0;
|
||||
|
||||
/** rectangle representing from-space memory range **/
|
||||
ImRect mem_rect_from_;
|
||||
/** rectangle representing to-space memory range **/
|
||||
ImRect mem_rect_to_;
|
||||
};
|
||||
|
||||
/* GenerationLayout.hpp */
|
||||
|
|
@ -1,53 +1,205 @@
|
|||
/* imgui_ex4.cpp */
|
||||
|
||||
#include "xo/imgui/VulkanApp.hpp"
|
||||
#include "xo/imgui/ImRect.hpp"
|
||||
#include "AppState.hpp"
|
||||
#include "DrawState.hpp"
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_vulkan.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace {
|
||||
ImDrawData *
|
||||
imgui_draw_frame(ImGuiContext * imgui_cx)
|
||||
using xo::gc::generation;
|
||||
using xo::scope;
|
||||
using xo::xtag;
|
||||
|
||||
void
|
||||
app_duty_cycle_top(AppState * p_app_state,
|
||||
DrawState * p_draw_state)
|
||||
{
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
// Create a simple ImGui window
|
||||
ImGui::Begin("Hello, Vulkan + SDL2!");
|
||||
ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!");
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||
if (ImGui::Button("Button"))
|
||||
++counter;
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", counter);
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
|
||||
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
ImGui::End();
|
||||
log && log(xtag("imgui_cx", (void*)ImGui::GetCurrentContext()));
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
/** on each draw cycle, app state falls into categories:
|
||||
* 1. allocation
|
||||
* multiple draw cycles because many allocations per gc.
|
||||
* 2. garbage collection
|
||||
* multiple draw cycles to animate copying process
|
||||
* Settle conflict between {GC, imgui} as to who drives event loop,
|
||||
* in favor of imgui; achieve this by copying what GC did,
|
||||
* so that we can animate it over multiple draw cycles
|
||||
**/
|
||||
|
||||
return draw_data;
|
||||
switch (p_draw_state->state_type_) {
|
||||
case draw_state_type::alloc:
|
||||
{
|
||||
/** generate random alloc **/
|
||||
p_app_state->generate_random_mutations();
|
||||
|
||||
p_draw_state->gcstate_ = p_app_state->snapshot_gc_state();
|
||||
|
||||
p_app_state->upto_ = (p_app_state->gc_->is_full_gc_pending()
|
||||
? generation::tenured
|
||||
: generation::nursery);
|
||||
|
||||
/* GC may run here, in which case control reenters via AnimateGcCopyCb;
|
||||
* that callback captures copy details (per object!) in AppState
|
||||
*/
|
||||
if (p_app_state->gc_->enable_gc_once()) {
|
||||
log && log(xtag("gc-type", (p_app_state->upto_ == generation::tenured) ? "full" : "incremental"));
|
||||
|
||||
p_draw_state->state_type_ = draw_state_type::animate_gc;
|
||||
p_draw_state->animate_copy_t0_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case draw_state_type::animate_gc:
|
||||
{
|
||||
/* don't update gcstate while animating,
|
||||
* that would use post-GC space sizing
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} /*app_duty_cycle_top*/
|
||||
|
||||
VulkanApp::ImguiDrawFn
|
||||
make_imgui_draw_frame(AppState * p_app_state,
|
||||
DrawState * p_draw_state,
|
||||
float * p_f, int * p_counter)
|
||||
{
|
||||
*p_f = 0.0f;
|
||||
*p_counter = 0;
|
||||
|
||||
return [p_app_state, p_draw_state, p_f, p_counter](ImGuiContext * imgui_cx)
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
app_duty_cycle_top(p_app_state, p_draw_state);
|
||||
|
||||
log && log(xtag("imgui_cx", (void*)ImGui::GetCurrentContext()));
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
log && log("after NewFrame", xtag("imgui_cx", (void*)ImGui::GetCurrentContext()));
|
||||
|
||||
#ifdef NOT_WORKING
|
||||
ImGuiIO & io = ImGui::GetIO(); (void)io;
|
||||
|
||||
// background
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
||||
ImGui::SetNextWindowSize(io.DisplaySize);
|
||||
ImGui::Begin("Background", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize
|
||||
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus
|
||||
| ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::End();
|
||||
#endif
|
||||
|
||||
// Create a simple ImGui window
|
||||
ImGui::Begin("Hello, Vulkan + SDL2!");
|
||||
ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!");
|
||||
ImGui::SliderFloat("float", p_f, 0.0f, 1.0f);
|
||||
if (ImGui::Button("Button"))
|
||||
++(*p_counter);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", *p_counter);
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
|
||||
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
ImGui::End();
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
return ImGui::GetDrawData();
|
||||
};
|
||||
}
|
||||
|
||||
void app_imgui_load_fonts(ImGuiContext * imgui_cx)
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
log && log(xtag("imgui_cx", (void*)ImGui::GetCurrentContext()));
|
||||
|
||||
ImGuiIO & io = ImGui::GetIO(); (void)io;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
// Load noto sans font from unix environment NOTO_ONFTS_PATH
|
||||
// (see xo-umbrella2/default.nix shellHook)
|
||||
|
||||
const char * fonts_path = std::getenv("DEJAVU_FONTS_PATH");
|
||||
|
||||
if (fonts_path) {
|
||||
const float font_size = 14.0f;
|
||||
std::string font_path = xo::tostr(fonts_path, "/truetype/DejaVuSans.ttf");
|
||||
|
||||
/* check file exists */
|
||||
std::ifstream font_in(font_path);
|
||||
if (font_in.good()) {
|
||||
std::cerr << "loading font [" << font_path << "]" << std::endl;
|
||||
ImFont * font = io.Fonts->AddFontFromFileTTF(font_path.c_str(), font_size);
|
||||
if (font) {
|
||||
std::cerr << "font loaded" << std::endl;
|
||||
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
|
||||
// latin extended chars
|
||||
static const ImWchar latin_ranges[] = {
|
||||
0x0020, 0x00ff, // basic latin + latin supplement
|
||||
0x0100, 0x017f, // latin extended-A
|
||||
0x0180, 0x024f, // latin extended-B
|
||||
0x2080, 0x2099, // subscript numerals + letters through n
|
||||
0x25b2, 0x25b4, // arrows
|
||||
0,
|
||||
};
|
||||
io.Fonts->AddFontFromFileTTF(font_path.c_str(), font_size, &config, latin_ranges);
|
||||
} else {
|
||||
std::cerr << "font file load failed" << std::endl;
|
||||
std::cerr << "Fallback to default ImGui font" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
std::cerr << "Expected DEJAVU_FONTS_PATH environment var." << std::endl;
|
||||
std::cerr << "Fallback to default ImGui font" << std::endl;
|
||||
}
|
||||
} /*app_imgui_load_fonts*/
|
||||
}
|
||||
|
||||
int main() {
|
||||
VulkanApp app;
|
||||
AppState app_state;
|
||||
DrawState draw_state;
|
||||
draw_state.gcstate_ = app_state.snapshot_gc_state();
|
||||
|
||||
app.assign_imgui_draw_frame(imgui_draw_frame);
|
||||
float f = 0.0;
|
||||
int counter = 0;
|
||||
VulkanApp::ImguiDrawFn draw_fn
|
||||
= make_imgui_draw_frame(&app_state, &draw_state, &f, &counter);
|
||||
VulkanApp vk_app(draw_fn);
|
||||
|
||||
/* establishes imgui context */
|
||||
vk_app.setup(app_imgui_load_fonts);
|
||||
|
||||
#ifdef NOT_YET
|
||||
app_state.gc_->add_gc_copy_callback
|
||||
(draw_state.make_gc_copy_animation(&app_state));
|
||||
#endif
|
||||
|
||||
try {
|
||||
app.run();
|
||||
vk_app.main_loop();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
vk_app.cleanup();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
139
xo-imgui/include/xo/imgui/ImRect.hpp
Normal file
139
xo-imgui/include/xo/imgui/ImRect.hpp
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/* ImRect.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "imgui.h"
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
inline ImVec2 operator+(const ImVec2 & p1, const ImVec2 & p2) {
|
||||
return ImVec2(p1.x + p2.x, p1.y + p2.y);
|
||||
}
|
||||
|
||||
struct ImRect {
|
||||
ImRect() = default;
|
||||
ImRect(const ImVec2 & tl, const ImVec2 & br) : top_left_{tl}, bottom_right_{br} {}
|
||||
ImRect(float x_lo, float y_lo, float x_hi, float y_hi) : top_left_{x_lo, y_lo}, bottom_right_{x_hi, y_hi} {}
|
||||
|
||||
static ImRect from_xy_span(const ImVec2 & x_span, const ImVec2 & y_span) {
|
||||
return ImRect(ImVec2{x_span.x, y_span.x}, ImVec2{x_span.y, y_span.y});
|
||||
}
|
||||
|
||||
static void draw_filled_rect_with_label(const char * text,
|
||||
const char * tooltip,
|
||||
const ImRect & rect,
|
||||
ImU32 fillcolor,
|
||||
ImU32 textcolor,
|
||||
ImDrawList * draw_list);
|
||||
|
||||
static void draw_filled_rect(const char * tooltip,
|
||||
const ImRect & rect,
|
||||
ImU32 fillcolor,
|
||||
ImDrawList * draw_list);
|
||||
|
||||
std::pair<float, float> x_span() const { return std::make_pair(top_left_.x, bottom_right_.x); }
|
||||
std::pair<float, float> y_span() const { return std::make_pair(top_left_.y, bottom_right_.y); }
|
||||
|
||||
const ImVec2 & top_left() const { return top_left_; }
|
||||
const ImVec2 & bottom_right() const { return bottom_right_; }
|
||||
|
||||
float x_lo() const { return top_left_.x; }
|
||||
float x_hi() const { return bottom_right_.x; }
|
||||
float y_lo() const { return top_left_.y; }
|
||||
float y_hi() const { return bottom_right_.y; }
|
||||
|
||||
float width() const { return bottom_right_.x - top_left_.x; }
|
||||
float height() const { return bottom_right_.y - top_left_.y; }
|
||||
|
||||
float x_mid() const { return 0.5 * (top_left_.x + bottom_right_.x); }
|
||||
float y_mid() const { return 0.5 * (top_left_.y + bottom_right_.y); }
|
||||
|
||||
ImVec2 bottom_left() const { return ImVec2(x_lo(), y_hi()); }
|
||||
ImVec2 top_right() const { return ImVec2(x_hi(), y_lo()); }
|
||||
|
||||
ImRect with_x_span(float x0, float x1) const {
|
||||
return ImRect(ImVec2(x0, top_left_.y), ImVec2(x1, bottom_right_.y));
|
||||
}
|
||||
ImRect with_y_span(float y0, float y1) const {
|
||||
return ImRect(ImVec2(top_left_.x, y0), ImVec2(bottom_right_.x, y1));
|
||||
}
|
||||
|
||||
ImRect within_margin(const ImRect & margin) const {
|
||||
return ImRect(this->x_lo() + margin.x_lo(),
|
||||
this->y_lo() + margin.y_lo(),
|
||||
this->x_hi() - margin.x_hi(),
|
||||
this->y_hi() - margin.y_hi());
|
||||
}
|
||||
|
||||
ImRect within_right_margin(float dx) const {
|
||||
return ImRect(this->x_lo(), this->y_lo(), this->x_hi() - dx, this->y_hi());
|
||||
}
|
||||
|
||||
ImRect within_top_margin(float dy) const {
|
||||
return ImRect(this->x_lo(), this->y_lo() + dy, this->x_hi(), this->y_hi());
|
||||
}
|
||||
|
||||
ImRect within_bottom_margin(float dy) const {
|
||||
return ImRect(this->x_lo(), this->y_lo(), this->x_hi(), this->y_hi() - dy);
|
||||
}
|
||||
|
||||
ImRect translate(const ImVec2 & dist) {
|
||||
return ImRect(top_left_ + dist,
|
||||
bottom_right_ + dist);
|
||||
}
|
||||
|
||||
/** Require: 0.0 <= p <= 1.0 **/
|
||||
ImRect left_fraction(float p, float min_width = 0.0, float max_width = 999999.0) const {
|
||||
max_width = std::min(max_width, this->width());
|
||||
|
||||
float w = std::clamp(p * this->width(), min_width, max_width);
|
||||
|
||||
return ImRect(top_left_,
|
||||
ImVec2(this->x_lo() + w, this->y_hi()));
|
||||
}
|
||||
|
||||
/** Require: 0.0 <= p <= 1.0 **/
|
||||
ImRect right_fraction(float p, float min_width = 0.0, float max_width = 999999.0) const {
|
||||
max_width = std::min(max_width, this->width());
|
||||
|
||||
float w = std::clamp(p * this->width(), min_width, max_width);
|
||||
|
||||
return ImRect(ImVec2(this->x_hi() - w, this->y_lo()),
|
||||
bottom_right_);
|
||||
}
|
||||
|
||||
/** Require: 0.0 <= p <= q <= 1.0 **/
|
||||
ImRect mid_x_fraction(float p, float q) const {
|
||||
assert(p <= q);
|
||||
|
||||
float w = this->width();
|
||||
|
||||
return this->with_x_span(this->x_lo() + p * w,
|
||||
this->x_lo() + q * w);
|
||||
}
|
||||
|
||||
/** Require: 0.0 <= p <= 1.0 **/
|
||||
ImRect top_fraction(float p, float min_height = 0.0, float max_height = 999999.0) const {
|
||||
max_height = std::min(max_height, this->height());
|
||||
|
||||
float h = std::clamp(p * this->height(), min_height, max_height);
|
||||
|
||||
return ImRect(top_left_,
|
||||
ImVec2(this->x_hi(), this->y_lo() + h));
|
||||
}
|
||||
|
||||
/** Require: 0.0 <= p <= 1.0 **/
|
||||
ImRect bottom_fraction(float p, float min_height = 0.0, float max_height = 999999.0) const {
|
||||
max_height = std::min(max_height, this->height());
|
||||
|
||||
float h = std::clamp(p * this->height(), min_height, max_height);
|
||||
|
||||
return ImRect(ImVec2(this->x_lo(), this->y_hi() - h),
|
||||
bottom_right_);
|
||||
}
|
||||
|
||||
ImVec2 top_left_{0, 0};
|
||||
ImVec2 bottom_right_{0, 0};
|
||||
};
|
||||
|
||||
/* end ImRect.hpp */
|
||||
|
|
@ -14,13 +14,22 @@ public:
|
|||
using ImguiDrawFn = std::function<ImDrawData * (ImGuiContext *)>;
|
||||
|
||||
public:
|
||||
VulkanApp() = default;
|
||||
VulkanApp(ImguiDrawFn fn);
|
||||
|
||||
#ifdef NOPE
|
||||
/** set imgui draw function **/
|
||||
void assign_imgui_draw_frame(ImguiDrawFn fn);
|
||||
#endif
|
||||
|
||||
/** equivalent to sequence setup(), main_loop(), cleanup() **/
|
||||
void run();
|
||||
|
||||
/** setup before main loop. idempotent **/
|
||||
void setup(std::function<void (ImGuiContext *)> load_fonts);
|
||||
void main_loop();
|
||||
/** cleanup before shutdown. idempotent **/
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
void init_window();
|
||||
void init_vulkan();
|
||||
|
|
@ -36,18 +45,18 @@ private:
|
|||
void create_command_buffers();
|
||||
void create_sync_objects();
|
||||
void create_descriptor_pool();
|
||||
void init_imgui();
|
||||
void init_imgui(std::function<void (ImGuiContext *)> load_fonts);
|
||||
VkCommandBuffer begin_single_time_commands();
|
||||
void end_single_time_commands(VkCommandBuffer commandBuffer);
|
||||
void record_command_buffer(VkCommandBuffer commandBuffer,
|
||||
uint32_t imageIndex);
|
||||
void cleanup();
|
||||
|
||||
/** TODO: replace with some generic mechanism **/
|
||||
void main_loop();
|
||||
void draw_frame();
|
||||
|
||||
private:
|
||||
bool setup_done_ = false;
|
||||
bool cleanup_done_ = false;
|
||||
|
||||
SDL_Window* window = nullptr;
|
||||
ImGuiContext* imgui_cx_ = nullptr;
|
||||
VkInstance instance;
|
||||
|
|
@ -77,7 +86,7 @@ private:
|
|||
bool quit_ = false;
|
||||
|
||||
/** draw imgui **/
|
||||
ImguiDrawFn imgui_draw_frame_;
|
||||
const ImguiDrawFn imgui_draw_frame_;
|
||||
};
|
||||
|
||||
/* end VulkanApp.hpp */
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ endif()
|
|||
set(SELF_LIB xo_imgui)
|
||||
set(SELF_SRCS
|
||||
VulkanApp.cpp
|
||||
ImRect.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp
|
||||
${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp
|
||||
|
|
@ -38,4 +39,4 @@ xo_external_pkgconfig_dependency(${SELF_LIB} SDL2 sdl2)
|
|||
#
|
||||
target_include_directories(${SELF_LIB} PUBLIC ${IMGUI_INCLUDE_DIR}/imgui)
|
||||
|
||||
#xo_dependency(${SELF_LIB} reflect)
|
||||
xo_dependency(${SELF_LIB} indentlog)
|
||||
|
|
|
|||
54
xo-imgui/src/imgui/ImRect.cpp
Normal file
54
xo-imgui/src/imgui/ImRect.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* ImRect.cpp */
|
||||
|
||||
#include "ImRect.hpp"
|
||||
|
||||
void
|
||||
ImRect::draw_filled_rect_with_label(const char * text,
|
||||
const char * tooltip,
|
||||
const ImRect & rect,
|
||||
ImU32 fillcolor,
|
||||
ImU32 textcolor,
|
||||
ImDrawList * draw_list)
|
||||
{
|
||||
draw_list->AddRectFilled(rect.top_left(),
|
||||
rect.bottom_right(),
|
||||
fillcolor);
|
||||
|
||||
if ((rect.width() > 0.0) && (rect.height() > 0.0)) {
|
||||
ImGui::SetCursorScreenPos(rect.top_left());
|
||||
ImGui::InvisibleButton("ttbutton", ImVec2(rect.width(), rect.height()));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("%s", tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
if (text) {
|
||||
auto textz = ImGui::CalcTextSize(text);
|
||||
|
||||
/* N1 can be empty: but in that case don't bother to label it */
|
||||
if (textz.x < rect.width()) {
|
||||
draw_list->AddText(ImVec2(rect.x_mid() - 0.5 * textz.x,
|
||||
rect.y_mid() - 0.5 * textz.y),
|
||||
textcolor,
|
||||
text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ImRect::draw_filled_rect(const char * tooltip,
|
||||
const ImRect & rect,
|
||||
ImU32 fillcolor,
|
||||
ImDrawList * draw_list)
|
||||
{
|
||||
draw_filled_rect_with_label(nullptr,
|
||||
tooltip,
|
||||
rect,
|
||||
fillcolor,
|
||||
IM_COL32(255, 255, 255, 255),
|
||||
draw_list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ImRect.cpp */
|
||||
|
|
@ -1,24 +1,37 @@
|
|||
/* file VulkanApp.cpp */
|
||||
|
||||
#include "VulkanApp.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include <SDL_vulkan.h>
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_vulkan.h>
|
||||
#include <cstdint>
|
||||
|
||||
using xo::scope;
|
||||
using xo::xtag;
|
||||
|
||||
constexpr std::size_t c_max_frames_in_flight = 2;
|
||||
|
||||
void
|
||||
VulkanApp::assign_imgui_draw_frame(ImguiDrawFn fn)
|
||||
VulkanApp::VulkanApp(ImguiDrawFn draw_fn)
|
||||
: imgui_draw_frame_{
|
||||
draw_fn
|
||||
}
|
||||
{
|
||||
this->imgui_draw_frame_ = std::move(fn);
|
||||
}
|
||||
|
||||
void
|
||||
VulkanApp::setup(std::function<void (ImGuiContext *)> load_fonts) {
|
||||
if (!setup_done_) {
|
||||
this->init_window();
|
||||
this->init_vulkan();
|
||||
this->init_imgui(load_fonts);
|
||||
this->setup_done_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VulkanApp::run() {
|
||||
this->init_window();
|
||||
this->init_vulkan();
|
||||
this->init_imgui();
|
||||
this->setup(nullptr);
|
||||
this->main_loop();
|
||||
this->cleanup();
|
||||
}
|
||||
|
|
@ -420,14 +433,21 @@ VulkanApp::create_descriptor_pool() {
|
|||
}
|
||||
|
||||
void
|
||||
VulkanApp::init_imgui()
|
||||
VulkanApp::init_imgui(std::function<void (ImGuiContext *)> load_fonts)
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
this->imgui_cx_ = ImGui::CreateContext();
|
||||
|
||||
log && log(xtag("imgui_cx", (void*)imgui_cx_));
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
|
||||
if (load_fonts)
|
||||
load_fonts(imgui_cx_);
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
|
|
@ -456,7 +476,7 @@ VulkanApp::init_imgui()
|
|||
ImGui_ImplVulkan_CreateFontsTexture();
|
||||
this->end_single_time_commands(command_buffer);
|
||||
|
||||
//ImGui_ImplVulkan_DestroyFontUploadObjects();
|
||||
ImGui_ImplVulkan_DestroyFontsTexture();
|
||||
} /*init_imgui*/
|
||||
|
||||
VkCommandBuffer
|
||||
|
|
@ -508,6 +528,25 @@ VulkanApp::main_loop()
|
|||
if (event.type == SDL_QUIT) {
|
||||
this->quit_ = true;
|
||||
}
|
||||
|
||||
#ifdef NOT_YET
|
||||
if (event.type == SDL_WINDOWEVENT) {
|
||||
if (event.window.event == SDL_WINDOWEVENT_CLOSE) {
|
||||
if (event.window.windowID == SDL_GetWindowID(window))
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
} else if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
// handle resize immediately
|
||||
int w, h;
|
||||
SDL_GetWindowSize(window, &w, &h);
|
||||
glViewport(0, 0, w, h);
|
||||
|
||||
break; // to force render during resize
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
this->draw_frame();
|
||||
|
|
@ -599,28 +638,8 @@ VulkanApp::record_command_buffer(VkCommandBuffer commandBuffer,
|
|||
&render_pass_info,
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
ImDrawData * draw_data = this->imgui_draw_frame_(imgui_cx_);
|
||||
|
||||
// Create a simple ImGui window
|
||||
ImGui::Begin("Hello, Vulkan + SDL2!");
|
||||
ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!");
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
|
||||
if (ImGui::Button("Button"))
|
||||
++counter;
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("counter = %d", counter);
|
||||
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
|
||||
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
ImGui::End();
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffer);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffer);
|
||||
|
|
@ -633,6 +652,11 @@ VulkanApp::record_command_buffer(VkCommandBuffer commandBuffer,
|
|||
void
|
||||
VulkanApp::cleanup()
|
||||
{
|
||||
if (cleanup_done_)
|
||||
return;
|
||||
|
||||
this->cleanup_done_ = true;
|
||||
|
||||
ImGui_ImplVulkan_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
|
@ -663,6 +687,7 @@ VulkanApp::cleanup()
|
|||
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
|
||||
}
|
||||
|
||||
/* end VulkanApp.cpp */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue