Program Listing for File CUDAEnvironmentDirectedGraphBuffers.cuh

Return to documentation for file (include/flamegpu/simulation/detail/CUDAEnvironmentDirectedGraphBuffers.cuh)

#ifndef INCLUDE_FLAMEGPU_SIMULATION_DETAIL_CUDAENVIRONMENTDIRECTEDGRAPHBUFFERS_CUH_
#define INCLUDE_FLAMEGPU_SIMULATION_DETAIL_CUDAENVIRONMENTDIRECTEDGRAPHBUFFERS_CUH_

#include <string>
#include <map>
#include <list>
#include <memory>
#include <vector>
#include <unordered_map>
#include <utility>
#include <limits>

#include "flamegpu/model/EnvironmentDirectedGraphData.cuh"
#include "flamegpu/defines.h"
#include "flamegpu/simulation/detail/CUDAErrorChecking.cuh"
#include "flamegpu/detail/type_decode.h"
#include "flamegpu/util/StringPair.h"
namespace flamegpu {
#ifdef FLAMEGPU_VISUALISATION
namespace visualiser {
struct ModelVisData;
}
#endif
namespace detail {
class CUDAScatter;
namespace curve {
class HostCurve;
class CurveRTCHost;
}
class CUDAEnvironmentDirectedGraphBuffers {
    struct Buffer {
        enum Ready { None = 0, Host = 1, Device = 2, Both = 3};
        size_t element_size;
        void *d_ptr = nullptr;
        void* d_ptr_swap = nullptr;
        void *h_ptr = nullptr;
        mutable Ready ready = None;
        void swap() { std::swap(d_ptr, d_ptr_swap); }
        void updateHostBuffer(size_type edge_count, cudaStream_t stream) const;
    };
    const EnvironmentDirectedGraphData &graph_description;
    std::map<std::string, Buffer> vertex_buffers;
    std::map<std::string, Buffer> edge_buffers;
    std::list<std::weak_ptr<detail::curve::HostCurve>> curve_instances;
    std::list<std::weak_ptr<detail::curve::CurveRTCHost>> rtc_curve_instances;
#ifdef FLAMEGPU_VISUALISATION
    mutable std::weak_ptr<visualiser::ModelVisData> visualisation;
#endif
    size_type vertex_count;
    size_type edge_count;
    bool requires_rebuild;
    uint64_t *d_keys = nullptr, *d_keys_swap = nullptr;
    uint32_t *d_vals = nullptr, *d_vals_swap = nullptr;
    // CSR/VBM (edgesLeaving())
    unsigned int *d_pbm = nullptr, *d_pbm_swap = nullptr;
    // CSC/invertedVBM (edgesJoining()), shares d_pbm_swap
    unsigned int *d_ipbm = nullptr;
    // Copy of the vals list from constructing ipbm, required to lookup edges
    unsigned int *d_ipbm_edges = nullptr;
    // Vertex ID -> index map, ?? has been reserved, otherwise any ID is valid
    // However, the ID->index map does not utilise any compression, so non-contiguous ID ranges may lead to out of memory errors.
    unsigned int *d_vertex_index_map = nullptr;

    void allocateVertexBuffers(size_type count, cudaStream_t stream);
    void allocateEdgeBuffers(size_type count);
    void deallocateVertexBuffers();
    void deallocateEdgeBuffers();
    /*
     * Reset the internal vertex ID range tracking variables as though no vertices have been assigned IDs
     */
    void resetVertexIDBounds();
    unsigned int vertex_id_min = std::numeric_limits<unsigned int>::max();
    unsigned int vertex_id_max = std::numeric_limits<unsigned int>::min();
    std::map<id_t, unsigned int> h_vertex_index_map;
    util::PairMap<id_t, id_t, unsigned int> h_edge_index_map;

