Эх сурвалжийг харах

Implemented image viewer.

jcsyshc 1 жил өмнө
parent
commit
533e8b3fd1

+ 1 - 1
CMakeLists.txt

@@ -4,10 +4,10 @@ project(DepthGuide)
 set(CMAKE_CXX_STANDARD 20)
 
 add_executable(${PROJECT_NAME} src/main.cpp
-        src/impl/context.cpp
         src/impl/main_impl.cpp
         src/impl/memory_pool.cpp
         src/impl/object_manager.cpp
+        src/module/impl/image_viewer.cpp
         src/render/impl/render_texture.cpp
         src/render/impl/render_utility.cpp)
 

+ 0 - 11
src/context.h

@@ -1,11 +0,0 @@
-#ifndef DEPTHGUIDE_CONTEXT_H
-#define DEPTHGUIDE_CONTEXT_H
-
-#include <memory>
-#include <thread>
-
-// get per-thread object, like memory_pool and io_context.
-template<typename T>
-std::shared_ptr<T> get_pth_obj(std::thread::id tid = std::this_thread::get_id());
-
-#endif //DEPTHGUIDE_CONTEXT_H

+ 4 - 4
src/device/impl/orb_camera.cpp

@@ -41,7 +41,7 @@ namespace orb_camera_impl {
 
     image_f32c1 depth_y16_to_mm(const image_u16c1 &y16, float scale) { // TODO: accelerate with CUDA
         auto y16_info = y16->as_host_info();
-        auto f32_info = create_image_info<float>(y16_info.size, MEM_HOST);
+        auto f32_info = create_image_info<float1>(y16_info.size, MEM_HOST);
         y16->as_host().convertTo(f32_info.as_mat(), CV_32FC1, scale);
         return create_image(f32_info);
     }
@@ -57,7 +57,7 @@ orb_camera::impl *orb_camera::impl::create(orb_camera::create_config conf) {
     auto dev = get_device(conf.sn_str);
     auto ret = new impl();
     ret->pipe = std::make_shared<ob::Pipeline>(dev);
-    ret->par_ctx = get_pth_obj<io_context>(conf.parent_tid);
+    ret->ctx = conf.ctx;
     ret->stream = conf.stream;
     return ret;
 }
@@ -145,11 +145,11 @@ void orb_camera::impl::frames_callback(const frames_type &frames) {
     if (auto d_frame = frames->depthFrame(); d_frame != nullptr) {
         assert(d_name != invalid_obj_name);
         assert(d_frame->format() == OB_FORMAT_Y16);
-        auto y16_img = video_uc_frame_to_image<ushort>(d_frame);
+        auto y16_img = video_uc_frame_to_image<ushort1>(d_frame);
         d_img = depth_y16_to_mm(y16_img, d_frame->getValueScale());
     }
 
-    post(*par_ctx, [=, _c_name = c_name, _d_name = d_name] {
+    post(*ctx, [=, _c_name = c_name, _d_name = d_name] {
         if (c_img != nullptr) { OBJ_SAVE(_c_name, c_img); }
         if (d_img != nullptr) { OBJ_SAVE(_d_name, d_img); }
     });

+ 1 - 1
src/device/impl/orb_camera_impl.h

@@ -29,8 +29,8 @@ using namespace orb_camera_impl;
 struct orb_camera::impl {
 
     std::shared_ptr<ob::Pipeline> pipe;
-    std::shared_ptr<io_context> par_ctx;
     smart_cuda_stream *stream = nullptr;
+    io_context *ctx = nullptr;
 
     using pf_list_type = std::shared_ptr<ob::StreamProfileList>;
     pf_list_type c_pf_list; // color profile list

+ 10 - 6
src/device/impl/orb_camera_ui.cpp

@@ -1,13 +1,17 @@
 #include "orb_camera_ui_impl.h"
 #include "imgui_utility.hpp"
 
+#include <boost/asio/io_context.hpp>
 #include <boost/asio/post.hpp>
 
+using boost::asio::io_context;
 using boost::asio::post;
 
+extern io_context main_ctx;
+
 orb_camera_ui::impl::impl(create_config conf) {
     cam_c_conf.stream = conf.stream;
-    cam_c_conf.parent_tid = std::this_thread::get_id();
+    cam_c_conf.ctx = &main_ctx;
     cam_s_conf.color.name = conf.cf_name;
     cam_s_conf.depth.name = conf.df_name;
 
@@ -72,7 +76,7 @@ void orb_camera_ui::impl::show_config() {
     }
     ImGui::SameLine();
     if (ImGui::Button("R")) {
-        post(*ctx, [this] { refresh_dev_info_list(); });
+        post(main_ctx, [this] { refresh_dev_info_list(); });
     }
 
     // select video config
@@ -116,21 +120,21 @@ void orb_camera_ui::impl::show() {
     if (cam == nullptr) {
         auto guard = imgui_disable_guard(dev_info_list.empty());
         if (ImGui::Button("Open")) {
-            post(*ctx, [this] { open_camera(); });
+            post(main_ctx, [this] { open_camera(); });
         }
     } else {
         assert(cam != nullptr);
         if (ImGui::Button("Close")) {
-            post(*ctx, [this] { cam = nullptr; });
+            post(main_ctx, [this] { cam = nullptr; });
         }
         ImGui::SameLine();
         if (!cam->is_capturing()) {
             if (ImGui::Button("Start")) {
-                post(*ctx, [this] { start_camera(); });
+                post(main_ctx, [this] { start_camera(); });
             }
         } else {
             if (ImGui::Button("Stop")) {
-                post(*ctx, [this] { cam->stop(); });
+                post(main_ctx, [this] { cam->stop(); });
             }
         }
     }

+ 0 - 4
src/device/impl/orb_camera_ui_impl.h

@@ -3,7 +3,6 @@
 
 #include "device/orb_camera.h"
 #include "device/orb_camera_ui.h"
-#include "context.h"
 
 #include <boost/asio/io_context.hpp>
 
@@ -27,9 +26,6 @@ struct orb_camera_ui::impl {
     orb_camera::start_config cam_s_conf;
     std::unique_ptr<orb_camera> cam;
 
-    std::shared_ptr<io_context> ctx
-            = get_pth_obj<io_context>();
-
     explicit impl(create_config conf);
 
     void refresh_dev_info_list();

+ 3 - 1
src/device/orb_camera.h

@@ -4,6 +4,8 @@
 #include "cuda_helper.hpp"
 #include "object_manager.h"
 
+#include <boost/asio/io_context.hpp>
+
 #include <memory>
 #include <thread>
 #include <vector>
@@ -23,8 +25,8 @@ public:
 
     struct create_config {
         const char *sn_str = nullptr; // serial number
-        std::thread::id parent_tid;
         smart_cuda_stream *stream = nullptr;
+        boost::asio::io_context *ctx = nullptr;
     };
 
     static orb_camera *create(create_config conf);

+ 5 - 5
src/image_utility.hpp

@@ -1,7 +1,6 @@
 #ifndef DEPTHGUIDE_IMAGE_UTILITY_HPP
 #define DEPTHGUIDE_IMAGE_UTILITY_HPP
 
-#include "context.h"
 #include "cuda_helper.hpp"
 #include "memory_pool.h"
 
@@ -10,9 +9,10 @@
 template<typename T>
 constexpr inline int get_cv_type() {
     // @formatter:off
+    if constexpr (std::is_same_v<T, uchar1>) { return CV_8UC1; }
     if constexpr (std::is_same_v<T, uchar3>) { return CV_8UC3; }
-    if constexpr (std::is_same_v<T, ushort>) { return CV_16UC1; }
-    if constexpr (std::is_same_v<T, float>) { return CV_32FC1; }
+    if constexpr (std::is_same_v<T, ushort1>) { return CV_16UC1; }
+    if constexpr (std::is_same_v<T, float1>) { return CV_32FC1; }
     // @formatter:on
     return 0;
 }
@@ -135,7 +135,7 @@ auto create_image(image_info_type<T> info) {
 
 using image_u8c3 = std::shared_ptr<smart_image<uchar3>>;
 using image_u8c4 = std::shared_ptr<smart_image<uchar4>>;
-using image_u16c1 = std::shared_ptr<smart_image<ushort>>;
-using image_f32c1 = std::shared_ptr<smart_image<float>>;
+using image_u16c1 = std::shared_ptr<smart_image<ushort1>>;
+using image_f32c1 = std::shared_ptr<smart_image<float1>>;
 
 #endif //DEPTHGUIDE_IMAGE_UTILITY_HPP

+ 0 - 78
src/impl/context.cpp

@@ -1,78 +0,0 @@
-#include "context_impl.h"
-
-#include <algorithm>
-#include <ranges>
-
-namespace context_impl {
-
-    static thread_local pth_objs_type pth_objs;
-
-    pth_objs_type::pth_objs_type() {
-        reg_pth_objs(this);
-        ctx = std::shared_ptr<io_context>(new io_context(), ctx_deleter);
-        mp = std::make_shared<memory_pool>();
-        om = std::make_shared<object_manager>();
-    }
-
-    pth_objs_type::~pth_objs_type() {
-        un_reg_pth_objs();
-    }
-
-    void reg_pth_objs(pth_objs_type *ptr) {
-        auto lock = std::unique_lock(pool_mu);
-        pth_objs_pool.emplace(std::this_thread::get_id(), ptr);
-    }
-
-    void un_reg_pth_objs() {
-        auto lock = std::unique_lock(pool_mu);
-        pth_objs_pool.erase(std::this_thread::get_id());
-    }
-
-    pth_objs_type *get_pth_objs(std::thread::id tid) {
-        if (std::this_thread::get_id() == tid) [[likely]] {
-            return &pth_objs;
-        }
-        { // read other thread's per-thread object
-            auto lock = std::shared_lock(pool_mu);
-            auto iter = pth_objs_pool.find(tid);
-//            assert(iter != pth_objs_pool.end());
-            if (iter == pth_objs_pool.end()) [[unlikely]] return nullptr;
-            return iter->second;
-        }
-    }
-
-//    bool is_pth_objs_valid(pth_objs_type *ptr) {
-//        if (ptr == &pth_objs) [[likely]] return true;
-//        return std::ranges::any_of(
-//                pth_objs_pool | std::views::values,
-//                [=](pth_objs_type *item) { return item == ptr; }
-//        );
-//    }
-
-    void ctx_deleter(io_context *ctx) {
-        ctx->restart();
-        ctx->poll();
-        delete ctx;
-    }
-
-}
-
-using namespace context_impl;
-
-template<>
-std::shared_ptr<io_context> get_pth_obj<io_context>(std::thread::id tid) {
-    auto ret = get_pth_objs(tid);
-    return ret != nullptr ? ret->ctx : nullptr;
-}
-
-template<>
-std::shared_ptr<memory_pool> get_pth_obj<memory_pool>(std::thread::id tid) {
-    auto ret = get_pth_objs(tid);
-    return ret != nullptr ? ret->mp : nullptr;
-}
-
-template<>
-std::shared_ptr<object_manager> get_pth_obj<object_manager>(std::thread::id tid) {
-    auto ret = get_pth_objs(tid);
-    return ret != nullptr ? ret->om : nullptr;
-}

+ 0 - 44
src/impl/context_impl.h

@@ -1,44 +0,0 @@
-#ifndef DEPTHGUIDE_CONTEXT_IMPL_H
-#define DEPTHGUIDE_CONTEXT_IMPL_H
-
-#include "context.h"
-#include "memory_pool.h"
-#include "object_manager.h"
-
-#include <boost/asio/io_context.hpp>
-
-#include <shared_mutex>
-#include <unordered_map>
-
-namespace context_impl {
-
-    using boost::asio::io_context;
-
-    struct pth_objs_type {
-        std::shared_ptr<io_context> ctx;
-        std::shared_ptr<memory_pool> mp;
-        std::shared_ptr<object_manager> om;
-
-        pth_objs_type();
-
-        ~pth_objs_type();
-    };
-
-    using pth_objs_pool_type = std::unordered_map<std::thread::id, pth_objs_type *>;
-    pth_objs_pool_type pth_objs_pool;
-
-    std::shared_mutex pool_mu;
-
-    void reg_pth_objs(pth_objs_type *ptr);
-
-    void un_reg_pth_objs();
-
-    pth_objs_type *get_pth_objs(std::thread::id tid = std::this_thread::get_id());
-
-    //    bool is_pth_objs_valid(pth_objs_type *ptr);
-
-    // processing pending handlers before delete
-    void ctx_deleter(io_context *ctx);
-}
-
-#endif //DEPTHGUIDE_CONTEXT_IMPL_H

+ 41 - 31
src/impl/main_impl.cpp

@@ -1,12 +1,11 @@
 #include "main_impl.h"
-#include "context.h"
 #include "device/orb_camera_ui.h"
 #include "image_utility.hpp"
+#include "module/image_viewer.h"
 #include "object_names.h"
-#include "render/render_texture.h"
-#include "render/render_utility.h"
 
 #include <boost/asio/io_context.hpp>
+#include <boost/asio/post.hpp>
 #include <boost/asio/steady_timer.hpp>
 
 #include <glad/gl.h>
@@ -20,19 +19,21 @@
 #include "imgui_utility.hpp"
 
 using boost::asio::io_context;
+using boost::asio::post;
 using boost::asio::steady_timer;
 using boost::system::error_code;
 
 CUcontext cuda_ctx = nullptr;
 GLFWwindow *window = nullptr;
 smart_cuda_stream *default_cuda_stream = nullptr;
+io_context main_ctx;
 
-std::shared_ptr<io_context> ctx;
 std::unique_ptr<steady_timer> ui_timer;
 std::chrono::milliseconds ui_interval;
 
 // modules
 std::unique_ptr<orb_camera_ui> orb_cam;
+std::unique_ptr<image_viewer> bg_viewer; // background viewer
 
 void init_cuda() {
     cuInit(0);
@@ -107,13 +108,12 @@ void init_window() {
 }
 
 void init_om() {
-    auto om = get_pth_obj<object_manager>();
-    om->save(img_color, image_u8c3());
-    om->save(img_depth, image_f32c1());
-    om->save(img_bg, image_u8c3());
+    OBJ_SAVE(img_color, image_u8c3());
+    OBJ_SAVE(img_depth, image_f32c1());
+    OBJ_SAVE(img_bg, image_u8c3());
 
-    om->observe(img_color, [=](obj_name_type _) {
-        om->save(img_bg, om->query<image_u8c3>(img_color));
+    main_ob.observe(img_color, [=](obj_name_type _) {
+        OBJ_SAVE(img_bg, OBJ_QUERY(image_u8c3, img_color));
     }, INT_MIN);
 }
 
@@ -123,6 +123,15 @@ void init_modules() {
             .stream = default_cuda_stream,
     };
     orb_cam = std::make_unique<orb_camera_ui>(orb_cam_conf);
+
+    auto bg_viewer_conf = image_viewer::create_config{
+            .mode = VIEW_COLOR_DEPTH, .flip_y = true,
+            .stream = default_cuda_stream,
+    };
+    auto &bg_extra_conf = bg_viewer_conf.extra.color_depth;
+    bg_extra_conf.c_name = img_color;
+    bg_extra_conf.d_name = img_depth;
+    bg_viewer = std::make_unique<image_viewer>(bg_viewer_conf);
 }
 
 void ui_timer_func(error_code ec) {
@@ -139,9 +148,8 @@ void init_all() {
     init_om();
     init_modules();
 
-    ctx = get_pth_obj<io_context>();
     ui_interval = std::chrono::milliseconds(33); // TODO: select refresh rate
-    ui_timer = std::make_unique<steady_timer>(*ctx, ui_interval);
+    ui_timer = std::make_unique<steady_timer>(main_ctx, ui_interval);
     ui_timer->async_wait(ui_timer_func);
 }
 
@@ -153,7 +161,7 @@ void show_ui() {
 
     if (glfwWindowShouldClose(window)) {
         ui_timer->cancel();
-        ctx->stop();
+        main_ctx.stop();
         return;
     }
 
@@ -164,6 +172,19 @@ void show_ui() {
             auto id_guard = imgui_id_guard("camera");
             orb_cam->show();
         }
+
+        if (ImGui::CollapsingHeader("Debug")) {
+            if (ImGui::TreeNode("Background")) {
+                bg_viewer->show();
+                ImGui::TreePop();
+            }
+            if (ImGui::TreeNode("Memory Pool")) {
+                if (ImGui::Button("Purge")) {
+                    post(main_ctx, [] { global_mp.purge(); });
+                }
+                ImGui::TreePop();
+            }
+        }
     }
     ImGui::End();
     ImGui::Render();
@@ -174,25 +195,14 @@ void show_ui() {
     glViewport(0, 0, frame_size.width, frame_size.height);
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
-    // TODO: create an background controller
-    auto om = get_pth_obj<object_manager>();
-    auto bg = om->query<image_u8c3>(img_bg);
-    if (bg != nullptr) {
-        static smart_pixel_buffer bg_pbo;
-        static smart_texture bg_tex;
-        auto bg_info = bg->as_host_info();
-        using bg_type = decltype(bg_info)::pix_type;
-        bg_pbo.upload(bg_info, default_cuda_stream);
-        bg_tex.upload<bg_type>(bg_pbo.id, bg_info.size);
-
-        auto info = tex_render_info();
-        info.mode = TEX_COLOR_ONLY;
-        float width_normal = bg_info.size.aspectRatio() / frame_size.aspectRatio();
-        info.range = simple_rect{-1, -1, 2, 2}.fit_aspect(width_normal);
-        info.color.id = bg_tex.id;
-        render_texture(info);
-    }
+    bg_viewer->render();
 
     ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
     glfwSwapBuffers(window);
+}
+
+void cleanup() {
+    ui_timer = nullptr;
+    orb_cam = nullptr;
+    bg_viewer = nullptr;
 }

+ 4 - 3
src/impl/main_impl.h

@@ -3,10 +3,9 @@
 
 #include "cuda_helper.hpp"
 
-struct GLFWwindow;
+#include <boost/asio/io_context.hpp>
 
-extern CUcontext cuda_ctx;
-extern GLFWwindow *window;
+extern boost::asio::io_context main_ctx;
 
 void init_cuda();
 
@@ -20,4 +19,6 @@ void init_all();
 
 void show_ui();
 
+void cleanup();
+
 #endif //DEPTHGUIDE_MAIN_IMPL_H

+ 9 - 49
src/impl/memory_pool.cpp

@@ -1,5 +1,4 @@
 #include "memory_pool_impl.h"
-#include "context.h"
 #include "cuda_helper.hpp"
 #include "utility.hpp"
 
@@ -16,6 +15,8 @@
 using boost::asio::io_context;
 using boost::asio::post;
 
+memory_pool global_mp;
+
 void memory_pool::impl::reg_allocate(mem_info_type mem_info) {
     malloc_pool.emplace(mem_info.ptr, mem_info);
 }
@@ -73,7 +74,7 @@ void *memory_pool::impl::direct_allocate_cuda_pitch(
         size_t width, size_t rows, size_t *pitch) {
     void *ptr = nullptr;
     CUDA_API_CHECK(cudaMallocPitch(&ptr, pitch, width, rows));
-    reg_allocate({.ptr = ptr, .loc = MEM_CUDA, .lay = MEM_LINEAR,
+    reg_allocate({.ptr = ptr, .loc = MEM_CUDA, .lay = MEM_PITCH,
                          .count = *pitch * rows, .pitch = *pitch, .rows = rows});
     return ptr;
 }
@@ -128,13 +129,14 @@ void *memory_pool::impl::allocate_pitch(
     RET_ERROR_P;
 }
 
-void memory_pool::impl::direct_deallocate(void *ptr) {
+void memory_pool::impl::deallocate(void *ptr) {
     auto iter = malloc_pool.find(ptr);
     if (iter == malloc_pool.end()) {
         SPDLOG_WARN("Deallocate unknown pointer: {}.", fmt::ptr(ptr));
         return;
     }
     auto mem_info = iter->second;
+    malloc_pool.erase(iter);
     switch (mem_info.loc) {
         case MEM_HOST: {
             reuse_host_pool.emplace(mem_info.count, mem_info);
@@ -153,12 +155,6 @@ void memory_pool::impl::direct_deallocate(void *ptr) {
     RET_ERROR;
 }
 
-memory_pool::impl::mem_info_type memory_pool::impl::query_mem_info(void *ptr) {
-    auto iter = malloc_pool.find(ptr);
-    assert(iter != malloc_pool.end());
-    return iter->second;
-}
-
 void memory_pool::impl::system_deallocate(mem_info_type mem_info) {
     switch (mem_info.loc) {
         case MEM_HOST: {
@@ -173,38 +169,6 @@ void memory_pool::impl::system_deallocate(mem_info_type mem_info) {
     RET_ERROR;
 }
 
-void memory_pool::impl::deallocate(void *ptr) {
-    if (std::this_thread::get_id() == tid) { // same thread
-        direct_deallocate(ptr);
-    } else {
-        auto mp = get_pth_obj<memory_pool>(tid);
-        if (mp != nullptr) {
-            // use weak_ptr to eliminate dependency loop
-            auto mp_weak = std::weak_ptr(mp);
-            post(*ctx, [=, mem_info = query_mem_info(ptr)] {
-                auto mp = mp_weak.lock();
-                if (mp != nullptr) {
-                    mp->pimpl->direct_deallocate(ptr);
-                } else {
-                    system_deallocate(mem_info);
-                }
-            });
-        } else {
-            system_deallocate(query_mem_info(ptr));
-        }
-    }
-}
-
-bool memory_pool::impl::contains(void *ptr) const {
-    return malloc_pool.contains(ptr);
-}
-
-std::shared_ptr<void> memory_pool::impl::as_shared(void *ptr) {
-    auto mp = get_pth_obj<memory_pool>();
-    assert(mp->contains(ptr));
-    return {ptr, [=](void *ptr) { mp->deallocate(ptr); }};
-}
-
 void memory_pool::impl::purge() {
     for (auto item: reuse_host_pool | std::views::values) {
         system_deallocate(item);
@@ -223,27 +187,23 @@ void memory_pool::impl::purge() {
 }
 
 void *memory_pool::allocate_impl(size_t count, memory_location mem_loc) {
+    auto guard = std::lock_guard(pimpl->mu);
     return pimpl->allocate(count, mem_loc);
 }
 
 void *memory_pool::allocate_pitch_impl(
         size_t width, size_t rows, memory_location mem_loc, size_t *pitch) {
+    auto guard = std::lock_guard(pimpl->mu);
     return pimpl->allocate_pitch(width, rows, mem_loc, pitch);
 }
 
 void memory_pool::deallocate(void *ptr) {
+    auto guard = std::lock_guard(pimpl->mu);
     return pimpl->deallocate(ptr);
 }
 
-bool memory_pool::contains(void *ptr) const {
-    return pimpl->contains(ptr);
-}
-
-std::shared_ptr<void> memory_pool::as_shared_impl(void *ptr) {
-    return impl::as_shared(ptr);
-}
-
 void memory_pool::purge() {
+    auto guard = std::lock_guard(pimpl->mu);
     pimpl->purge();
 }
 

+ 1 - 13
src/impl/memory_pool_impl.h

@@ -1,7 +1,6 @@
 #ifndef DEPTHGUIDE_MEMORY_POOL_IMPL_H
 #define DEPTHGUIDE_MEMORY_POOL_IMPL_H
 
-#include "context.h"
 #include "memory_pool.h"
 
 #include <boost/asio/io_context.hpp>
@@ -12,8 +11,6 @@
 #include <thread>
 #include <unordered_map>
 
-using boost::asio::io_context;
-
 struct memory_pool::impl {
 
     // reuse_length * reuse_threshold >= request_length
@@ -45,8 +42,7 @@ struct memory_pool::impl {
     reuse_cuda_linear_pool_type reuse_cuda_linear_pool;
     reuse_cuda_pitch_pool_type reuse_cuda_pitch_pool;
 
-    std::thread::id tid = std::this_thread::get_id();
-    std::shared_ptr<io_context> ctx = get_pth_obj<io_context>();
+    std::mutex mu;
 
     void reg_allocate(mem_info_type mem_info);
 
@@ -72,18 +68,10 @@ struct memory_pool::impl {
 
     void *allocate_pitch(size_t width, size_t rows, memory_location mem_loc, size_t *pitch);
 
-    void direct_deallocate(void *ptr);
-
-    mem_info_type query_mem_info(void *ptr);
-
     static void system_deallocate(mem_info_type mem_info);
 
     void deallocate(void *ptr);
 
-    bool contains(void *ptr) const;
-
-    static std::shared_ptr<void> as_shared(void *ptr);
-
     void purge();
 };
 

+ 6 - 5
src/impl/object_manager.cpp

@@ -1,6 +1,5 @@
 #include "object_manager.h"
 #include "object_manager_impl.h"
-#include "context.h"
 
 #include <boost/asio/io_context.hpp>
 #include <boost/asio/post.hpp>
@@ -8,6 +7,10 @@
 using boost::asio::io_context;
 using boost::asio::post;
 
+object_manager main_ob;
+
+extern io_context main_ctx;
+
 object_manager::impl::~impl() {
     for (auto &item: obj_pool) {
         auto &obj_st = item.second;
@@ -41,8 +44,7 @@ void object_manager::impl::create_placeholder(name_type obj_name, std::type_inde
 void object_manager::impl::notify_signal(name_type obj_name) {
     auto obj_st = query_st(obj_name);
     if (!obj_st->is_pending) {
-        auto ctx = get_pth_obj<io_context>();
-        post(*ctx, [=] {
+        post(main_ctx, [=] {
             obj_st->is_running = true;
             for (const auto &ob_st: obj_st->ob_list) {
                 ob_st.func(obj_name);
@@ -63,8 +65,7 @@ object_manager::impl::observe(name_type obj_name, const ob_type &cb_func, priori
     auto ob_iter = ob_list->begin();
     ob_list->sort(); // sort to ensure priority order
 
-    auto exit_func = [=, weak_om = std::weak_ptr<object_manager>()] {
-        auto om_ptr = weak_om.lock();
+    auto exit_func = [=] {
         assert(!obj_st->is_running);
         obj_st->ob_list.erase(ob_iter);
     };

+ 2 - 2
src/main.cpp

@@ -1,5 +1,4 @@
 #include "impl/main_impl.h"
-#include "context.h"
 
 #include <boost/asio/io_context.hpp>
 
@@ -10,6 +9,7 @@ using boost::asio::io_context;
 int main() {
 //    spdlog::set_level(spdlog::level::trace);
     init_all();
-    get_pth_obj<io_context>()->run();
+    main_ctx.run();
+    cleanup();
     return 0;
 }

+ 6 - 9
src/memory_pool.h

@@ -9,7 +9,6 @@ enum memory_location {
     MEM_CUDA
 };
 
-// TODO: analyse dependency graph
 class memory_pool {
 public:
 
@@ -26,8 +25,8 @@ public:
     // Only shared pointers created by this can be used after thread deconstruction.
     // Must be called at the same thread as allocation.
     template<typename T>
-    static std::shared_ptr<T> as_shared(T *ptr) {
-        return std::reinterpret_pointer_cast<T>(as_shared_impl(ptr));
+    std::shared_ptr<T> as_shared(T *ptr) {
+        return {ptr, [this](T *ptr) { deallocate(ptr); }};
     }
 
     template<typename T>
@@ -46,8 +45,6 @@ public:
     // free all unused memory
     void purge();
 
-    bool contains(void *ptr) const;
-
 private:
 
     void *allocate_impl(size_t count, memory_location mem_loc);
@@ -55,8 +52,6 @@ private:
     void *allocate_pitch_impl(size_t width, size_t rows,
                               memory_location mem_loc, size_t *pitch);
 
-    static std::shared_ptr<void> as_shared_impl(void *ptr);
-
     struct impl;
     std::unique_ptr<impl> pimpl;
 
@@ -67,10 +62,12 @@ public:
     ~memory_pool();
 };
 
+extern memory_pool global_mp;
+
 #define ALLOC_SHARED(type, n, loc) \
-    get_pth_obj<memory_pool>()->allocate_shared<type>(n, loc)
+    global_mp.allocate_shared<type>(n, loc)
 
 #define ALLOC_PITCH_SHARED(type, cols, rows, loc, pitch) \
-    get_pth_obj<memory_pool>()->allocate_pitch_shared<type>(cols, rows, loc, pitch)
+    global_mp.allocate_pitch_shared<type>(cols, rows, loc, pitch)
 
 #endif //DEPTHGUIDE_MEMORY_POOL_H

+ 41 - 0
src/module/image_viewer.h

@@ -0,0 +1,41 @@
+#ifndef DEPTHGUIDE_IMAGE_VIEWER_H
+#define DEPTHGUIDE_IMAGE_VIEWER_H
+
+#include "object_manager.h"
+#include "render/render_utility.h"
+
+#include <memory>
+
+enum image_viewer_mode {
+    VIEW_COLOR_DEPTH,
+    VIEW_STEREO
+};
+
+class image_viewer {
+public:
+
+    struct create_config {
+        image_viewer_mode mode = VIEW_COLOR_DEPTH;
+        bool flip_y = false;
+        smart_cuda_stream *stream = nullptr;
+        union {
+            struct {
+                obj_name_type c_name, d_name;
+            } color_depth;
+        } extra = {};
+    };
+
+    explicit image_viewer(create_config conf);
+
+    ~image_viewer();
+
+    void show();
+
+    void render();
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+#endif //DEPTHGUIDE_IMAGE_VIEWER_H

+ 137 - 0
src/module/impl/image_viewer.cpp

@@ -0,0 +1,137 @@
+#include "image_viewer_impl.h"
+#include "imgui_utility.hpp"
+#include "render/render_texture.h"
+
+#include <opencv2/imgproc.hpp>
+
+void image_viewer::impl::show_color_depth() {
+    ImGui::RadioButton("Color", &chose_index, 0);
+    ImGui::SameLine();
+    ImGui::RadioButton("Depth", &chose_index, 1);
+    ImGui::SameLine();
+    ImGui::RadioButton("Both", &chose_index, 2);
+
+    {
+        auto guard = imgui_disable_guard(!manual_dep_range);
+        static constexpr float dep_hard_min = 0.15; // TODO: config value
+        static constexpr float dep_hard_max = 10.0;
+        ImGui::DragFloat2("Depth Range", (float *) &dep_range, 0.05f, dep_hard_min, dep_hard_max, "%.2f");
+    }
+    ImGui::SameLine();
+    ImGui::Checkbox("##manual_dep_range", &manual_dep_range);
+
+    if (chose_index == 2) { // both
+        ImGui::SliderFloat("Depth Alpha", &dep_alpha, 0.f, 1.f, "%.2f");
+    }
+}
+
+void image_viewer::impl::show() {
+    switch (conf.mode) {
+        case VIEW_COLOR_DEPTH: {
+            show_color_depth();
+            break;
+        }
+        default: {
+            RET_ERROR;
+        }
+    }
+}
+
+simple_rect image_viewer::impl::calc_range(cv::Size size) {
+    return simple_rect{-1, -1, 2, 2}
+            .fit_aspect(size.aspectRatio() /
+                        query_viewport_size().aspectRatio());
+}
+
+void image_viewer::impl::render_rgb_image(const image_u8c3 &img, float alpha) {
+    auto img_info = img->as_cuda_info(conf.stream);
+    img_tex.upload(img_info, conf.stream);
+
+    auto info = tex_render_info();
+    info.mode = TEX_COLOR_ONLY;
+    info.flip_y = conf.flip_y;
+    info.range = calc_range(img_info.size);
+    info.color.id = img_tex.id;
+    info.color.alpha = alpha;
+    render_texture(info);
+}
+
+void image_viewer::impl::render_color_obj(obj_name_type name, float alpha) {
+    auto img = OBJ_QUERY(image_u8c3, name);
+    if (img == nullptr) [[unlikely]] return;
+    render_rgb_image(img, alpha);
+}
+
+void image_viewer::impl::render_depth_obj(obj_name_type name, float alpha) {
+    auto img = OBJ_QUERY(image_f32c1, name);
+    if (img == nullptr) [[unlikely]] return;
+    auto img_mat = img->as_host(conf.stream);
+
+    // convert to u8c1 // TODO: accelerate with CUDA
+    double min_val, max_val;
+    if (!manual_dep_range) {
+        cv::minMaxLoc(img_mat, &min_val, &max_val, nullptr, nullptr);
+    } else {
+        min_val = dep_range.min * 1000; // m -> mm
+        max_val = dep_range.max * 1000;
+    }
+    auto img_u8 = create_image_info<uchar1>(img_mat.size(), MEM_HOST);
+    img_mat.convertTo(img_u8.as_mat(), CV_8UC1,
+                      255.0 / (max_val - min_val),
+                      -min_val / (max_val - min_val));
+
+    // add false color // TODO: accelerate with CUDA
+    auto img_color = create_image_info<uchar3>(img_mat.size(), MEM_HOST);
+    cv::applyColorMap(img_u8.as_mat(), img_color.as_mat(), cv::COLORMAP_TURBO);
+
+    render_rgb_image(create_image(img_color), alpha);
+}
+
+void image_viewer::impl::render_color_depth() {
+    auto info = conf.extra.color_depth;
+    switch (chose_index) {
+        case 0: { // color
+            render_color_obj(info.c_name);
+            break;
+        }
+        case 1: { // depth
+            render_depth_obj(info.d_name);
+            break;
+        }
+        case 2: { // both
+            render_color_obj(info.c_name);
+            render_depth_obj(info.d_name, dep_alpha);
+            break;
+        }
+        default: {
+            RET_ERROR;
+        }
+    }
+}
+
+void image_viewer::impl::render() {
+    switch (conf.mode) {
+        case VIEW_COLOR_DEPTH: {
+            render_color_depth();
+            break;
+        }
+        default: {
+            RET_ERROR;
+        }
+    }
+}
+
+image_viewer::image_viewer(create_config conf)
+        : pimpl(std::make_unique<impl>()) {
+    pimpl->conf = conf;
+}
+
+image_viewer::~image_viewer() = default;
+
+void image_viewer::show() {
+    pimpl->show();
+}
+
+void image_viewer::render() {
+    pimpl->render();
+}

+ 47 - 0
src/module/impl/image_viewer_impl.h

@@ -0,0 +1,47 @@
+#ifndef DEPTHGUIDE_IMAGE_VIEWER_IMPL_H
+#define DEPTHGUIDE_IMAGE_VIEWER_IMPL_H
+
+#include "module/image_viewer.h"
+#include "render/render_utility.h"
+
+struct image_viewer::impl {
+
+    create_config conf;
+
+    /* for VIEW_COLOR_DEPTH
+     *   0 = color, 1 = depth, 2 = both
+     * for VIEW_STEREO
+     *   0 = left, 1 = right */
+    int chose_index = 0;
+
+    // for depth false color
+    struct {
+        float min = 0.15, max = 10.0;
+    } dep_range; // depth range
+    bool manual_dep_range = false;
+
+    // depth over color alpha
+    float dep_alpha = 0.5;
+
+    smart_texture img_tex;
+
+    void show_color_depth();
+
+    void show();
+
+    static simple_rect calc_range(cv::Size size);
+
+    void render_rgb_image(const image_u8c3 &img, float alpha = 1.0);
+
+    void render_color_obj(obj_name_type name, float alpha = 1.0);
+
+    // render depth with false color
+    void render_depth_obj(obj_name_type name, float alpha = 1.0);
+
+    void render_color_depth();
+
+    void render();
+
+};
+
+#endif //DEPTHGUIDE_IMAGE_VIEWER_IMPL_H

+ 12 - 1
src/object_manager.h

@@ -68,7 +68,18 @@ using obj_name_type = object_manager::name_type;
 
 static constexpr obj_name_type invalid_obj_name = -1;
 
+extern object_manager main_ob;
+
+#define OBJ_QUERY(type, name) \
+    main_ob.query<type>(name)
+
 #define OBJ_SAVE(name, val) \
-    get_pth_obj<object_manager>()->save(name, val)
+    main_ob.save(name, val)
+
+#define OBJ_OBSERVE(name, func) \
+    main_ob.observe(name, func)
+
+#define OBJ_OBSERVE_PRI(name, func, pri) \
+    main_ob.observe(name, func, pri)
 
 #endif //DEPTHGUIDE_OBJECT_MANAGER_H

+ 5 - 10
src/render/impl/render_texture.cpp

@@ -9,9 +9,6 @@ namespace render_texture_impl {
             1, 2, 3 // second triangle
     };
 
-    std::filesystem::path shader_folder
-            = "/home/tpx/project/DepthGuide/src/render/impl/shader"; // TODO: config shader path
-
     GLuint vao = 0, vbo = 0, ebo = 0;
     bool init_ok = false;
 
@@ -77,21 +74,19 @@ namespace render_texture_impl {
     // render color only
     void ren_c_only(const tex_render_info &info) {
         if (pg_color_only == nullptr) {
-            auto vert_path = shader_folder / "tex.vert";
-            auto frag_path = shader_folder / "tex_c_only.frag";
             pg_color_only = std::unique_ptr<smart_program>(
-                    smart_program::create("tex_color_only",
-                                          {{GL_VERTEX_SHADER,   vert_path.c_str()},
-                                           {GL_FRAGMENT_SHADER, frag_path.c_str()}}));
+                    smart_program::create("tex_rgb",
+                                          {{GL_VERTEX_SHADER,   "tex.vert"},
+                                           {GL_FRAGMENT_SHADER, "tex_rgb.frag"}}));
         }
         assert(pg_color_only != nullptr);
         pg_color_only->use();
 
-        pg_color_only->set_uniform_f("opacity", info.color.opacity);
+        pg_color_only->set_uniform_f("alpha", info.color.alpha);
 
         glActiveTexture(GL_TEXTURE0 + 0);
         glBindTexture(GL_TEXTURE_2D, info.color.id);
-        pg_color_only->set_uniform_i("tex", 0);
+        pg_color_only->set_uniform_i("c_tex", 0);
 
         glDisable(GL_DEPTH_TEST);
         config_buffers(info);

+ 6 - 4
src/render/impl/render_utility.cpp

@@ -6,10 +6,11 @@
 
 #include <boost/iostreams/device/mapped_file.hpp>
 
-#include <filesystem>
-
 using boost::iostreams::mapped_file;
 
+std::filesystem::path shader_folder
+        = "/home/tpx/project/DepthGuide/src/render/impl/shader"; // TODO: config shader path
+
 cv::Size query_viewport_size() {
     struct {
         GLint pad[2];
@@ -28,14 +29,15 @@ void check_framebuffer() {
     }
 }
 
-GLuint compile_shader(GLenum type, const char *path) {
+GLuint compile_shader(GLenum type, const char *filename) {
     static std::unordered_map<std::string, GLuint> cache;
-    auto iter = cache.find(path);
+    auto iter = cache.find(filename);
     if (iter != cache.end()) {
         return iter->second;
     }
 
     auto shader = glCreateShader(type);
+    auto path = shader_folder / filename;
     auto file = mapped_file(path, mapped_file::readonly);
     assert(file.is_open());
     auto file_content = file.const_data();

+ 0 - 14
src/render/impl/shader/tex_c_only.frag

@@ -1,14 +0,0 @@
-#version 460
-
-uniform float opacity;
-
-uniform sampler2D tex;
-
-in vec2 frag_uv;
-
-layout (location = 0) out vec4 frag_color;
-
-void main() {
-    frag_color = texture(tex, frag_uv);
-    frag_color.a *= opacity;
-}

+ 14 - 0
src/render/impl/shader/tex_rgb.frag

@@ -0,0 +1,14 @@
+#version 460
+
+uniform float alpha;
+
+uniform sampler2D c_tex; // color texture
+
+in vec2 frag_uv;
+
+layout (location = 0) out vec4 frag_color;
+
+void main() {
+    frag_color.rgb = texture(c_tex, frag_uv).rgb;
+    frag_color.a = alpha;
+}

+ 1 - 1
src/render/render_texture.h

@@ -20,7 +20,7 @@ struct tex_render_info {
     // color texture info
     struct {
         GLuint id = 0;
-        GLfloat opacity = 1.0;
+        GLfloat alpha = 1.0;
     } color;
 
     // depth texture info

+ 51 - 40
src/render/render_utility.h

@@ -9,11 +9,15 @@
 
 #include <boost/core/noncopyable.hpp>
 
+#include <filesystem>
+
+extern std::filesystem::path shader_folder;
+
 cv::Size query_viewport_size();
 
 void check_framebuffer();
 
-GLuint compile_shader(GLenum type, const char *path);
+GLuint compile_shader(GLenum type, const char *filename);
 
 void check_program(const char *name, GLuint id);
 
@@ -48,36 +52,42 @@ struct simple_rect {
     simple_rect fit_aspect(float aspect) const;
 };
 
-// only upload
-class smart_texture : private boost::noncopyable {
+class smart_pixel_buffer : private boost::noncopyable {
 public:
     GLuint id = 0;
-    GLenum format = 0;
-    cv::Size size;
+    GLsizeiptr size = 0;
 
     // used for CUDA inter-op
-    cudaGraphicsResource_t cuda_res = nullptr;
+    cudaGraphicsResource_t cuda_res_up = nullptr; // for upload
+    cudaGraphicsResource_t cuda_res_down = nullptr; // for download
     std::shared_ptr<void> img_ptr;
 
-    ~smart_texture();
+    ~smart_pixel_buffer();
 
-    void create(GLenum format, cv::Size size);
+    void create(GLsizeiptr size);
 
-    void set_filter(GLint min_filter, GLint max_filter);
+    // download from current viewport
+    void download_viewport(GLenum format, GLenum type);
 
     template<typename T>
     void upload(const image_info_type<T> &img, smart_cuda_stream *stream) {
-        // direct upload only supports 1, 2 or 4 components
-        static_assert(!std::is_same_v<T, uchar3>);
-
-        create(get_tex_internal_format<T>(), img.size);
+        create(sizeof(T) * img.size.area());
         upload_impl(img.mem_info(), stream);
     }
 
     template<typename T>
-    void upload(GLuint pbo_id, cv::Size _size) {
-        create(get_tex_internal_format<T>(), _size);
-        upload_impl(pbo_id, get_tex_format<T>(), get_tex_type<T>());
+    void download(const image_info_type<T> &img, smart_cuda_stream *stream) {
+        assert(size == sizeof(T) * img.size.area());
+        download_impl(img.mem_info(), stream);
+    }
+
+    // memory location maintains as img->loc.
+    template<typename T>
+    void download_viewport(image_info_type<T> *img, GLenum format,
+                           GLenum type, smart_cuda_stream *stream) {
+        img->create(query_viewport_size(), img->loc);
+        download_viewport(format, type);
+        download(*img, stream);
     }
 
 private:
@@ -86,46 +96,47 @@ private:
 
     void upload_impl(const image_mem_info &img, smart_cuda_stream *stream);
 
-    void upload_impl(GLuint pbo_id, GLenum format, GLenum type);
+    void download_impl(const image_mem_info &img, smart_cuda_stream *stream);
 
 };
 
-class smart_pixel_buffer : private boost::noncopyable {
+// only upload
+class smart_texture : private boost::noncopyable {
 public:
     GLuint id = 0;
-    GLsizeiptr size = 0;
+    GLenum format = 0;
+    cv::Size size;
 
     // used for CUDA inter-op
-    cudaGraphicsResource_t cuda_res_up = nullptr; // for upload
-    cudaGraphicsResource_t cuda_res_down = nullptr; // for download
+    cudaGraphicsResource_t cuda_res = nullptr;
     std::shared_ptr<void> img_ptr;
 
-    ~smart_pixel_buffer();
+    // for image upload
+    smart_pixel_buffer img_pbo;
 
-    void create(GLsizeiptr size);
+    ~smart_texture();
 
-    // download from current viewport
-    void download_viewport(GLenum format, GLenum type);
+    void create(GLenum format, cv::Size size);
 
-    template<typename T>
-    void upload(const image_info_type<T> &img, smart_cuda_stream *stream) {
-        create(sizeof(T) * img.size.area());
-        upload_impl(img.mem_info(), stream);
-    }
+    void set_filter(GLint min_filter, GLint max_filter);
 
     template<typename T>
-    void download(const image_info_type<T> &img, smart_cuda_stream *stream) {
-        assert(size == sizeof(T) * img.size.area());
-        download_impl(img.mem_info(), stream);
+    void upload(GLuint pbo_id, cv::Size _size) {
+        create(get_tex_internal_format<T>(), _size);
+        upload_impl(pbo_id, get_tex_format<T>(), get_tex_type<T>());
     }
 
-    // memory location maintains as img->loc.
     template<typename T>
-    void download_viewport(image_info_type<T> *img, GLenum format,
-                           GLenum type, smart_cuda_stream *stream) {
-        img->create(query_viewport_size(), img->loc);
-        download_viewport(format, type);
-        download(*img, stream);
+    void upload(const image_info_type<T> &img, smart_cuda_stream *stream) {
+        create(get_tex_internal_format<T>(), img.size);
+
+        // direct upload only supports 1, 2 or 4 components
+        if constexpr (std::is_same_v<T, uchar3>) {
+            img_pbo.upload(img, stream);
+            upload<T>(img_pbo.id, size);
+        } else {
+            upload_impl(img.mem_info(), stream);
+        }
     }
 
 private:
@@ -134,7 +145,7 @@ private:
 
     void upload_impl(const image_mem_info &img, smart_cuda_stream *stream);
 
-    void download_impl(const image_mem_info &img, smart_cuda_stream *stream);
+    void upload_impl(GLuint pbo_id, GLenum format, GLenum type);
 
 };