Skip to content

File plugin.cpp

File List > ada > mesh_decompression_grey > plugin.cpp

Go to the documentation of this file

#include "plugin.hpp"

#include "illixr/concurrentqueue/readwritequeue/readerwritercircularbuffer.h"

#include 
#include 
#include 
#include 

using namespace ILLIXR;
using namespace ILLIXR::data_format;

using b_queue = moodycamel::BlockingReaderWriterCircularBuffer<std::shared_ptr<const mesh_type>>;

std::vector<b_queue> queue_;
std::mutex           writer_mutex_;
std::atomic<bool>    done_{false};
std::string          data_path_;

unsigned hash_vb(const ILLIXR::VoxelBlockIndex& index) {
    int x, y, z;
    std::tie(x, y, z) = index;
    auto hash         = (x * 73856093) < (y * 19349669) < (z * 83492791);
    return static_cast<unsigned>(std::abs(hash) % 25600);
}

void decompress(const uint idx, std::shared_ptr<switchboard::writer<draco_type>> writer) {
    std::shared_ptr<const mesh_type> datum;

    std::fstream decoding_latency;

    // pyh: prepare output directory & open latency log
    decoding_latency.open(data_path_ + "/decoding_latency_" + std::to_string(idx) + ".csv", std::ios::out);
    if (!decoding_latency.is_open()) {
        spdlog::get("illixr")->error("Failed to open decompression latency file {}",
                                     data_path_ + "/decoding_latency_" + std::to_string(idx) + ".csv");
    }
    while (true) {
        if (queue_[idx].wait_dequeue_timed(datum, std::chrono::milliseconds(2))) {
            auto start = std::chrono::high_resolution_clock::now();

            draco_illixr::DecoderBuffer buffer;
            buffer.Init(datum->mesh.data(), datum->mesh.size());

            draco_illixr::Decoder               decoder;
            std::unique_ptr<draco_illixr::Mesh> dracoMesh;

            auto                                    type_statusor = draco_illixr::Decoder::GetEncodedGeometryType(&buffer);
            const draco_illixr::EncodedGeometryType geom_type     = type_statusor.value();

            if (geom_type == draco_illixr::TRIANGULAR_MESH) {
                auto statusor = decoder.DecodeMeshFromBuffer(&buffer);
                dracoMesh     = std::move(statusor).value();
            }

            auto       decoding_done = std::chrono::high_resolution_clock::now();
            uint       m_type        = datum->type;
            const auto decoding_us   = std::chrono::duration_cast<std::chrono::microseconds>(decoding_done - start).count();
            decoding_latency << "Decode " << datum->id << " " << datum->chunk_id << " " << (decoding_us / 1000.0) << "\n";

            // pyh: formatting the decoded mesh into Live Mesh Format
            std::unordered_map<unsigned, std::vector<NewVB>> AllocateNewVB;
            AllocateNewVB.reserve(256);

            const draco_illixr::PointAttribute* pos_attribute =
                dracoMesh->GetNamedAttribute(draco_illixr::GeometryAttribute::POSITION);

            if (!pos_attribute) {
                spdlog::get("illixr")->error("No position attribute found in the draco_illixr mesh.");
                return;
            }

            // pyh get voxel block info attached to each face (see section 4.2)
            const int vb_id = dracoMesh->GetAttributeIdByMetadataEntry("attribute_name", "_VOXELBLOCK_INFO");
            auto      vb    = dracoMesh->GetAttributeByUniqueId(vb_id);

            spdlog::get("illixr")->info("Decompressing chunk {} with {} faces", datum->chunk_id, dracoMesh->num_faces());
            for (draco_illixr::FaceIndex faceIndex(0); faceIndex < dracoMesh->num_faces(); ++faceIndex) {
                float dracoVertex_v1[3], dracoVertex_v2[3], dracoVertex_v3[3];
                int   vb_index_v1[3];

                auto face = dracoMesh->face(faceIndex).data();
                auto v1   = draco_illixr::PointIndex(face[0].value());
                auto v2   = draco_illixr::PointIndex(face[1].value());
                auto v3   = draco_illixr::PointIndex(face[2].value());

                pos_attribute->GetMappedValue(v1, dracoVertex_v1);
                pos_attribute->GetMappedValue(v2, dracoVertex_v2);
                pos_attribute->GetMappedValue(v3, dracoVertex_v3);

                vb->GetMappedValue(v1, vb_index_v1);

                VoxelBlockIndex vb_index{vb_index_v1[0], vb_index_v1[1], vb_index_v1[2]};
                unsigned        hash_idx = hash_vb(vb_index);

                Eigen::Vector3d vertex1(dracoVertex_v1[0], dracoVertex_v1[1], dracoVertex_v1[2]);
                Eigen::Vector3d vertex2(dracoVertex_v2[0], dracoVertex_v2[1], dracoVertex_v2[2]);
                Eigen::Vector3d vertex3(dracoVertex_v3[0], dracoVertex_v3[1], dracoVertex_v3[2]);

                auto& bucketlist = AllocateNewVB[hash_idx];

                bool appended = false;
                for (auto& vb_entry : bucketlist) {
                    if (std::get<0>(vb_entry) == vb_index) {
                        // pyh find existing entry and append vertices
                        std::get<1>(vb_entry).push_back(vertex1);
                        std::get<1>(vb_entry).push_back(vertex2);
                        std::get<1>(vb_entry).push_back(vertex3);
                        appended = true;
                        break;
                    }
                }
                if (!appended) {
                    // pyh means 2 scenarios:
                    // 1. there is a hash collision 2. nothing has been allocated for this VB
                    // either case we need to create a new VB entry
                    std::vector<Eigen::Vector3d> new_vertices;
                    std::vector<Eigen::Vector3d> new_colors; // pyh kept for color case
                    // 8x8x8 vb x 3 faces (avg 2.8 faces in MC possibilities)  * 3 point each
                    new_vertices.reserve(4608);

                    new_vertices.push_back(vertex1);
                    new_vertices.push_back(vertex2);
                    new_vertices.push_back(vertex3);

                    bucketlist.emplace_back(vb_index, std::move(new_vertices), std::move(new_colors));
                }
            }
            {
                std::lock_guard<std::mutex> lock(writer_mutex_);

                writer->put(writer->allocate<draco_type>(draco_type{datum->id, datum->chunk_id, std::move(AllocateNewVB)}));
            }
            auto end       = std::chrono::high_resolution_clock::now();
            auto pvbgen_us = std::chrono::duration_cast<std::chrono::microseconds>(end - decoding_done).count();
            decoding_latency << "PVBGen " << datum->id << " " << (pvbgen_us / 1000.0) << "\n";
            decoding_latency.flush();
        }
        if (done_) {
            break;
        }
    }
}

