#include #include #include "bench_window.hpp" #include "main.hpp" bench_window::bench_window() : runner_stop_now(false), runner_stop_on_death(true), runner_stop_on_win(true), runner_hyperspeed(false), core_fading(false), control_box(Gtk::Orientation::VERTICAL), new_round_button("new round"), single_step_button("single step"), start_button("start"), stop_button("stop"), core_fading_toggle("core fading"), hyperspeed_toggle("hyperspeed"), pause_on_death_toggle("pause on death"), pause_on_win_toggle("pause on win"), target_core_rate_label(), target_core_rate_scale(), add_warrior_button("add warrior"), remove_warrior_button("remove warrior"), runner({}), runner_active(false) { warrior_list_store = Gtk::TreeStore::create(warrior_list_columns); instructions_store = Gtk::TreeStore::create(instructions_columns); warrior_list_view.set_model(warrior_list_store); instructions_view.set_model(instructions_store); instructions_view.get_selection()->set_mode(Gtk::SelectionMode::NONE); warrior_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &bench_window::update_buttons)); warrior_list_view.append_column("name", warrior_list_columns.warrior_name); warrior_list_view.append_column("#p", warrior_list_columns.processes); warrior_list_view.append_column("pc", warrior_list_columns.next_pc); instructions_view.append_column("", instructions_columns.address); instructions_view.append_column("", instructions_columns.instruction); for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i) { auto row = instructions_store->append(); (*row)[instructions_columns.address] = i; } update_ui(); pause_on_death_toggle.activate(); pause_on_win_toggle.activate(); new_round_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_new_round)); single_step_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_single_step)); start_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_start)); stop_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_stop)); add_warrior_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_add_warrior)); remove_warrior_button.signal_clicked().connect(sigc::mem_fun(*this, &bench_window::on_click_remove_warrior)); core_fading_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_core_fading)); hyperspeed_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_hyperspeed)); pause_on_death_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_pause_on_death)); pause_on_win_toggle.signal_toggled().connect(sigc::mem_fun(*this, &bench_window::on_toggle_pause_on_win)); target_core_rate_scale.signal_value_changed().connect(sigc::mem_fun(*this, &bench_window::on_target_core_rate_changed)); target_core_rate_scale.set_range(0.0, 4.0); target_core_rate_scale.set_value(std::log10(20.0)); control_box.append(new_round_button); control_box.append(single_step_button); control_box.append(start_button); control_box.append(stop_button); control_box.append(core_fading_toggle); control_box.append(hyperspeed_toggle); control_box.append(pause_on_death_toggle); control_box.append(pause_on_win_toggle); control_box.append(target_core_rate_label); control_box.append(target_core_rate_scale); control_box.append(add_warrior_button); control_box.append(remove_warrior_button); warrior_list_scroll.set_child(warrior_list_view); warrior_list_scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC); warrior_list_scroll.set_vexpand(); control_box.append(warrior_list_scroll); control_box.append(core_rate_label); control_box.append(core_render_label); instructions_scroll.set_child(instructions_view); instructions_scroll.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC); control_box.set_size_request(200, -1); core.set_size_request(200, 200); core.set_expand(); instructions_scroll.set_size_request(200, -1); main_box.append(control_box); main_box.append(core); main_box.append(instructions_scroll); set_child(main_box); set_title("lib94 bench"); runner_update_ui_dispatcher.connect(sigc::mem_fun(*this, &bench_window::update_ui)); runner_stopping_dispatcher.connect(sigc::mem_fun(*this, &bench_window::on_runner_stopping)); } void bench_window::add_modified_for_instruction_view(std::set the_set) { for (lib94::number_t n : the_set) modified_addresses_for_instructions_view.insert(n); } const lib94::warrior *bench_window::do_step() { auto time = std::chrono::system_clock::now(); last_core_step_distance = time - last_core_step_start; last_core_step_start = time; const lib94::warrior *result = lib94::single_step(); core.mut.lock(); if (core_fading) core.age_all(); core.add_new_writes(lib94::get_written_addresses()); core.add_new_reads(lib94::get_read_addresses()); core.add_new_executions(lib94::get_executed_addresses()); core.mut.unlock(); add_modified_for_instruction_view(lib94::get_written_addresses()); add_modified_for_instruction_view(lib94::get_read_addresses()); add_modified_for_instruction_view(lib94::get_executed_addresses()); lib94::clear_address_sets(); return result; } static constexpr std::chrono::nanoseconds min_hyperspeed_ui_update_distance(1000000); void bench_window::runner_main() { std::chrono::system_clock::time_point last_step = std::chrono::system_clock::now(); core_mutex.lock(); while (!runner_stop_now) { bool death = do_step() != 0; if (runner_stop_on_death && death) break; if (runner_stop_on_win && death && lib94::alive_warrior_count() == 1) break; core_mutex.unlock(); if (runner_hyperspeed) { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); if (now - last_hyperspeed_ui_update >= min_hyperspeed_ui_update_distance) { last_hyperspeed_ui_update = now; runner_update_ui_dispatcher.emit(); } } else { runner_update_ui_dispatcher.emit(); std::this_thread::sleep_until(last_step + time_between_steps); last_step = std::chrono::system_clock::now(); } core_mutex.lock(); } runner_stopping_dispatcher.emit(); core_mutex.unlock(); } void bench_window::on_click_single_step() { do_step(); update_ui(); } void bench_window::on_click_new_round() { lib94::clear_core({ .op = lib94::DAT, .mod = lib94::F, .amode = lib94::DIRECT, .bmode = lib94::DIRECT, .anumber = 0, .bnumber = 0 }); if (!lib94::init_round(warriors.data(), warriors.size(), 0, true)) { Gtk::MessageDialog *md = new Gtk::MessageDialog("warriors do not fit in core; removing last warrior"); md->set_transient_for(*this); md->set_modal(); md->signal_response().connect([md](int) {delete md;}); md->show(); //is this safe? delete warriors.back(); warriors.pop_back(); if (warriors.size() == 0) { update_ui(); return; } assert(lib94::init_round(warriors.data(), warriors.size(), 0, true)); } core.mut.lock(); core.age_scale = std::pow(2.0 / 3.0, 1.0 / (float)warriors.size()); core.clear_all(); core.mut.unlock(); modified_addresses_for_instructions_view.clear(); for (lib94::number_t i = 0; i < LIB94_CORE_SIZE; ++i) instructions_store->children()[i][instructions_columns.instruction] = lib94::instruction_to_string(lib94::get_instruction(i)); update_ui(); } void bench_window::on_click_start() { runner_active = true; update_ui(); runner_stop_now = false; runner = std::thread(sigc::mem_fun(*this, &bench_window::runner_main)); } void bench_window::on_click_stop() { runner_stop_now = true; } void bench_window::on_toggle_core_fading() { core_fading = core_fading_toggle.get_active(); } void bench_window::on_toggle_hyperspeed() { runner_hyperspeed = hyperspeed_toggle.get_active(); } void bench_window::on_toggle_pause_on_death() { runner_stop_on_death = pause_on_death_toggle.get_active(); } void bench_window::on_toggle_pause_on_win() { runner_stop_on_win = pause_on_win_toggle.get_active(); } void bench_window::on_target_core_rate_changed() { double target_rate = std::pow(10.0, target_core_rate_scale.get_value()); target_core_rate_label.set_text("target core rate (" + hz_to_string(target_rate) + ")"); time_between_steps = std::chrono::nanoseconds((int)(1000000000.0 / target_rate)); } void bench_window::on_add_warrior_dialog_response(int response_id, Gtk::FileChooserDialog *dialog) { if (response_id == Gtk::ResponseType::OK) { Glib::RefPtr file = dialog->get_file(); char *contents; gsize length; file->load_contents(contents, length); try { warriors.push_back(lib94::compile_warrior(std::string(contents, length))); on_click_new_round(); } catch (const lib94::compiler_exception &ex) { Gtk::MessageDialog *md = new Gtk::MessageDialog(std::string("failed to compile: ") + ex.message + " on line " + std::to_string(ex.source_line_number)); md->set_transient_for(*this); md->set_modal(); md->signal_response().connect([md](int) {delete md;}); md->show(); } delete contents; } delete dialog; } void bench_window::on_click_add_warrior() { Gtk::FileChooserDialog *dialog = new Gtk::FileChooserDialog("select a warrior"); dialog->set_transient_for(*this); dialog->set_modal(); dialog->signal_response().connect(sigc::bind(sigc::mem_fun(*this, &bench_window::on_add_warrior_dialog_response), dialog)); dialog->add_button("add", Gtk::ResponseType::OK); dialog->add_button("cancel", Gtk::ResponseType::CANCEL); Glib::RefPtr text_filter = Gtk::FileFilter::create(); text_filter->add_mime_type("text/plain"); text_filter->set_name("text files"); dialog->add_filter(text_filter); Glib::RefPtr any_filter = Gtk::FileFilter::create(); any_filter->add_pattern("*"); any_filter->set_name("all files"); dialog->add_filter(any_filter); dialog->show(); } void bench_window::on_click_remove_warrior() { auto iter = warrior_list_view.get_selection()->get_selected(); if (iter) { while (!(*iter)[warrior_list_columns.warrior]) iter = iter->parent(); const lib94::warrior *w = (*iter)[warrior_list_columns.warrior]; for (auto i = warriors.begin(); i != warriors.end(); ++i) if (*i == w) { warriors.erase(i); break; } if (warriors.size()) on_click_new_round(); else { lib94::remove_all_warriors(); lib94::clear_address_sets(); core.mut.lock(); core.clear_all(); core.mut.unlock(); update_ui(); } delete w; } } void bench_window::update_buttons() { new_round_button.set_sensitive(!runner_active && warriors.size() > 0); single_step_button.set_sensitive(!runner_active && lib94::alive_warrior_count() > 0); start_button.set_sensitive(!runner_active && lib94::alive_warrior_count() > 0); stop_button.set_sensitive(runner_active); add_warrior_button.set_sensitive(!runner_active); remove_warrior_button.set_sensitive(!runner_active && warrior_list_view.get_selection()->get_selected()); warrior_list_view.get_selection()->set_mode(runner_active ? Gtk::SelectionMode::NONE : Gtk::SelectionMode::SINGLE); core_render_label.set_text("core render: " + ns_to_string(core.last_draw_time)); core_rate_label.set_text("core rate: " + hz_to_string(1000000000.0 / (double)last_core_step_distance.count())); } void bench_window::update_ui() { if (runner_active && runner_hyperspeed) { update_buttons(); return; } if (runner_active) core_mutex.lock(); update_buttons(); core.queue_draw(); warrior_list_store->clear(); for (const lib94::warrior *w : warriors) { auto w_row = warrior_list_store->append(); (*w_row)[warrior_list_columns.warrior] = w; (*w_row)[warrior_list_columns.warrior_name] = w->name; auto procs = lib94::get_processes(w); (*w_row)[warrior_list_columns.processes] = procs.size(); (*w_row)[warrior_list_columns.next_pc] = procs.size() ? std::to_string(procs[0]) : ""; } for (lib94::number_t i : modified_addresses_for_instructions_view) instructions_store->children()[i][instructions_columns.instruction] = lib94::instruction_to_string(lib94::get_instruction(i)); modified_addresses_for_instructions_view.clear(); if (runner_active) core_mutex.unlock(); } void bench_window::on_runner_stopping() { runner.join(); runner_active = false; update_ui(); }