 public:
    explicit CUDAEnvironmentDirectedGraphBuffers(const EnvironmentDirectedGraphData &description);
    ~CUDAEnvironmentDirectedGraphBuffers();
    void registerCurveInstance(const std::shared_ptr<detail::curve::HostCurve>& curve);
    void registerCurveInstance(const std::shared_ptr<detail::curve::CurveRTCHost>& curve);
    const EnvironmentDirectedGraphData& getDescription() const { return graph_description; }
    void setVertexCount(size_type count, cudaStream_t stream);
    void setEdgeCount(size_type count);
    size_type getVertexCount() const { return vertex_count; }
    size_type getEdgeCount() const { return edge_count; }
    unsigned int createIfNotExistVertex(id_t vertex_id, cudaStream_t stream);
    unsigned int createIfNotExistEdge(id_t source_vertex_id, id_t dest_vertex_id, cudaStream_t stream);
    id_t* getVertexIDBuffer(cudaStream_t stream);
    template<typename T>
    const T* getVertexPropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream) const;
    template<typename T>
    T* getVertexPropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream);
    template<typename T>
    const T *getEdgePropertyBuffer(const std::string &property_name, size_type &N, cudaStream_t stream) const;
    template<typename T>
    T* getEdgePropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream);
#ifdef SWIG
    template<typename T>
    std::vector<T> getVertexPropertyArray(const std::string& property_name, const unsigned int vertex_index, cudaStream_t stream) const;
    template<typename T>
    std::vector<T> getEdgePropertyArray(const std::string& property_name, const unsigned int edge_index, cudaStream_t stream) const;
#endif
    void markForRebuild() { requires_rebuild = true; }
    void syncDevice_async(detail::CUDAScatter& scatter, unsigned int streamID, cudaStream_t stream);
    void setVertexID(unsigned int vertex_index, id_t vertex_id, cudaStream_t stream);
    unsigned int getVertexIndex(id_t vertex_id) const;
    void setEdgeSourceDestination(unsigned int edge_index, id_t src_vertex_id, id_t dest_vertex_id);
    unsigned int getEdgeIndex(id_t src_vertex_id, id_t dest_vertex_id) const;
#ifdef FLAMEGPU_VISUALISATION
    void setVisualisation(std::shared_ptr<visualiser::ModelVisData> &_visualisation) const {
        this->visualisation = _visualisation;
    }
#endif
};


