Skip to content

File plugin.cpp

File List > ada > scene_management > plugin.cpp

Go to the documentation of this file

// This version only test for speed since we don't have Open3D working on Orin yet
// #include 
#include "plugin.hpp"

#include 
#include 

// for printing out mesh
#define VERIFY

// new slim scene management
using namespace ILLIXR;
using namespace ILLIXR::data_format;

[[maybe_unused]] scene_management::scene_management(const std::string& name_, phonebook* pb_)
    : threadloop{name_, pb_}
    , switchboard_{phonebook_->lookup_impl<switchboard>()} {
    //, input_inactive_mesh_{switchboard_->get_buffered_reader("decoded_inactive_scene")}
    //, input_vb_lists_{switchboard_->get_buffered_reader("VB_update_lists")} {
    switchboard_->schedule<draco_type>(id_, "decoded_inactive_scene",
                                       [&](switchboard::ptr<const draco_type> datum, std::size_t) {
                                           this->process_inactive_frame(datum);
                                       });
    switchboard_->schedule<vb_type>(id_, "VB_update_lists", [&](switchboard::ptr<const vb_type> datum, std::size_t) {
        this->process_vb_lists(datum);
    });

    current_id_      = -1;
    last_processed_  = -1;
    chunk_counter_   = 0;
    last_cleaned_    = -1;
    mesh_processing_ = false;
    clean_waiting_   = false;

    pending_chunks_.reserve(thread_count_ * 5);
    pending_clean_reqs_.reserve(thread_count_ * 5);
    mesh_management_latency_.open(data_path_ + "/mesh_management_latency.csv");

    const char* env_var_name   = "FRAME_COUNT";
    const char* env_value      = std::getenv(env_var_name);
    const char* env_var_name_1 = "FPS";
    const char* env_value_1    = std::getenv(env_var_name_1);
    const char* env_var_name_2 = "PARTIAL_MESH_COUNT";
    const char* env_value_2    = std::getenv(env_var_name_2);

    if (env_value != nullptr && env_value_1 != nullptr && env_value_2 != nullptr) {
        try {
            frame_count_  = std::stoul(env_value);
            fps_          = std::stoul(env_value_1);
            thread_count_ = std::stoul(env_value_2);
            std::cout << "SM: FRAME_COUNT is: " << frame_count_ << " FPS is " << fps_ << " CHUNK_COUNT is " << thread_count_
                      << std::endl;
        } catch (const std::invalid_argument& e) {
            std::cerr << "SM: Invalid argument: the environment variable is not a valid unsigned integer." << std::endl;
        } catch (const std::out_of_range& e) {
            std::cerr << "SM: Out of range: the value of the environment variable is too large for an unsigned integer."
                      << std::endl;
        }
    } else {
        std::cerr << "SM: Environment variable not found." << std::endl;
    }
    std::cout.flush();
}

void scene_management::process_vb_lists(switchboard::ptr<const vb_type>& datum) {
    // printf("================================Device Mesh Manager: Started Processing VB List for Scene
    // %u=========================\n", datum->scene_id);
    if (mesh_processing_) {
        // if the old frame is still processing we will wait
        pending_clean_reqs_.push_back(datum);
        clean_waiting_ = true;
    } else {
        auto start = std::chrono::high_resolution_clock::now();

        // stage 1 outdated region processing
        grid_.clean_mesh_vb_redesign_with_list(datum->unique_VB_lists);
        grid_.deleted_ranges_processing();
        last_cleaned_ = static_cast<int>(datum->scene_id);

        auto end = std::chrono::high_resolution_clock::now();
        auto duration =
            static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()) / 1000.0;
        mesh_management_latency_ << "Clean " << datum->scene_id << " " << duration << "\n";
        printf("===Device Mesh Manager: Finished Processing VB List for Scene %u===\n", datum->scene_id);
    }
}

