Commit 541ea398 authored by Gauthier Quesnel's avatar Gauthier Quesnel
Browse files

gui: use ImGui loop to run simulation

parent ade76b33
Pipeline #34497 passed with stage
in 1 minute and 32 seconds
......@@ -23,14 +23,20 @@ application::init()
ed->settings.compute_colors();
}
try {
simulation_duration.resize(editors.capacity(), 0);
} catch (const std::bad_alloc& /*e*/) {
return false;
}
try {
if (auto home = get_home_directory(); home) {
home_dir = home.value();
home_dir /= "irritator";
} else {
log_w.log(
3,
"Fail to retrieve home directory. Use current directory instead\n");
log_w.log(3,
"Fail to retrieve home directory. Use current directory "
"instead\n");
home_dir = std::filesystem::current_path();
}
......@@ -210,56 +216,102 @@ application::shutdown()
imnodes::EditorContextFree(ed->context);
}
static status
run_for(editor& ed,
long long int duration_in_microseconds,
const double end,
double& current) noexcept
static void
run_for(editor& ed, long long int duration_in_microseconds) noexcept
{
namespace stdc = std::chrono;
auto start_at = stdc::high_resolution_clock::now();
long long int duration_since_start;
do {
if (auto ret = ed.sim.run(current); is_bad(ret))
return ret;
if (ed.simulation_bag_id < 0) {
ed.sim.clean();
ed.simulation_current = ed.simulation_begin;
ed.simulation_during_date = ed.simulation_begin;
ed.models_make_transition.resize(ed.sim.models.capacity(), false);
if (ed.sim_st = ed.sim.initialize(ed.simulation_current);
irt::is_bad(ed.sim_st)) {
log_w.log(3,
"Simulation initialisation failure (%s)\n",
irt::status_string(ed.sim_st));
return;
}
ed.simulation_next_time = ed.sim.sched.empty()
? time_domain<time>::infinity
: ed.sim.sched.tn();
ed.simulation_bag_id = 0;
}
if (ed.st == editor_status::running) {
do {
++ed.simulation_bag_id;
if (ed.sim_st = ed.sim.run(ed.simulation_current);
is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
ed.sim.finalize(ed.simulation_end);
return;
}
auto end_at = stdc::high_resolution_clock::now();
auto duration = end_at - start_at;
auto duration_cast = stdc::duration_cast<stdc::microseconds>(duration);
duration_since_start = duration_cast.count();
} while (duration_since_start < duration_in_microseconds);
if (ed.simulation_current >= ed.simulation_end) {
ed.st = editor_status::editing;
ed.sim.finalize(ed.simulation_end);
return;
}
auto end_at = stdc::high_resolution_clock::now();
auto duration = end_at - start_at;
auto duration_cast =
stdc::duration_cast<stdc::microseconds>(duration);
duration_since_start = duration_cast.count();
} while (duration_since_start < duration_in_microseconds);
return;
}
const int loop = ed.st == editor_status::running_1_step ? 1
: ed.st == editor_status::running_10_step ? 10
: 100;
for (int i = 0; i < loop; ++i) {
++ed.simulation_bag_id;
if (ed.sim_st = ed.sim.run(ed.simulation_current); is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
ed.sim.finalize(ed.simulation_end);
return;
}
if (ed.simulation_current >= ed.simulation_end) {
ed.st = editor_status::editing;
ed.sim.finalize(ed.simulation_end);
return;
}
}
return status::success;
ed.st = editor_status::running_pause;
}
void
application::run_simulations()
{
int running_simulation = 0;
auto running_simulation = 0.f;
editor* ed = nullptr;
while (editors.next(ed))
if (match(ed->st,
editor_status::running_debug,
editor_status::running_thread,
editor_status::running_thread_need_join))
if (ed->is_running())
++running_simulation;
if (!running_simulation)
return;
auto duration = 10000 / running_simulation;
const auto duration = 10000.f / running_simulation;
ed = nullptr;
while (editors.next(ed))
if (match(ed->st,
editor_status::running_debug,
editor_status::running_thread,
editor_status::running_thread_need_join))
ed->sim_st = run_for(
*ed, duration, ed->simulation_end, ed->simulation_current);
if (ed->is_running())
run_for(
*ed,
static_cast<long long int>(duration * ed->synchronize_timestep));
}
editor*
......
......@@ -11,7 +11,6 @@
#include <filesystem>
#include <fstream>
#include <map>
#include <thread>
#include <variant>
#include <vector>
......@@ -60,11 +59,14 @@ enum class log_status : int
enum class editor_status
{
editing,
initializing,
running_debug,
running_thread,
running_thread_need_join
editing, // Default mode. No simulation run.
running, //
running_1_step, //
running_10_step, //
running_100_step, //
running_pause, //
running_stop, //
finalizing // cleanup internal simulation data.
};
struct plot_output
......@@ -223,7 +225,6 @@ struct editor
small_string<16> name;
std::filesystem::path path;
imnodes::EditorContext* context = nullptr;
bool initialized = false;
bool show = true;
simulation sim;
......@@ -234,14 +235,23 @@ struct editor
double simulation_current = 10.0;
double simulation_next_time = 0.0;
long simulation_bag_id = 0;
int step_by_step_bag = 0;
double simulation_during_date;
int simulation_during_bag;
std::thread simulation_thread;
editor_status st = editor_status::editing;
status sim_st = status::success;
bool is_running() const noexcept
{
return match(st,
editor_status::running,
editor_status::running_1_step,
editor_status::running_10_step,
editor_status::running_100_step);
}
bool simulation_show_value = false;
bool stop = false;
......@@ -304,7 +314,7 @@ struct editor
bool use_real_time;
bool starting = true;
double synchronize_timestep;
float synchronize_timestep = 1.f;
top_cluster top;
......@@ -446,6 +456,7 @@ struct application
data_array<editor, editor_id> editors;
std::filesystem::path home_dir;
std::filesystem::path executable_dir;
std::vector<long long int> simulation_duration;
bool show_log = true;
bool show_simulation = true;
......
......@@ -163,6 +163,8 @@ main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
// Main loop
while (!glfwWindowShouldClose(window)) {
app.run_simulations();
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags
// to tell if dear imgui wants to use your inputs.
......
......@@ -192,6 +192,8 @@ main(int, char**)
continue;
}
app.run_simulations();
// Start the Dear ImGui frame
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
......
......@@ -1022,8 +1022,6 @@ editor::initialize(u32 id) noexcept
format(name, "Editor {}", id);
initialized = true;
return status::success;
}
......@@ -2822,8 +2820,7 @@ editor::show_editor() noexcept
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
int node_id;
if (imnodes::IsNodeHovered(&node_id) &&
st == editor_status::running_debug) {
if (imnodes::IsNodeHovered(&node_id) && is_running()) {
const auto index = top.get_index(node_id);
if (index != not_found || top.children[index].first.index() == 0) {
const auto id = std::get<model_id>(top.children[index].first);
......
......@@ -248,275 +248,63 @@ file_output::operator()(const irt::observer& obs,
}
static void
run_synchronized_simulation(window_logger& log_w,
simulation& sim,
double begin,
double end,
double synchronize_timestep,
double& current,
editor_status& st,
status& sim_st,
const bool& stop) noexcept
{
current = begin;
st = editor_status::initializing;
if (sim_st = sim.initialize(current); irt::is_bad(sim_st)) {
log_w.log(3,
"Simulation initialization failure (%s)\n",
irt::status_string(sim_st));
st = editor_status::editing;
return;
}
st = editor_status::running_thread;
do {
const double old = current;
if (sim_st = sim.run(current); irt::is_bad(sim_st)) {
log_w.log(
3, "Simulation failure (%s)\n", irt::status_string(sim_st));
st = editor_status::editing;
return;
}
const auto duration = (current - old) / synchronize_timestep;
if (duration > 0.)
std::this_thread::sleep_for(
std::chrono::duration<double>(duration));
} while (current < end && !stop);
sim.finalize(end);
st = editor_status::running_thread_need_join;
}
static void
run_simulation(window_logger& log_w,
simulation& sim,
double begin,
double end,
double& current,
editor_status& st,
status& sim_st,
const bool& stop) noexcept
{
current = begin;
st = editor_status::initializing;
if (sim_st = sim.initialize(current); irt::is_bad(sim_st)) {
log_w.log(3,
"Simulation initialization failure (%s)\n",
irt::status_string(sim_st));
st = editor_status::editing;
return;
}
st = editor_status::running_thread;
do {
if (sim_st = sim.run(current); irt::is_bad(sim_st)) {
log_w.log(
3, "Simulation failure (%s)\n", irt::status_string(sim_st));
st = editor_status::editing;
return;
}
} while (current < end && !stop);
sim.finalize(end);
st = editor_status::running_thread_need_join;
}
static void
show_simulation_run_once(window_logger& log_w, editor& ed)
{
if (ed.st == editor_status::running_thread_need_join) {
if (ed.simulation_thread.joinable()) {
ed.simulation_thread.join();
ed.st = editor_status::editing;
}
} else if (ed.st == editor_status::editing ||
ed.st == editor_status::running_debug) {
ImGui::Checkbox("Time synchronization", &ed.use_real_time);
if (ed.use_real_time) {
ImGui::InputDouble("t/s", &ed.synchronize_timestep);
if (ed.synchronize_timestep > 0. && ImGui::Button("run")) {
// initialize_observation(log_w, ed);
ed.stop = false;
ed.simulation_thread =
std::thread(&run_synchronized_simulation,
std::ref(log_w),
std::ref(ed.sim),
ed.simulation_begin,
ed.simulation_end,
ed.synchronize_timestep,
std::ref(ed.simulation_current),
std::ref(ed.st),
std::ref(ed.sim_st),
std::cref(ed.stop));
}
} else {
if (ImGui::Button("run")) {
// initialize_observation(log_w, ed);
ed.stop = false;
ed.simulation_thread =
std::thread(&run_simulation,
std::ref(log_w),
std::ref(ed.sim),
ed.simulation_begin,
ed.simulation_end,
std::ref(ed.simulation_current),
std::ref(ed.st),
std::ref(ed.sim_st),
std::cref(ed.stop));
}
}
}
if (ed.st == editor_status::running_thread) {
ImGui::SameLine();
if (ImGui::Button("Force stop"))
ed.stop = true;
}
}
static void
simulation_init(window_logger& log_w, editor& ed)
{
ed.sim.clean();
// initialize_observation(log_w, ed);
ed.simulation_current = ed.simulation_begin;
ed.simulation_during_date = ed.simulation_begin;
ed.st = editor_status::initializing;
ed.models_make_transition.resize(ed.sim.models.capacity(), false);
if (ed.sim_st = ed.sim.initialize(ed.simulation_current);
irt::is_bad(ed.sim_st)) {
log_w.log(3,
"Simulation initialisation failure (%s)\n",
irt::status_string(ed.sim_st));
ed.st = editor_status::editing;
} else {
ed.simulation_next_time = ed.sim.sched.empty()
? time_domain<time>::infinity
: ed.sim.sched.tn();
ed.simulation_bag_id = 0;
ed.st = editor_status::running_debug;
}
}
static void
show_simulation_run_debug(window_logger& log_w, editor& ed)
show_simulation_run(window_logger& /*log_w*/, editor& ed)
{
ImGui::Text("Current time %g", ed.simulation_current);
ImGui::Text("Current bag %ld", ed.simulation_bag_id);
ImGui::Text("Next time %g", ed.simulation_next_time);
ImGui::Text("Model %lu", (unsigned long)ed.sim.sched.size());
if (ImGui::Button("init."))
simulation_init(log_w, ed);
ImGui::SliderFloat("Speed",
&ed.synchronize_timestep,
0.00001f,
1.0f,
"%.6f",
ImGuiSliderFlags_Logarithmic);
ImGui::SameLine();
if (ImGui::Button("Next bag")) {
if (ed.sim_st = ed.sim.run(ed.simulation_current); is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
log_w.log(3,
"Simulation next bag failure (%s)\n",
irt::status_string(ed.sim_st));
}
++ed.simulation_bag_id;
HelpMarker("1.0 means maximum speed to run simulation for all editors. "
"Smaller values slow down the simulation speed.");
if (ImGui::Button("[]")) {
ed.sim.finalize(ed.simulation_current);
ed.simulation_current = ed.simulation_begin;
ed.simulation_bag_id = -1;
ed.simulation_during_date = ed.simulation_begin;
ed.st = editor_status::editing;
}
ImGui::SameLine();
if (ImGui::Button("Bag >> 10")) {
for (int i = 0; i < 10; ++i) {
if (ed.sim_st = ed.sim.run(ed.simulation_current);
is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
log_w.log(3,
"Simulation next bag failure (%s)\n",
irt::status_string(ed.sim_st));
break;
}
++ed.simulation_bag_id;
}
if (ImGui::Button("||")) {
if (ed.is_running())
ed.st = editor_status::running_pause;
else
ed.st = editor_status::running;
}
ImGui::SameLine();
if (ImGui::Button("Bag >> 100")) {
for (int i = 0; i < 100; ++i) {
if (ed.sim_st = ed.sim.run(ed.simulation_current);
is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
log_w.log(3,
"Simulation next bag failure (%s)\n",
irt::status_string(ed.sim_st));
break;
}
++ed.simulation_bag_id;
}
if (ImGui::Button(">")) {
if (ed.st == editor_status::editing)
ed.simulation_bag_id = -1;
ed.st = editor_status::running;
}
ImGui::InputDouble("##date", &ed.simulation_during_date);
ImGui::SameLine();
if (ImGui::Button("run##date")) {
const auto end = ed.simulation_current + ed.simulation_during_date;
while (ed.simulation_current < end) {
if (ed.sim_st = ed.sim.run(ed.simulation_current);
is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
log_w.log(3,
"Simulation next bag failure (%s)\n",
irt::status_string(ed.sim_st));
break;
}
++ed.simulation_bag_id;
}
if (ImGui::Button("+1")) {
if (ed.st == editor_status::editing)
ed.simulation_bag_id = -1;
ed.st = editor_status::running_1_step;
ed.step_by_step_bag = 0;
}
ImGui::InputInt("##bag", &ed.simulation_during_bag);
ImGui::SameLine();
if (ImGui::Button("run##bag")) {
for (int i = 0, e = ed.simulation_during_bag; i != e; ++i) {
if (ed.sim_st = ed.sim.run(ed.simulation_current);
is_bad(ed.sim_st)) {
ed.st = editor_status::editing;
log_w.log(3,
"Simulation next bag failure (%s)\n",
irt::status_string(ed.sim_st));
break;
}
++ed.simulation_bag_id;
}
if (ImGui::Button("+10")) {
if (ed.st == editor_status::editing)
ed.simulation_bag_id = -1;
ed.st = editor_status::running_10_step;
ed.step_by_step_bag = 0;
}
if (ed.st == editor_status::running_debug) {
ed.simulation_next_time = ed.sim.sched.empty()
? time_domain<time>::infinity
: ed.sim.sched.tn();
const auto& l = ed.sim.sched.list_model_id();
std::fill_n(ed.models_make_transition.begin(),
ed.models_make_transition.size(),
false);
for (auto it = l.begin(), e = l.end(); it != e; ++it)
ed.models_make_transition[get_index(*it)] = true;
} else {
ed.simulation_next_time = time_domain<time>::infinity;
ImGui::SameLine();
if (ImGui::Button("+100")) {
if (ed.st == editor_status::editing)
ed.simulation_bag_id = -1;
ed.st = editor_status::running_100_step;
ed.step_by_step_bag = 0;
}
}
......@@ -539,20 +327,26 @@ application::show_simulation_window()
if (ImGui::Button("Output files"))
ed->show_select_directory_dialog = true;
ImGui::Text("output directory: ");
ImGui::Text("output directory:");
#if _WIN32
ImGui::Text("%s", ed->observation_directory.u8string().c_str());
ImGui::InputText(
"Path",
const_cast<char*>(ed->observation_directory.u8string().c_str()),
ed->observation_directory.u8string().size(),
ImGuiInputTextFlags_ReadOnly);
#else
ImGui::Text("%s",
reinterpret_cast<const char*>(
ed->observation_directory.u8string().c_str()));
ImGui::InputText("Path",
const_cast<char*>(reinterpret_cast<const char*>(