template<typename T>
const T* CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream) const {
    if (!vertex_count) {
        THROW exception::OutOfBoundsException("Vertex buffers not yet allocated, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()");
    }
    const auto &vv = graph_description.vertexProperties.find(property_name);
    if (vv == graph_description.vertexProperties.end()) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' not found, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()", property_name.c_str());
    } else if (vv->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' type mismatch '%s' != '%s', in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()",
        property_name.c_str(), vv->second.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    } else if (N == 0) {
        N = vv->second.elements;
    } else if (vv->second.elements != N) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' length mismatch '%u' != '%u', in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()",
            property_name.c_str(), N, vv->second.elements);
    }
    auto& vb = vertex_buffers.at(property_name);
    vb.updateHostBuffer(vertex_count, stream);
    return static_cast<T*>(vb.h_ptr);
}
template<typename T>
T* CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream) {
    if (!vertex_count) {
        THROW exception::OutOfBoundsException("Vertex buffers not yet allocated, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()");
    }
    const auto& vv = graph_description.vertexProperties.find(property_name);
    if (vv == graph_description.vertexProperties.end()) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' not found, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()", property_name.c_str());
    } else if (vv->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' type mismatch '%s' != '%s', in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()",
            property_name.c_str(), vv->second.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    } else if (N == 0) {
        N = vv->second.elements;
    } else if (vv->second.elements != N) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' length mismatch '%u' != '%u', in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyBuffer()",
            property_name.c_str(), N, vv->second.elements);
    }
    auto &vb = vertex_buffers.at(property_name);
    vb.updateHostBuffer(vertex_count, stream);
    vb.ready = Buffer::Host;
    return static_cast<T*>(vb.h_ptr);
}
template<typename T>
const T* CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream) const {
    if (!edge_count) {
        THROW exception::OutOfBoundsException("Edge buffers not yet allocated, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()");
    }
    const auto& ev = graph_description.edgeProperties.find(property_name);
    if (ev == graph_description.edgeProperties.end()) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' not found, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()", property_name.c_str());
    } else if (ev->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' type mismatch '%s' != '%s', in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()",
            property_name.c_str(), ev->second.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    } else if (N == 0) {
        N = ev->second.elements;
    } else if (ev->second.elements != N) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' length mismatch '%u' != '%u', in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()",
            property_name.c_str(), N, ev->second.elements);
    }
    auto &eb = edge_buffers.at(property_name);
    eb.updateHostBuffer(edge_count, stream);
    return static_cast<T*>(eb.h_ptr);
}
template<typename T>
T* CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer(const std::string& property_name, size_type &N, cudaStream_t stream) {
    if (!edge_count) {
        THROW exception::OutOfBoundsException("Edge buffers not yet allocated, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()");
    }
    const auto& ev = graph_description.edgeProperties.find(property_name);
    if (ev == graph_description.edgeProperties.end()) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' not found, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()", property_name.c_str());
    } else if (ev->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' type mismatch '%s' != '%s', in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()",
            property_name.c_str(), ev->second.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    } else if (N == 0) {
        N = ev->second.elements;
    } else if (ev->second.elements != N) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' length mismatch '%u' != '%u', in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyBuffer()",
            property_name.c_str(), N, ev->second.elements);
    }
    auto &eb = edge_buffers.at(property_name);
    eb.updateHostBuffer(edge_count, stream);
    eb.ready = Buffer::Host;
    return static_cast<T*>(eb.h_ptr);
}
#ifdef SWIG
template<typename T>
std::vector <T> CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyArray(const std::string& property_name, const unsigned int vertex_index, cudaStream_t stream) const {
    if (!vertex_count) {
        THROW exception::OutOfBoundsException("Vertex buffers not yet allocated, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyArray()");
    }
    const auto& vv = graph_description.vertexProperties.find(property_name);
    if (vv == graph_description.vertexProperties.end()) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' not found, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyArray()", property_name.c_str());
    } else if (vv->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidGraphProperty("Vertex property with name '%s' type mismatch '%s' != '%s', in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyArray()",
            property_name.c_str(), vv->second.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    } else if (vertex_index >= vertex_count) {
        THROW exception::OutOfBoundsException("Vertex index %u is out of range %u, in CUDAEnvironmentDirectedGraphBuffers::getVertexPropertyArray()\n",
            vertex_index, vertex_count);
    }
    std::vector<T> rtn(vv->second.elements);
    auto& vb = vertex_buffers.at(property_name);
    vb.updateHostBuffer(vertex_count, stream);
    memcpy(rtn.data(), static_cast<T*>(vb.h_ptr) + vertex_index * vv->second.elements, vv->second.type_size * vv->second.elements);
    return rtn;
}
template<typename T>
std::vector<T> CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyArray(const std::string& property_name, const unsigned int edge_index, cudaStream_t stream) const {
    if (!edge_count) {
        THROW exception::OutOfBoundsException("Edge buffers not yet allocated, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyArray()");
    }
    const auto& ev = graph_description.edgeProperties.find(property_name);
    if (ev == graph_description.edgeProperties.end()) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' not found, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyArray()", property_name.c_str());
    }  else if (ev->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
        THROW exception::InvalidGraphProperty("Edge property with name '%s' type mismatch '%s' != '%s', in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyArray()",
            property_name.c_str(), ev->second.type.name(), std::type_index(typeid(typename detail::type_decode<T>::type_t)).name());
    } else if (edge_index >= edge_count) {
        THROW exception::OutOfBoundsException("Edge index %u is out of range %u, in CUDAEnvironmentDirectedGraphBuffers::getEdgePropertyArray()\n",
            edge_index, edge_count);
    }
    std::vector<T> rtn(ev->second.elements);
    auto& eb = edge_buffers.at(property_name);
    eb.updateHostBuffer(edge_count, stream);
    memcpy(rtn.data(), static_cast<T*>(eb.h_ptr) + edge_index * ev->second.elements, ev->second.type_size * ev->second.elements);
    return rtn;
}
#endif

}  // namespace detail
}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_SIMULATION_DETAIL_CUDAENVIRONMENTDIRECTEDGRAPHBUFFERS_CUH_