Bladeren bron

Integrated VTK.

jcsyshc 2 jaren geleden
bovenliggende
commit
8f11ae74b6
11 gewijzigde bestanden met toevoegingen van 752 en 515 verwijderingen
  1. 9 2
      CMakeLists.txt
  2. 1 0
      src/main_ext.cpp
  3. 204 204
      src/mvs_camera.cpp
  4. 47 47
      src/mvs_camera.h
  5. 141 141
      src/simple_mq.cpp
  6. 63 63
      src/simple_mq.h
  7. 46 37
      src/simple_opengl.cpp
  8. 19 0
      src/simple_opengl.h
  9. 21 21
      src/variable_defs.h
  10. 158 0
      src/vtk_viewer.cpp
  11. 43 0
      src/vtk_viewer.h

+ 9 - 2
CMakeLists.txt

@@ -8,7 +8,6 @@ add_executable(RemoteAR3 src/main.cpp
         src/frame_sender.cpp
         src/image_process.cpp
         src/simple_mq.cpp
-        src/simple_opengl.cpp
         src/third_party/rs.c)
 
 add_subdirectory(src/image_process)
@@ -49,6 +48,7 @@ else ()
 endif ()
 target_include_directories(${PROJECT_NAME} PRIVATE ${GLAD_DIR}/include)
 target_sources(${PROJECT_NAME} PRIVATE ${GLAD_DIR}/src/gl.c)
+target_sources(${PROJECT_NAME} PRIVATE src/simple_opengl.cpp)
 
 # imgui config
 if (WIN32)
@@ -105,4 +105,11 @@ target_link_libraries(${PROJECT_NAME} ${YAML_CPP_LIBRARIES})
 # Boost config
 find_package(Boost REQUIRED COMPONENTS iostreams)
 target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
-target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
+target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
+
+# VTK config
+set(VTK_DIR /home/tpx/src/VTK-9.3.0/Build/lib/cmake/vtk-9.3)
+find_package(VTK REQUIRED)
+target_link_libraries(${PROJECT_NAME} ${VTK_LIBRARIES})
+vtk_module_autoinit(TARGETS ${PROJECT_NAME} MODULES ${VTK_LIBRARIES})
+target_sources(${PROJECT_NAME} PRIVATE src/vtk_viewer.cpp)

+ 1 - 0
src/main_ext.cpp

@@ -7,6 +7,7 @@
 #include "third_party/timestamp_helper.hpp"
 #include "variable_defs.h"
 #include "video_encoder.h"
+#include "vtk_viewer.h"
 
 #include <spdlog/spdlog.h>
 

+ 204 - 204
src/mvs_camera.cpp