void scene_management::process_inactive_frame(switchboard::ptr<const draco_type>& datum) {
    // printf("================================Device Mesh Manager: Started Scene ID: %u=========================\n",
    // datum->frame_id); printf("receiving a new scene with id %d, last processed %d,  current_id %d, chunk_counter %u, last
    // cleaned %d, pending chunk size %zu\n", datum->frame_id, last_processed, current_id, chunk_counter, last_cleaned,
    // pending_chunks.size());
    if (static_cast<int>(datum->frame_id) > last_processed_ && static_cast<int>(datum->frame_id) > current_id_) {
        current_id_    = static_cast<int>(datum->frame_id);
        chunk_counter_ = 1;
    } else if (static_cast<int>(datum->frame_id) == current_id_) {
        chunk_counter_++;
    }
    if (chunk_counter_ <= thread_count_) {
        pending_chunks_.push_back(datum);
    }
    if (chunk_counter_ == thread_count_) {
        printf("===Device Mesh Manager: Processing Scene %u with %zu pending chunks===\n", datum->frame_id,
               pending_chunks_.size());
        mesh_processing_ = true;
        // pyh omitting the code for merging multiple clean requests (not used for the paper)
        auto const_start = std::chrono::high_resolution_clock::now();

        // pyh step1 restore unused nullified faces
        grid_.restore_deleted_faces();

        auto start = std::chrono::high_resolution_clock::now();
        for (uint i = 0; i < thread_count_; ++i) {
            // pyh this is Partial VB-Aligned Vertex Merging (S4.4)
            grid_.append_mesh_allocate(pending_chunks_[i]->scene_update_mapping);
        }
        auto end = std::chrono::high_resolution_clock::now();
        auto duration =
            static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()) / 1000.0;
        mesh_management_latency_ << "Merge " << datum->frame_id << " " << duration << "\n";

        start = std::chrono::high_resolution_clock::now();

        unsigned current_gap = 0;
        // this is Live Mesh Integration & Mesh Nullification (Sec4.3 Stage 3 and Stage 4)
        current_gap = grid_.append_mesh_match_and_insert(false);

        end      = std::chrono::high_resolution_clock::now();
        duration = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()) / 1000.0;
        mesh_management_latency_ << "Map " << datum->frame_id << " " << duration << "\n";

        // At this point the Scene Mesh is up-to-date
        duration =
            static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - const_start).count()) / 1000.0;

        size_t vertices_size_in_bytes = grid_.vertices_.size() * sizeof(Eigen::Vector3d);
        size_t faces_size_in_bytes    = grid_.faces_.size() * sizeof(int);
        size_t total_size_in_bytes    = vertices_size_in_bytes + faces_size_in_bytes;

        mesh_management_latency_ << "Display " << datum->frame_id << " " << duration << " " << vertices_size_in_bytes << " "
                                 << faces_size_in_bytes << " " << total_size_in_bytes << " " << current_gap << "\n";

        auto since_epoch = end.time_since_epoch();
        auto millis      = std::chrono::duration_cast<std::chrono::milliseconds>(since_epoch).count();
        // record timestamp on when mesh is available
        mesh_management_latency_ << "Ready " << datum->frame_id << " " << millis << "\n";

        // Note: This release does not ship a test application (Our Jetson Jetpack version has some Open3D dependency
        // issues) and for evaluation we used scene update time which does not need an App.
        //   To enable Scene Request Handling, Integrate by:
        //   1) Publishing a SceneRequest to switchboard from your app/plugin.
        //   2) Let Scene Management subscribes to listen for it and when ready give the update
        //       2.1 For cached scene, one can simply publish to the switchboard current grid.vertices and grid.faces
        //       2.2 For latest scene, need to create a send a request signal (like how depth encoding and mesh compression)
        //       to InfiniTAM and let it start GetMesh() immediately, then after the use the active_id to specify
        //      it as a latest scene request then pretty much following the same code logic and wait until Ready before
        // publish
        // TODO: minimal example later.

        start = std::chrono::high_resolution_clock::now();

        pending_chunks_.erase(pending_chunks_.begin(), pending_chunks_.begin() + thread_count_);

        end      = std::chrono::high_resolution_clock::now();
        duration = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()) / 1000.0;
        // PP: post-processing (remove partial mesh chunks) very minimal but recorded if people want to test it out
        mesh_management_latency_ << "PP " << datum->frame_id << " " << duration << "\n";

#if defined VERIFY
        if (datum->frame_id == ((frame_count_ / fps_) - 1)) {
            grid_.print_mesh_as_obj(datum->frame_id, 1, "");
        }
#endif
        // if a clean request received while the previous frame is processing need to clean it
        if (clean_waiting_) {
            // printf("before finish processing frame %u, we already received cleaning request\n", datum->frame_id);
            start                = std::chrono::high_resolution_clock::now();
            auto pending_request = pending_clean_reqs_.back();
            grid_.clean_mesh_vb_redesign_with_list(pending_request->unique_VB_lists);
            grid_.deleted_ranges_processing();
            last_cleaned_  = static_cast<int>(pending_request->scene_id);
            clean_waiting_ = false;
            end            = std::chrono::high_resolution_clock::now();
            duration = static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()) / 1000.0;
            mesh_management_latency_ << "Clean " << pending_request->scene_id << " " << duration << "\n";
            printf("===Device Mesh Manager: Finished Processing VB List for Scene %u===\n", pending_request->scene_id);
            pending_clean_reqs_.pop_back();
        }

        last_processed_ = static_cast<int>(datum->frame_id);

        printf("===Device Mesh Manager: Finished Scene %u===\n", datum->frame_id);
        mesh_processing_ = false;

        std::cout.flush();
        if (datum->frame_id == (frame_count_ / fps_) - 1) {
            printf("Scene Management processed all frames, shutting down...\n");
            mesh_management_latency_.flush();
        }
    }
}

threadloop::skip_option scene_management::_p_should_skip() {
    return skip_option::run;
}

PLUGIN_MAIN(scene_management)