#include "main_impl.h" #include "core/object_manager.h" #include "apps/app_selector/app_selector.h" #include "core_v2/memory_manager.h" #include "core_v2/utility.hpp" // from sophiar #include "core/local_connection.h" #include #include #include #include #include #include #include #include // make glad happy #include "core/imgui_utility.hpp" using boost::asio::io_context; using boost::asio::post; using boost::asio::steady_timer; using boost::system::error_code; GLFWwindow *window = nullptr; smart_cuda_stream *default_cuda_stream = nullptr; io_context *main_ctx; BS::thread_pool *g_thread_pool; sophiar::local_connection *g_sophiar_conn = nullptr; using cleanup_list_type = std::vector; cleanup_list_type cleanup_list; //event_timer perf_timer; // performance timer std::unique_ptr ui_timer; std::chrono::milliseconds ui_interval; std::unique_ptr app; bool hide_app_ui = false; bool hide_debug_ui = true; bool show_demo = false; // display config bool full_screen = false; int chose_monitor = 0; struct { int x_pos, y_pos; int width, height; } win_info; // windowed mode info void init_cuda() { CUDA_API_CHECK(cuInit(0)); auto cuda_dev = CUdevice(); CUDA_API_CHECK(cuDeviceGet(&cuda_dev, 0)); // TODO: select device CUDA_API_CHECK(cuCtxCreate(&cuda_ctx, CU_CTX_SCHED_AUTO, cuda_dev)); default_cuda_stream = new smart_cuda_stream(); g_cuda_event_pool = new cuda_event_pool(); std::atexit([] { // elegant cleanup cuCtxDestroy(cuda_ctx); }); } void init_window() { // set GLFW error handler glfwSetErrorCallback([](int error, const char *desc) { SPDLOG_ERROR("GLFW error: code = {}, description = {}", error, desc); }); // create main window auto ret = glfwInit(); assert(ret == GLFW_TRUE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); // TODO: select width and height window = glfwCreateWindow(800, 600, "An not simple platform for visual navigation", nullptr, nullptr); assert(window != nullptr); glfwMakeContextCurrent(window); glfwSwapInterval(0); // load opengl functions auto version = gladLoadGL(glfwGetProcAddress); assert(version > 0); SPDLOG_INFO("Loaded OpenGL {}.{}", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); // enable color blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #ifndef NDEBUG // log opengl error glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user_data) { if (type == GL_DEBUG_TYPE_ERROR) { SPDLOG_ERROR("OpenGL error: type = {}, severity = {}, message = {}", type, severity, message); assert(false); } }, nullptr); #endif // setup imgui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); auto io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(); // elegant cleanup std::atexit([] { ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); glfwDestroyWindow(window); glfwTerminate(); }); } void ui_timer_func(error_code ec) { if (ec == boost::asio::error::operation_aborted) return; assert(ec == error_code()); show_ui(); ui_timer->expires_after(ui_interval); ui_timer->async_wait(ui_timer_func); } void init_all() { init_cuda(); init_window(); main_ctx = new io_context(); main_ob = new object_manager_v2({.ctx = main_ctx}); g_memory_manager = new memory_manager(); constexpr auto background_thread_count = 6; // TODO: load this in config file g_thread_pool = new BS::thread_pool(background_thread_count); auto app_conf = app_selector::create_config(); app_conf.asio_ctx = main_ctx; app_conf.cuda_ctx = &cuda_ctx; app_conf.app_ptr = &app; app = std::make_unique(app_conf); glfwSetWindowTitle(window, app->window_name()); ui_interval = std::chrono::milliseconds(33); // TODO: select refresh rate ui_timer = std::make_unique(*main_ctx, ui_interval); ui_timer->async_wait(ui_timer_func); } void process_keys() { auto &io = ImGui::GetIO(); if (io.WantCaptureKeyboard) return; if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_H)) { // Ctrl+H hide_app_ui ^= true; } if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_D)) { // Ctrl+D hide_debug_ui ^= true; } } void update_display() { auto win = glfwGetCurrentContext(); if (!full_screen) { glfwSetWindowMonitor(win, nullptr, win_info.x_pos, win_info.y_pos, win_info.width, win_info.height, GLFW_DONT_CARE); } else { assert(full_screen); int monitor_num = 0; auto monitors = glfwGetMonitors(&monitor_num); assert(chose_monitor < monitor_num); auto monitor = monitors[chose_monitor]; auto mode = glfwGetVideoMode(monitor); glfwSetWindowMonitor(win, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); } } void recorde_window_info() { auto win = glfwGetCurrentContext(); glfwGetWindowPos(win, &win_info.x_pos, &win_info.y_pos); glfwGetWindowSize(win, &win_info.width, &win_info.height); } void show_display_config() { // display mode if (ImGui::RadioButton("Windowed", !full_screen)) { if (full_screen) { full_screen = false; update_display(); } } ImGui::SameLine(); if (ImGui::RadioButton("Full Screen", full_screen)) { if (!full_screen) { recorde_window_info(); full_screen = true; update_display(); } } if (full_screen) { int monitor_count; auto monitors = glfwGetMonitors(&monitor_count); if (chose_monitor >= monitor_count) { chose_monitor = 0; } auto monitor_name_preview = glfwGetMonitorName(monitors[chose_monitor]); if (ImGui::BeginCombo("Monitor", monitor_name_preview)) { // let user select monitors for (int k = 0; k < monitor_count; ++k) { auto is_selected = (chose_monitor == k); auto monitor_name = fmt::format("{} - {}", k, glfwGetMonitorName(monitors[k])); if (ImGui::Selectable(monitor_name.c_str(), is_selected)) { if (chose_monitor != k) { chose_monitor = k; update_display(); } } if (is_selected) { ImGui::SetItemDefaultFocus(); } } ImGui::EndCombo(); } } } void show_memory_usage() { auto status = g_memory_manager->status(); constexpr float kb_to_mb = 1.0 / 1e6f; ImGui::Text("Host: %.2f MB (%.2f MB)", status.host_allocated * kb_to_mb, status.host_cached * kb_to_mb); ImGui::Text("CUDA: %.2f MB (%.2f MB)", status.cuda_allocated * kb_to_mb, status.cuda_cached * kb_to_mb); } void show_thread_pool_usage() { ImGui::Text("Running: %ld", g_thread_pool->get_tasks_running()); ImGui::Text("Queued: %ld", g_thread_pool->get_tasks_queued()); } void show_debug_ui() { if (ImGui::Begin("Debug")) { ImGui::SeparatorText("Display Config"); show_display_config(); ImGui::SeparatorText("Memory Usage"); show_memory_usage(); ImGui::SeparatorText("Thread Pool Usage"); show_thread_pool_usage(); ImGui::SeparatorText("Miscellaneous"); ImGui::Checkbox("Show Demo", &show_demo); } ImGui::End(); } void show_ui() { glfwPollEvents(); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); if (glfwWindowShouldClose(window)) { ui_timer->cancel(); main_ctx->stop(); return; } process_keys(); assert(app != nullptr); if (!hide_app_ui) { app->show_ui(); } if (!hide_debug_ui) { show_debug_ui(); } if (show_demo) { ImGui::ShowDemoWindow(); } cv::Size frame_size; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glfwGetFramebufferSize(window, &frame_size.width, &frame_size.height); glViewport(0, 0, frame_size.width, frame_size.height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); app->render_background(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); glfwSwapBuffers(window); } void register_cleanup_func(cleanup_func_type func) { cleanup_list.push_back(func); } void cleanup() { app = nullptr; ui_timer = nullptr; // custom cleanup funcs for (auto func: cleanup_list) { func(); } delete main_ob; delete main_ctx; delete g_memory_manager; delete g_sophiar_conn; }