@@ -1,205 +1,205 @@
-#include "mvs_camera.h"
-#include "simple_mq.h"
-#include "third_party/scope_guard.hpp"
-#include "utility.hpp"
-
-#include <MvCameraControl.h>
-
-#include <opencv2/core/mat.hpp>
-
-#include <spdlog/spdlog.h>
-
-using namespace simple_mq_singleton;
-
-namespace mvs {
-
-    bool check_api_call(int api_ret, unsigned int line_number,
-                        const char *file_name, const char *api_call_str) {
-        if (api_ret == MV_OK) [[likely]] return true;
-        SPDLOG_ERROR("MVS api call {} failed at {}:{} with error 0x{:x}",
-                     api_call_str, file_name, line_number, api_ret);
-        return false;
-    }
-
-#define API_CHECK(api_call) \
-    check_api_call( \
-        api_call, __LINE__, __FILE__, #api_call)
-
-#define API_CHECK_P(api_call) \
-    if (!check_api_call( \
-        api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
-        return nullptr
-
-#define API_CHECK_B(api_call) \
-    if (!check_api_call( \
-        api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
-        return false
-
-    struct camera::impl {
-        void *handle = nullptr;
-        std::string name;
-        pixel_type type = RG_8;
-        int image_out_index = -1;
-        int frame_width = -1, frame_height = -1;
-        bool is_capture = false;
-
-        ~impl() {
-            if (is_capture) {
-                API_CHECK(MV_CC_StopGrabbing(handle));
-            }
-            API_CHECK(MV_CC_CloseDevice(handle));
-            API_CHECK(MV_CC_DestroyHandle(handle));
-            SPDLOG_INFO("MVS camera {} closed.", name);
-        };
-
-        void on_error_impl(unsigned int msg_type) const {
-            if (msg_type == 0x8003) return; // stop capture event, not an error
-            SPDLOG_ERROR("MVS camera {} exception 0x{:x}.", name, msg_type);
-        }
-
-        static void on_error(unsigned int msg_type, void *user_data) {
-            ((impl *) user_data)->on_error_impl(msg_type);
-        }
-
-        size_t calc_frame_size() const {
-            switch (type) {
-                case RG_8:
-                    return frame_width * frame_height * sizeof(uint8_t);
-            }
-            unreachable();
-        }
-
-        cv::Mat create_mat_view(unsigned char *data) {
-            switch (type) {
-                case RG_8:
-                    return cv::Mat{frame_height, frame_width, CV_8UC1, data};
-            }
-            unreachable();
-        }
-
-        void on_image_impl(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info) {
-            assert(frame_info->nFrameLen == calc_frame_size());
-            auto img_view = create_mat_view(data);
-            mq().update_variable(image_out_index, img_view.clone());
-        }
-
-        static void on_image(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info, void *user_data) {
-            ((impl *) user_data)->on_image_impl(data, frame_info);
-        }
-
-        static MvGvspPixelType convert_pixel_type(pixel_type type) {
-            switch (type) {
-                case RG_8:
-                    return PixelType_Gvsp_BayerRG8;
-            }
-            unreachable();
-        }
-
-        static impl *create(const create_config &conf) {
-            // find
-            MV_CC_DEVICE_INFO_LIST dev_info_list;
-            API_CHECK_P(MV_CC_EnumDevices(MV_USB_DEVICE, &dev_info_list));
-            MV_CC_DEVICE_INFO *dev_info = nullptr;
-            for (int i = 0; i < dev_info_list.nDeviceNum; ++i) {
-                auto cur_dev_info = dev_info_list.pDeviceInfo[i];
-                auto cur_dev_name = (char *) cur_dev_info->SpecialInfo.stUsb3VInfo.chUserDefinedName;
-                if (cur_dev_name == conf.name) {
-                    dev_info = cur_dev_info;
-                    break;
-                }
-            }
-            if (dev_info == nullptr) {
-                SPDLOG_ERROR("MVS camera with name {} not found.", conf.name);
-                return nullptr;
-            }
-            void *handle = nullptr;
-            API_CHECK_P(MV_CC_CreateHandle(&handle, dev_info));
-            assert(handle != nullptr);
-
-            // open
-            API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
-            API_CHECK_P(MV_CC_CloseDevice(handle)); // close and open again to fix some bug
-            API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
-            SPDLOG_INFO("MVS camera {} opened.", conf.name);
-
-            // config
-            API_CHECK_P(MV_CC_SetEnumValue(handle, "PixelFormat",
-                                           convert_pixel_type(conf.pixel)));
-            API_CHECK_P(MV_CC_SetEnumValue(handle, "AcquisitionMode",
-                                           MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS));
-            API_CHECK_P(MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF));
-            API_CHECK_P(MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", true));
-
-            // create impl
-            auto ret = new impl;
-            auto closer = sg::make_scope_guard([&] { delete ret; });
-            ret->handle = handle;
-            ret->name = conf.name;
-            ret->type = conf.pixel;
-            ret->image_out_index = conf.image_out_index;
-            MVCC_INTVALUE int_val;
-            API_CHECK_P(MV_CC_GetIntValue(handle, "Width", &int_val));
-            ret->frame_width = int_val.nCurValue;
-            API_CHECK_P(MV_CC_GetIntValue(handle, "Height", &int_val));
-            ret->frame_height = int_val.nCurValue;
-
-            // register callbacks
-            API_CHECK_P(MV_CC_RegisterExceptionCallBack(handle, impl::on_error, ret));
-            API_CHECK_P(MV_CC_RegisterImageCallBackEx(handle, impl::on_image, ret));
-
-            closer.dismiss();
-            return ret;
-        }
-
-        bool set_capture_config(const capture_config &conf) {
-            API_CHECK_B(MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", conf.frame_rate));
-            API_CHECK_B(MV_CC_SetFloatValue(handle, "ExposureTime", conf.expo_time_ms * 1000)); // ms -> us
-            API_CHECK_B(MV_CC_SetFloatValue(handle, "Gain", conf.gain_db));
-            return true;
-        }
-
-        bool start_capture() {
-            assert(!is_capture);
-            API_CHECK_B(MV_CC_StartGrabbing(handle));
-            is_capture = true;
-            SPDLOG_INFO("MVS camera {} started capturing.", name);
-            return true;
-        }
-
-        bool stop_capture() {
-            assert(is_capture);
-            API_CHECK_B(MV_CC_StopGrabbing(handle));
-            is_capture = false;
-            SPDLOG_INFO("MVS camera {} stopped capturing.", name);
-            return true;
-        }
-
-    };
-
-    camera::~camera() = default;
-
-    bool camera::set_capture_config(const mvs::capture_config &conf) {
-        return pimpl->set_capture_config(conf);
-    }
-
-    bool camera::start_capture() {
-        return pimpl->start_capture();
-    }
-
-    bool camera::stop_capture() {
-        return pimpl->stop_capture();
-    }
-
-    bool camera::is_capture() const {
-        return pimpl->is_capture;
-    }
-
-    camera *camera::create(const mvs::create_config &conf) {
-        auto pimpl = impl::create(conf);
-        if (pimpl == nullptr) return nullptr;
-        auto ret = new camera;
-        ret->pimpl.reset(pimpl);
-        return ret;
-    }
-
+#include "mvs_camera.h"
+#include "simple_mq.h"
+#include "third_party/scope_guard.hpp"
+#include "utility.hpp"
+
+#include <MvCameraControl.h>
+
+#include <opencv2/core/mat.hpp>
+
+#include <spdlog/spdlog.h>
+
+using namespace simple_mq_singleton;
+
+namespace mvs {
+
+    bool check_api_call(int api_ret, unsigned int line_number,
+                        const char *file_name, const char *api_call_str) {
+        if (api_ret == MV_OK) [[likely]] return true;
+        SPDLOG_ERROR("MVS api call {} failed at {}:{} with error 0x{:x}",
+                     api_call_str, file_name, line_number, api_ret);
+        return false;
+    }
+
+#define API_CHECK(api_call) \
+    check_api_call( \
+        api_call, __LINE__, __FILE__, #api_call)
+
+#define API_CHECK_P(api_call) \
+    if (!check_api_call( \
+        api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
+        return nullptr
+
+#define API_CHECK_B(api_call) \
+    if (!check_api_call( \
+        api_call, __LINE__, __FILE__, #api_call)) [[unlikely]] \
+        return false
+
+    struct camera::impl {
+        void *handle = nullptr;
+        std::string name;
+        pixel_type type = RG_8;
+        int image_out_index = -1;
+        int frame_width = -1, frame_height = -1;
+        bool is_capture = false;
+
+        ~impl() {
+            if (is_capture) {
+                API_CHECK(MV_CC_StopGrabbing(handle));
+            }
+            API_CHECK(MV_CC_CloseDevice(handle));
+            API_CHECK(MV_CC_DestroyHandle(handle));
+            SPDLOG_INFO("MVS camera {} closed.", name);
+        };
+
+        void on_error_impl(unsigned int msg_type) const {
+            if (msg_type == 0x8003) return; // stop capture event, not an error
+            SPDLOG_ERROR("MVS camera {} exception 0x{:x}.", name, msg_type);
+        }
+
+        static void on_error(unsigned int msg_type, void *user_data) {
+            ((impl *) user_data)->on_error_impl(msg_type);
+        }
+
+        size_t calc_frame_size() const {
+            switch (type) {
+                case RG_8:
+                    return frame_width * frame_height * sizeof(uint8_t);
+            }
+            unreachable();
+        }
+
+        cv::Mat create_mat_view(unsigned char *data) {
+            switch (type) {
+                case RG_8:
+                    return cv::Mat{frame_height, frame_width, CV_8UC1, data};
+            }
+            unreachable();
+        }
+
+        void on_image_impl(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info) {
+            assert(frame_info->nFrameLen == calc_frame_size());
+            auto img_view = create_mat_view(data);
+            mq().update_variable(image_out_index, img_view.clone());
+        }
+
+        static void on_image(unsigned char *data, MV_FRAME_OUT_INFO_EX *frame_info, void *user_data) {
+            ((impl *) user_data)->on_image_impl(data, frame_info);
+        }
+
+        static MvGvspPixelType convert_pixel_type(pixel_type type) {
+            switch (type) {
+                case RG_8:
+                    return PixelType_Gvsp_BayerRG8;
+            }
+            unreachable();
+        }
+
+        static impl *create(const create_config &conf) {
+            // find
+            MV_CC_DEVICE_INFO_LIST dev_info_list;
+            API_CHECK_P(MV_CC_EnumDevices(MV_USB_DEVICE, &dev_info_list));
+            MV_CC_DEVICE_INFO *dev_info = nullptr;
+            for (int i = 0; i < dev_info_list.nDeviceNum; ++i) {
+                auto cur_dev_info = dev_info_list.pDeviceInfo[i];
+                auto cur_dev_name = (char *) cur_dev_info->SpecialInfo.stUsb3VInfo.chUserDefinedName;
+                if (cur_dev_name == conf.name) {
+                    dev_info = cur_dev_info;
+                    break;
+                }
+            }
+            if (dev_info == nullptr) {
+                SPDLOG_ERROR("MVS camera with name {} not found.", conf.name);
+                return nullptr;
+            }
+            void *handle = nullptr;
+            API_CHECK_P(MV_CC_CreateHandle(&handle, dev_info));
+            assert(handle != nullptr);
+
+            // open
+            API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
+            API_CHECK_P(MV_CC_CloseDevice(handle)); // close and open again to fix some bug
+            API_CHECK_P(MV_CC_OpenDevice(handle, MV_ACCESS_Control));
+            SPDLOG_INFO("MVS camera {} opened.", conf.name);
+
+            // config
+            API_CHECK_P(MV_CC_SetEnumValue(handle, "PixelFormat",
+                                           convert_pixel_type(conf.pixel)));
+            API_CHECK_P(MV_CC_SetEnumValue(handle, "AcquisitionMode",
+                                           MV_CAM_ACQUISITION_MODE::MV_ACQ_MODE_CONTINUOUS));
+            API_CHECK_P(MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF));
+            API_CHECK_P(MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", true));
+
+            // create impl
+            auto ret = new impl;
+            auto closer = sg::make_scope_guard([&] { delete ret; });
+            ret->handle = handle;
+            ret->name = conf.name;
+            ret->type = conf.pixel;
+            ret->image_out_index = conf.image_out_index;
+            MVCC_INTVALUE int_val;
+            API_CHECK_P(MV_CC_GetIntValue(handle, "Width", &int_val));
+            ret->frame_width = int_val.nCurValue;
+            API_CHECK_P(MV_CC_GetIntValue(handle, "Height", &int_val));
+            ret->frame_height = int_val.nCurValue;
+
+            // register callbacks
+            API_CHECK_P(MV_CC_RegisterExceptionCallBack(handle, impl::on_error, ret));
+            API_CHECK_P(MV_CC_RegisterImageCallBackEx(handle, impl::on_image, ret));
+
+            closer.dismiss();
+            return ret;
+        }
+
+        bool set_capture_config(const capture_config &conf) {
+            API_CHECK_B(MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", conf.frame_rate));
+            API_CHECK_B(MV_CC_SetFloatValue(handle, "ExposureTime", conf.expo_time_ms * 1000)); // ms -> us
+            API_CHECK_B(MV_CC_SetFloatValue(handle, "Gain", conf.gain_db));
+            return true;
+        }
+
+        bool start_capture() {
+            assert(!is_capture);
+            API_CHECK_B(MV_CC_StartGrabbing(handle));
+            is_capture = true;
+            SPDLOG_INFO("MVS camera {} started capturing.", name);
+            return true;
+        }
+
+        bool stop_capture() {
+            assert(is_capture);
+            API_CHECK_B(MV_CC_StopGrabbing(handle));
+            is_capture = false;
+            SPDLOG_INFO("MVS camera {} stopped capturing.", name);
+            return true;
+        }
+
+    };
+
+    camera::~camera() = default;
+
+    bool camera::set_capture_config(const mvs::capture_config &conf) {
+        return pimpl->set_capture_config(conf);
+    }
+
+    bool camera::start_capture() {
+        return pimpl->start_capture();
+    }
+
+    bool camera::stop_capture() {
+        return pimpl->stop_capture();
+    }
+
+    bool camera::is_capture() const {
+        return pimpl->is_capture;
+    }
+
+    camera *camera::create(const mvs::create_config &conf) {
+        auto pimpl = impl::create(conf);
+        if (pimpl == nullptr) return nullptr;
+        auto ret = new camera;
+        ret->pimpl.reset(pimpl);
+        return ret;
+    }
+
 }

+ 47 - 47
src/mvs_camera.h

@@ -1,47 +1,47 @@
-#ifndef REMOTEAR3_MVS_CAMERA_H
-#define REMOTEAR3_MVS_CAMERA_H
-
-#include <memory>
-#include <string_view>
-
-namespace mvs {
-
-    enum pixel_type {
-        RG_8
-    };
-
-    struct create_config {
-        std::string_view name;
-        pixel_type pixel;
-        int image_out_index;
-    };
-
-    struct capture_config {
-        int frame_rate; // frame per second
-        float expo_time_ms;
-        float gain_db;
-    };
-
-    class camera {
-    public:
-
-        ~camera();
-
-        bool set_capture_config(const capture_config &conf);
-
-        bool start_capture();
-
-        bool stop_capture();
-
-        bool is_capture() const;
-
-        static camera *create(const create_config &conf);
-
-    private:
-        struct impl;
-        std::unique_ptr<impl> pimpl;
-    };
-}
-
-
-#endif //REMOTEAR3_MVS_CAMERA_H
+#ifndef REMOTEAR3_MVS_CAMERA_H
+#define REMOTEAR3_MVS_CAMERA_H
+
+#include <memory>
+#include <string_view>
+
+namespace mvs {
+
+    enum pixel_type {
+        RG_8
+    };
+
+    struct create_config {
+        std::string_view name;
+        pixel_type pixel;
+        int image_out_index;
+    };
+
+    struct capture_config {
+        int frame_rate; // frame per second
+        float expo_time_ms;
+        float gain_db;
+    };
+
+    class camera {
+    public:
+
+        ~camera();
+
+        bool set_capture_config(const capture_config &conf);
+
+        bool start_capture();
+
+        bool stop_capture();
+
+        bool is_capture() const;
+
+        static camera *create(const create_config &conf);
+
+    private:
+        struct impl;
+        std::unique_ptr<impl> pimpl;
+    };
+}
+
+
+#endif //REMOTEAR3_MVS_CAMERA_H

+ 141 - 141
src/simple_mq.cpp

@@ -1,142 +1,142 @@
-#include "simple_mq.h"
-
-#include <cassert>
-#include <condition_variable>
-#include <map>
-#include <mutex>
-#include <shared_mutex>
-
-struct simple_mq::impl {
-
-    struct variable_info {
-        std::shared_ptr<void> ptr;
-        std::type_index type;
-        uint64_t update_cnt = 0;
-        std::shared_mutex mu;
-        std::condition_variable_any cv;
-    };
-    using pool_type = std::map<index_type, variable_info *>;
-
-    pool_type pool;
-    std::shared_mutex pool_mu;
-    std::condition_variable_any pool_cv;
-
-    ~impl() {
-        for (auto &info: pool) {
-            delete info.second;
-        }
-    };
-
-    bool update_variable(index_type index,
-                         const std::shared_ptr<void> &ptr,
-                         std::type_index type) {
-        auto iter = pool_type::iterator{};
-        {
-            auto lock_pool = std::shared_lock{pool_mu};
-            iter = pool.find(index);
-        }
-        if (iter == pool.end()) return false;
-        auto &info = iter->second;
-        {
-            auto lock_variable = std::unique_lock{info->mu};
-            info->ptr = ptr;
-            info->type = type;
-            ++info->update_cnt;
-        }
-        info->cv.notify_all();
-        return true;
-    }
-
-    void create_variable(index_type index,
-                         const std::shared_ptr<void> &ptr,
-                         std::type_index type) {
-        auto info = new variable_info{ptr, type};
-        {
-            auto lock_pool = std::unique_lock{pool_mu};
-            pool.emplace(index, info);
-            ++info->update_cnt;
-        }
-        pool_cv.notify_all();
-    }
-
-    std::shared_ptr<void> query_variable(index_type index,
-                                         std::type_index type,
-                                         uint64_t *update_cnt) {
-        auto iter = pool_type::iterator{};
-        {
-            auto lock_pool = std::shared_lock{pool_mu};
-            iter = pool.find(index);
-        }
-        if (iter == pool.end()) {
-            if (update_cnt != nullptr) {
-                *update_cnt = 0;
-            }
-            return nullptr;
-        }
-        auto &info = iter->second;
-        {
-            auto lock_variable = std::shared_lock{info->mu};
-            assert(info->type == type);
-            if (update_cnt != nullptr) {
-                *update_cnt = info->update_cnt;
-            }
-            return info->ptr;
-        }
-    }
-
-    bool wait_variable_update(index_type index, uint64_t old_cnt) {
-        auto iter = pool_type::iterator{};
-        {
-            auto lock_pool = std::shared_lock{pool_mu};
-            iter = pool.find(index);
-        }
-        if (iter == pool.end()) return false;
-        auto &info = iter->second;
-        {
-            auto lock_variable = std::shared_lock{info->mu};
-            info->cv.wait(lock_variable, [=] {
-                return info->update_cnt > old_cnt;
-            });
-        }
-        return true;
-    }
-
-    void wait_variable_create(index_type index) {
-        auto lock_pool = std::shared_lock{pool_mu};
-        pool_cv.wait(lock_pool, [=, this] {
-            return pool.contains(index);
-        });
-    }
-};
-
-simple_mq::simple_mq()
-        : pimpl(std::make_unique<impl>()) {}
-
-simple_mq::~simple_mq() = default;
-
-void simple_mq::update_variable_impl(index_type index,
-                                     const std::shared_ptr<void> &ptr,
-                                     std::type_index type) {
-    auto ret = pimpl->update_variable(index, ptr, type);
-    if (ret) [[likely]] return;
-    pimpl->create_variable(index, ptr, type);
-}
-
-std::shared_ptr<void> simple_mq::query_variable_impl(index_type index,
-                                                     std::type_index type,
-                                                     uint64_t *update_cnt) {
-    return pimpl->query_variable(index, type, update_cnt);
-}
-
-void simple_mq::wait_variable(index_type index, uint64_t old_cnt) {
-    auto ret = pimpl->wait_variable_update(index, old_cnt);
-    if (ret)[[likely]] return;
-    pimpl->wait_variable_create(index);
-}
-
-namespace simple_mq_singleton {
-    simple_mq &mq() {
-        static simple_mq instance;
-        return instance;
-    }
+#include "simple_mq.h"
+
+#include <cassert>
+#include <condition_variable>
+#include <map>
+#include <mutex>
+#include <shared_mutex>
+
+struct simple_mq::impl {
+
+    struct variable_info {
+        std::shared_ptr<void> ptr;
+        std::type_index type;
+        uint64_t update_cnt = 0;
+        std::shared_mutex mu;
+        std::condition_variable_any cv;
+    };
+    using pool_type = std::map<index_type, variable_info *>;
+
+    pool_type pool;
+    std::shared_mutex pool_mu;
+    std::condition_variable_any pool_cv;
+
+    ~impl() {
+        for (auto &info: pool) {
+            delete info.second;
+        }
+    };
+
+    bool update_variable(index_type index,
+                         const std::shared_ptr<void> &ptr,
+                         std::type_index type) {
+        auto iter = pool_type::iterator{};
+        {
+            auto lock_pool = std::shared_lock{pool_mu};
+            iter = pool.find(index);
+        }
+        if (iter == pool.end()) return false;
+        auto &info = iter->second;
+        {
+            auto lock_variable = std::unique_lock{info->mu};
+            info->ptr = ptr;
+            info->type = type;
+            ++info->update_cnt;
+        }
+        info->cv.notify_all();
+        return true;
+    }
+
+    void create_variable(index_type index,
+                         const std::shared_ptr<void> &ptr,
+                         std::type_index type) {
+        auto info = new variable_info{ptr, type};
+        {
+            auto lock_pool = std::unique_lock{pool_mu};
+            pool.emplace(index, info);
+            ++info->update_cnt;
+        }
+        pool_cv.notify_all();
+    }
+
+    std::shared_ptr<void> query_variable(index_type index,
+                                         std::type_index type,
+                                         uint64_t *update_cnt) {
+        auto iter = pool_type::iterator{};
+        {
+            auto lock_pool = std::shared_lock{pool_mu};
+            iter = pool.find(index);
+        }
+        if (iter == pool.end()) {
+            if (update_cnt != nullptr) {
+                *update_cnt = 0;
+            }
+            return nullptr;
+        }
+        auto &info = iter->second;
+        {
+            auto lock_variable = std::shared_lock{info->mu};
+            assert(info->type == type);
+            if (update_cnt != nullptr) {
+                *update_cnt = info->update_cnt;
+            }
+            return info->ptr;
+        }
+    }
+
+    bool wait_variable_update(index_type index, uint64_t old_cnt) {
+        auto iter = pool_type::iterator{};
+        {
+            auto lock_pool = std::shared_lock{pool_mu};
+            iter = pool.find(index);
+        }
+        if (iter == pool.end()) return false;
+        auto &info = iter->second;
+        {
+            auto lock_variable = std::shared_lock{info->mu};
+            info->cv.wait(lock_variable, [=] {
+                return info->update_cnt > old_cnt;
+            });
+        }
+        return true;
+    }
+
+    void wait_variable_create(index_type index) {
+        auto lock_pool = std::shared_lock{pool_mu};
+        pool_cv.wait(lock_pool, [=, this] {
+            return pool.contains(index);
+        });
+    }
+};
+
+simple_mq::simple_mq()
+        : pimpl(std::make_unique<impl>()) {}
+
+simple_mq::~simple_mq() = default;
+
+void simple_mq::update_variable_impl(index_type index,
+                                     const std::shared_ptr<void> &ptr,
+                                     std::type_index type) {
+    auto ret = pimpl->update_variable(index, ptr, type);
+    if (ret) [[likely]] return;
+    pimpl->create_variable(index, ptr, type);
+}
+
+std::shared_ptr<void> simple_mq::query_variable_impl(index_type index,
+                                                     std::type_index type,
+                                                     uint64_t *update_cnt) {
+    return pimpl->query_variable(index, type, update_cnt);
+}
+
+void simple_mq::wait_variable(index_type index, uint64_t old_cnt) {
+    auto ret = pimpl->wait_variable_update(index, old_cnt);
+    if (ret)[[likely]] return;
+    pimpl->wait_variable_create(index);
+}
+
+namespace simple_mq_singleton {
+    simple_mq &mq() {
+        static simple_mq instance;
+        return instance;
+    }
 }

+ 63 - 63
src/simple_mq.h

@@ -1,63 +1,63 @@
-#ifndef REMOTEAR3_SIMPLE_MQ_H
-#define REMOTEAR3_SIMPLE_MQ_H
-
-#include <cstdint>
-#include <memory>
-#include <type_traits>
-#include <typeindex>
-#include <typeinfo>
-
-class simple_mq {
-public:
-    using index_type = int;
-
-    simple_mq();
-
-    ~simple_mq();
-
-    template<typename T>
-    void update_variable_ptr(index_type index,
-                             const std::shared_ptr<T> &ptr) {
-        update_variable_impl(index,
-                             std::static_pointer_cast<void>(ptr),
-                             typeid(T));
-    }
-
-    template<typename T>
-    void update_variable(index_type index, T &&value) {
-        using RT = typename std::remove_cvref_t<T>;
-        update_variable_ptr(index,
-                            std::make_shared<RT>(std::forward<T>(value)));
-    }
-
-    template<typename T>
-    std::shared_ptr<T> query_variable_ptr(index_type index, uint64_t *update_cnt = nullptr) {
-        return std::static_pointer_cast<T>(
-                query_variable_impl(index, typeid(T), update_cnt));
-    }
-
-    template<typename T>
-    T query_variable(index_type index, uint64_t *update_cnt = nullptr) {
-        return *query_variable_ptr<T>(index, update_cnt);
-    }
-
-    void wait_variable(index_type index, uint64_t old_cnt);
-
-private:
-    struct impl;
-    std::unique_ptr<impl> pimpl;
-
-    void update_variable_impl(index_type index,
-                              const std::shared_ptr<void> &ptr,
-                              std::type_index type);
-
-    std::shared_ptr<void> query_variable_impl(index_type index,
-                                              std::type_index type,
-                                              uint64_t *update_cnt);
-};
-
-namespace simple_mq_singleton {
-    simple_mq &mq();
-}
-
-#endif //REMOTEAR3_SIMPLE_MQ_H
+#ifndef REMOTEAR3_SIMPLE_MQ_H
+#define REMOTEAR3_SIMPLE_MQ_H
+
+#include <cstdint>
+#include <memory>
+#include <type_traits>
+#include <typeindex>
+#include <typeinfo>
+
+class simple_mq {
+public:
+    using index_type = int;
+
+    simple_mq();
+
+    ~simple_mq();
+
+    template<typename T>
+    void update_variable_ptr(index_type index,
+                             const std::shared_ptr<T> &ptr) {
+        update_variable_impl(index,
+                             std::static_pointer_cast<void>(ptr),
+                             typeid(T));
+    }
+
+    template<typename T>
+    void update_variable(index_type index, T &&value) {
+        using RT = typename std::remove_cvref_t<T>;
+        update_variable_ptr(index,
+                            std::make_shared<RT>(std::forward<T>(value)));
+    }
+
+    template<typename T>
+    std::shared_ptr<T> query_variable_ptr(index_type index, uint64_t *update_cnt = nullptr) {
+        return std::static_pointer_cast<T>(
+                query_variable_impl(index, typeid(T), update_cnt));
+    }
+
+    template<typename T>
+    T query_variable(index_type index, uint64_t *update_cnt = nullptr) {
+        return *query_variable_ptr<T>(index, update_cnt);
+    }
+
+    void wait_variable(index_type index, uint64_t old_cnt);
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+
+    void update_variable_impl(index_type index,
+                              const std::shared_ptr<void> &ptr,
+                              std::type_index type);
+
+    std::shared_ptr<void> query_variable_impl(index_type index,
+                                              std::type_index type,
+                                              uint64_t *update_cnt);
+};
+
+namespace simple_mq_singleton {
+    simple_mq &mq();
+}
+
+#endif //REMOTEAR3_SIMPLE_MQ_H

+ 46 - 37
src/simple_opengl.cpp

@@ -1,4 +1,3 @@
-#include "cuda_helper.hpp"
 #include "simple_opengl.h"
 
 #include <cuda_gl_interop.h>
@@ -105,42 +104,6 @@ namespace simple_opengl_impl {
         }
     };
 
-    struct smart_texture {
-        GLuint id = 0;
-
-        void create(GLenum format, cv::Size size,
-                    GLint min_filter = GL_NEAREST, GLint max_filter = GL_NEAREST) {
-            if (size == last_size) [[likely]] return;
-            deallocate();
-            allocate(format, size, min_filter, max_filter);
-        }
-
-        ~smart_texture() {
-            deallocate();
-        }
-
-    private:
-        cv::Size last_size;
-
-        void allocate(GLenum format, cv::Size size,
-                      GLint min_filter, GLint max_filter) {
-            glGenTextures(1, &id);
-            glBindTexture(GL_TEXTURE_2D, id);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, max_filter);
-            glTexStorage2D(GL_TEXTURE_2D, 1, format, size.width, size.height);
-            glBindTexture(GL_TEXTURE_2D, 0);
-            last_size = size;
-        }
-
-        void deallocate() {
-            if (id == 0) return;
-            glDeleteTextures(1, &id);
-            id = 0;
-            last_size = {};
-        }
-    };
-
 }
 
 using namespace simple_opengl_impl;
@@ -419,3 +382,49 @@ cv::Size smart_frame_buffer::size() const {
 void smart_frame_buffer::download(cv::cuda::GpuMat *img, cudaStream_t stream) {
     pimpl->download(img, stream);
 }
+
+struct smart_texture::impl {
+
+    smart_texture *q_this = nullptr;
+    cv::Size last_size = {};
+
+    void create(GLenum format, cv::Size size, GLint min_filter, GLint max_filter) {
+        if (size == last_size) [[likely]] return;
+        deallocate();
+        allocate(format, size, min_filter, max_filter);
+    }
+
+    void allocate(GLenum format, cv::Size size,
+                  GLint min_filter, GLint max_filter) {
+        glGenTextures(1, &q_this->id);
+        glBindTexture(GL_TEXTURE_2D, q_this->id);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, max_filter);
+        glTexStorage2D(GL_TEXTURE_2D, 1, format, size.width, size.height);
+        glBindTexture(GL_TEXTURE_2D, 0);
+        last_size = size;
+    }
+
+    void deallocate() {
+        if (q_this->id == 0) return;
+        glDeleteTextures(1, &q_this->id);
+        q_this->id = 0;
+        last_size = {};
+    }
+
+};
+
+smart_texture::smart_texture()
+        : pimpl(std::make_unique<impl>()) {
+    pimpl->q_this = this;
+}
+
+smart_texture::~smart_texture() = default;
+
+void smart_texture::create(GLenum format, cv::Size size, GLint min_filter, GLint max_filter) {
+    pimpl->create(format, size, min_filter, max_filter);
+}
+
+cv::Size smart_texture::size() const {
+    return pimpl->last_size;
+}

+ 19 - 0
src/simple_opengl.h

@@ -1,6 +1,8 @@
 #ifndef REMOTEAR3_SIMPLE_OPENGL_H
 #define REMOTEAR3_SIMPLE_OPENGL_H
 
+#include "cuda_helper.hpp"
+
 #include <opencv2/core/cuda.hpp>
 #include <opencv2/core/mat.hpp>
 
@@ -35,6 +37,23 @@ private:
     std::unique_ptr<impl> pimpl;
 };
 
+struct smart_texture {
+    GLuint id = 0;
+
+    smart_texture();
+
+    ~smart_texture();
+
+    void create(GLenum format, cv::Size size,
+                GLint min_filter = GL_NEAREST, GLint max_filter = GL_NEAREST);
+
+    cv::Size size() const;
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
 class smart_frame_buffer {
 public:
     GLuint id = 0;

+ 21 - 21
src/variable_defs.h

@@ -1,21 +1,21 @@
-#ifndef REMOTEAR3_VARIABLE_DEFS_H
-#define REMOTEAR3_VARIABLE_DEFS_H
-
-constexpr auto IMG_RAW_HOST_LEFT = 0;
-constexpr auto IMG_RAW_HOST_RIGHT = 1;
-
-constexpr auto ENCODER_BUSY = 2;
-constexpr auto ENCODER_CONFIG = 3;
-constexpr auto OUTPUT_FRAME = 4;
-constexpr auto ENCODER_SHOULD_STOP = 5;
-
-constexpr auto CUDA_CONTEXT = 6;
-
-constexpr auto REQUEST_IDR = 7;
-constexpr auto SENDER_CONNECTED = 8;
-constexpr auto SENDER_SHOULD_STOP = 9;
-constexpr auto SENDER_CONFIG = 10;
-
-// global variable declaration
-
-#endif //REMOTEAR3_VARIABLE_DEFS_H
+#ifndef REMOTEAR3_VARIABLE_DEFS_H
+#define REMOTEAR3_VARIABLE_DEFS_H
+
+constexpr auto IMG_RAW_HOST_LEFT = 0;
+constexpr auto IMG_RAW_HOST_RIGHT = 1;
+
+constexpr auto ENCODER_BUSY = 2;
+constexpr auto ENCODER_CONFIG = 3;
+constexpr auto OUTPUT_FRAME = 4;
+constexpr auto ENCODER_SHOULD_STOP = 5;
+
+constexpr auto CUDA_CONTEXT = 6;
+
+constexpr auto REQUEST_IDR = 7;
+constexpr auto SENDER_CONNECTED = 8;
+constexpr auto SENDER_SHOULD_STOP = 9;
+constexpr auto SENDER_CONFIG = 10;
+
+// global variable declaration
+
+#endif //REMOTEAR3_VARIABLE_DEFS_H

+ 158 - 0
src/vtk_viewer.cpp

@@ -0,0 +1,158 @@
+#include "vtk_viewer.h"
+
+#include <vtkCallbackCommand.h>
+#include <vtkGenericOpenGLRenderWindow.h>
+#include <vtkGenericRenderWindowInteractor.h>
+#include <vtkInteractorStyleTrackballCamera.h>
+#include <vtkNew.h>
+#include <vtkOpenGLFramebufferObject.h>
+#include <vtkPolyDataMapper.h>
+#include <vtkRenderer.h>
+#include <vtkSTLReader.h>
+
+#include <spdlog/spdlog.h>
+
+struct vtk_viewer::impl {
+
+    // switching between multiple render window will cause block
+    static vtkSmartPointer<vtkGenericOpenGLRenderWindow> window;
+
+    smart_texture tex;
+    vtkSmartPointer<vtkRenderWindowInteractor> controller;
+    vtkSmartPointer<vtkInteractorStyle> style;
+    vtkSmartPointer<vtkRenderer> renderer;
+
+    static cv::Size to_cv_size(ImVec2 size) {
+        return {(int) size.x, (int) size.y};
+    }
+
+    impl() {
+        if (window == nullptr) [[unlikely]] {
+            window = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
+            window->InitializeFromCurrentContext();
+            window->SetOffScreenRendering(true);
+            window->SetAlphaBitPlanes(true);
+            window->FramebufferFlipYOn();
+            window->SetIsCurrent(true);
+//            window->SwapBuffersOn();
+//            window->SetFrameBlitModeToNoBlit();
+        }
+
+        renderer = vtkSmartPointer<vtkRenderer>::New();
+        renderer->SetUseDepthPeeling(true);
+
+        style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
+        style->SetDefaultRenderer(renderer);
+        controller = vtkSmartPointer<vtkGenericRenderWindowInteractor>::New();
+        controller->SetInteractorStyle(style);
+        controller->EnableRenderOff();
+
+        window->AddRenderer(renderer);
+        window->SetInteractor(controller);
+    }
+
+    void render(cv::Size size, bool interactive) {
+        assert(size.area() > 0);
+        if (size != tex.size()) [[unlikely]] {
+            tex.create(GL_RGBA8, size);
+            controller->SetSize(size.width, size.height);
+            controller->Modified();
+
+        }
+
+        // setup window for render
+        assert(window != nullptr);
+        auto fbo = window->GetDisplayFramebuffer();
+        fbo->Bind();
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.id, 0);
+        fbo->UnBind();
+        window->SetSize(size.width, size.height);
+        window->AddRenderer(renderer);
+        window->SetInteractor(controller);
+        window->Modified();
+
+        if (interactive) {
+            process_event();
+        }
+        window->Render();
+
+        // cleanup
+        window->RemoveRenderer(renderer);
+    }
+
+    void process_event() {
+        if (!ImGui::IsWindowFocused() || !ImGui::IsWindowHovered()) return;
+
+        // set event position
+        auto &io = ImGui::GetIO();
+        io.ConfigWindowsMoveFromTitleBarOnly = true;
+        auto view_pos = ImGui::GetCursorStartPos();
+        auto x_pos = io.MousePos.x - view_pos.x;
+        auto y_pos = io.MousePos.y - view_pos.y;
+        controller->SetEventInformationFlipY(x_pos, y_pos, io.KeyCtrl, io.KeyShift);
+
+        // dispatch event
+        if (ImGui::IsWindowHovered()) {
+            if (io.MouseClicked[ImGuiMouseButton_Left]) {
+                controller->InvokeEvent(vtkCommand::LeftButtonPressEvent);
+            } else if (io.MouseWheel > 0) {
+                controller->InvokeEvent(vtkCommand::MouseWheelForwardEvent);
+            } else if (io.MouseWheel < 0) {
+                controller->InvokeEvent(vtkCommand::MouseWheelBackwardEvent);
+            }
+        }
+        if (io.MouseReleased[ImGuiMouseButton_Left]) {
+            controller->InvokeEvent(vtkCommand::LeftButtonReleaseEvent);
+        }
+        controller->InvokeEvent(vtkCommand::MouseMoveEvent);
+    }
+
+    void show_imgui_window(const char *name, ImVec2 req_size) {
+        ImGui::BeginChild(name, req_size, 0, no_scroll_flag);
+        auto render_size = ImGui::GetContentRegionAvail();
+        auto render_size_cv = to_cv_size(render_size);
+        if (render_size_cv.area() <= 0)return;
+        render(render_size_cv, true);
+        ImGui::Image(reinterpret_cast<void *>(tex.id), render_size);
+        ImGui::EndChild();
+    }
+
+};
+
+vtkSmartPointer<vtkGenericOpenGLRenderWindow> vtk_viewer::impl::window;
+
+vtk_viewer::vtk_viewer()
+        : pimpl(std::make_unique<impl>()) {}
+
+vtk_viewer::~vtk_viewer() = default;
+
+void vtk_viewer::render(cv::Size size) {
+    return pimpl->render(size, false);
+}
+
+smart_texture *vtk_viewer::get_tex() const {
+    return &pimpl->tex;
+}
+
+void vtk_viewer::add_actor(vtkActor *actor) {
+    pimpl->renderer->AddActor(actor);
+}
+
+void vtk_viewer::remove_actor(vtkActor *actor) {
+    pimpl->renderer->RemoveActor(actor);
+}
+
+void vtk_viewer::show(const std::string &name) {
+    pimpl->show_imgui_window(name.c_str(), ImGui::GetContentRegionAvail());
+}
+
+vtkSmartPointer<vtkActor> vtk_viewer::load_stl(const std::string &path) {
+    vtkNew<vtkSTLReader> reader;
+    reader->SetFileName(path.c_str());
+    reader->Update();
+    vtkNew<vtkPolyDataMapper> mapper;
+    mapper->SetInputData(reader->GetOutput());
+    vtkNew<vtkActor> actor;
+    actor->SetMapper(mapper);
+    return actor;
+}

+ 43 - 0
src/vtk_viewer.h

@@ -0,0 +1,43 @@
+#ifndef REMOTEAR3_VTK_VIEWER_H
+#define REMOTEAR3_VTK_VIEWER_H
+
+#include "simple_opengl.h"
+
+#include <imgui.h>
+
+#include <opencv2/core/types.hpp>
+
+#include <vtkActor.h>
+#include <vtkSmartPointer.h>
+
+#include <memory>
+
+class vtk_viewer {
+public:
+
+    static constexpr auto no_scroll_flag = ImGuiWindowFlags_NoScrollWithMouse |
+                                           ImGuiWindowFlags_NoScrollbar;
+
+    vtk_viewer();
+
+    ~vtk_viewer();
+
+    void add_actor(vtkActor *actor);
+
+    void remove_actor(vtkActor *actor);
+
+    void render(cv::Size size);
+
+    smart_texture *get_tex() const;
+
+    void show(const std::string &name = "VtkViewer"); // Show in ImGui as child
+
+    static vtkSmartPointer<vtkActor> load_stl(const std::string &path);
+
+private:
+    struct impl;
+    std::unique_ptr<impl> pimpl;
+};
+
+
+#endif //REMOTEAR3_VTK_VIEWER_H