[[maybe_unused]] mesh_decompression::mesh_decompression(const std::string& name_, ILLIXR::phonebook* pb_)
    : plugin{name_, pb_}
    , switchboard_{phonebook_->lookup_impl<switchboard>()}
    , decoded_mesh_{
          std::make_shared<switchboard::writer<draco_type>>(switchboard_->get_writer<draco_type>("decoded_inactive_scene"))} {
    draco_illixr::FileWriterFactory::RegisterWriter(draco_illixr::StdioFileWriter::Open);

    data_path_ = std::filesystem::current_path().string() + "/recorded_data";
    if (!std::filesystem::exists(data_path_)) {
        if (!std::filesystem::create_directories(data_path_)) {
            spdlog::get("illixr")->error("Failed to create data directory.");
        }
    }
    spdlog::get("illixr")->debug("[md] {}", data_path_);
    mesh_count_ = switchboard_->get_env_ulong("MESH_DECOMPRESS_PARALLELISM", 8);

    for (uint i = 0; i < mesh_count_; i++) {
        queue_.push_back(b_queue(8));
        decompress_thread_.push_back(std::thread(decompress, i, decoded_mesh_));
    }
    switchboard_->schedule<mesh_type>(id_, "compressed_scene", [&](switchboard::ptr<const mesh_type> datum, std::size_t) {
        this->process_frame(datum);
    });
}

void mesh_decompression::process_frame(switchboard::ptr<const mesh_type> datum) {
    while (!queue_[datum->type].try_enqueue(datum)) { }
}

mesh_decompression::~mesh_decompression() {
    {
        std::lock_guard<std::mutex> lock(writer_mutex_);
        done_ = true;
    }
    for (auto& t : decompress_thread_) {
        t.join();
    }
}

PLUGIN_MAIN(mesh_decompression)