Program Listing for File EnvironmentManager.cuh

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

#ifndef INCLUDE_FLAMEGPU_SIMULATION_DETAIL_ENVIRONMENTMANAGER_CUH_
#define INCLUDE_FLAMEGPU_SIMULATION_DETAIL_ENVIRONMENTMANAGER_CUH_

#include <cuda_runtime.h>

#include <map>
#include <string>
#include <memory>
#include <set>
#include <unordered_map>
#include <functional>
#include <utility>
#include <vector>

#include "flamegpu/defines.h"
#include "flamegpu/exception/FLAMEGPUException.h"
#include "flamegpu/runtime/detail/curve/HostCurve.cuh"
#include "flamegpu/detail/type_decode.h"
#include "flamegpu/detail/Any.h"

namespace flamegpu {
struct SubEnvironmentData;
struct EnvironmentData;
class CUDASimulation;
namespace detail {

class EnvironmentManager : public std::enable_shared_from_this<EnvironmentManager> {
    friend class flamegpu::CUDASimulation;

 private:
    struct EnvProp {
        EnvProp(const ptrdiff_t& _offset, const size_t _length, const bool _isConst, const size_type _elements, const std::type_index& _type)
            : offset(_offset),
            length(_length),
            isConst(_isConst),
            elements(_elements),
            type(_type) {}
        ptrdiff_t offset;
        size_t length;
        bool isConst;
        size_type elements;
        const std::type_index type;
    };
    struct MappedProp {
        MappedProp(const std::string& _remoteName, const std::shared_ptr<EnvironmentManager> &_remoteEnv, bool _isConst = false)
            : remoteName(_remoteName)
            , remoteEnv(_remoteEnv)
            , isConst(_isConst) {}
        const std::string remoteName;
        const std::weak_ptr<EnvironmentManager> remoteEnv;
        const bool isConst;
    };
    struct DefragProp {
        DefragProp(void *_data, const size_t _length, bool _isConst, const size_type _elements, const std::type_index &_type)
            : data(_data),
            length(_length),
            isConst(_isConst),
            elements(_elements),
            type(_type),
            offset(0) { }
        void *data;
        size_t length;
        bool isConst;
        size_type elements;
        const std::type_index type;
        ptrdiff_t offset;
    };
    friend bool operator<(const std::pair<size_t, std::string>& fk, const size_t lk) { return fk.first < lk; }
    friend bool operator<(const size_t lk, const std::pair<size_t, std::string>& fk) { return lk < fk.first; }
    friend bool operator<(const std::pair<size_t, std::string>& fk1, const std::pair<size_t, std::string>& fk2) {
        if (fk1.first == fk2.first) {
            // If size is equal, order by name
            return fk1.second < fk2.second;
        }
        return fk1.first < fk2.first;
    }
    typedef std::multimap<std::pair<size_t, std::string>, DefragProp, std::less<>> DefragMap;

 public:
     [[nodiscard]] static std::shared_ptr<EnvironmentManager> create(const EnvironmentData& desc) {
        std::shared_ptr<EnvironmentManager> rtn(new EnvironmentManager());  // Can't use make_shared with private constructor!
        rtn->init(desc);
        return rtn;
    }
     [[nodiscard]] static std::shared_ptr<EnvironmentManager> create(const EnvironmentData& desc, const std::shared_ptr<EnvironmentManager>& parent_environment, const SubEnvironmentData& mapping) {
        std::shared_ptr<EnvironmentManager> rtn(new EnvironmentManager());  // Can't use make_shared with private constructor!
        rtn->init(desc, parent_environment, mapping);
        return rtn;
    }
    ~EnvironmentManager();
    EnvironmentManager(EnvironmentManager const&) = delete;
    void operator=(EnvironmentManager const&) = delete;
    template<typename T>
    T setProperty(const std::string& name, T value);
    template<typename T, size_type N>
    std::array<T, N> setProperty(const std::string& name, const std::array<T, N>& value);
    template<typename T, size_type N = 0>
    T setProperty(const std::string& name,  size_type index, T value);
#ifdef SWIG
    template<typename T>
    std::vector<T> setPropertyArray(const std::string& name, const std::vector<T>& value);
#endif
    template<typename T>
    T getProperty(const std::string& name);
    template<typename T, size_type N>
    std::array<T, N> getProperty(const std::string& name);
    template<typename T, size_type N = 0>
    T getProperty(const std::string& name, size_type index);
#ifdef SWIG
    template<typename T>
    std::vector<T> getPropertyArray(const std::string& name);
#endif
    void resetModel(const EnvironmentData& desc);
    void updateDevice_async(cudaStream_t stream) const;
    size_t getBufferLen() const { return h_buffer_len; }
    const void* getHostBuffer() const {
        return h_buffer;
    }
    const void* getDeviceBuffer() const {
        return d_buffer;
    }
    const std::unordered_map<std::string, EnvProp> &getPropertiesMap() const {
        return properties;
    }

 private:
    void init(const EnvironmentData& desc);
    void init(const EnvironmentData& desc, const std::shared_ptr<EnvironmentManager>& parent_environment, const SubEnvironmentData& mapping);
    void linkMappedProperty(const std::string &parent_name, const std::string& sub_name, const std::shared_ptr<EnvironmentManager>& sub_environment);
    void propagateMappedPropertyValue(const std::string &property_name, const char *src_ptr);
    void setPropertyDirect(const std::string& property_name, const char * src_ptr);
    template<typename T>
    const EnvProp &findProperty(const std::string& property_name, bool setter, size_type length) const;
    detail::Any getPropertyAny(const std::string &property_name) const;
    char *h_buffer = nullptr;
    char *d_buffer = nullptr;
    mutable bool d_buffer_ready = false;
    size_t h_buffer_len = 0;
    std::unordered_map<std::string, EnvProp> properties{};
    std::multimap<std::string, MappedProp> mapped_child_properties{};
    std::map<std::string, MappedProp> mapped_parent_properties{};
    EnvironmentManager() = default;
};

template<typename T>
const EnvironmentManager::EnvProp& EnvironmentManager::findProperty(const std::string& property_name, const bool setter, const size_type length) const {
    // Limited to Arithmetic types
    // Compound types would allow host pointers inside structs to be passed
    static_assert(std::is_arithmetic<typename detail::type_decode<T>::type_t>::value || std::is_enum<typename detail::type_decode<T>::type_t>::value || std::is_void<typename detail::type_decode<T>::type_t>::value,
        "Only arithmetic types can be used as environmental properties");
    const auto a = properties.find(property_name);
    if (a != properties.end()) {
        if (std::type_index(typeid(T)) != std::type_index(typeid(void)) && a->second.type != std::type_index(typeid(typename detail::type_decode<T>::type_t))) {
            THROW exception::InvalidEnvPropertyType("Environmental property with name '%s', type (%s) does not match template argument T (%s), "
                "in EnvironmentManager::setProperty().",
                property_name.c_str(), a->second.type.name(), typeid(typename detail::type_decode<T>::type_t).name());
        } else if (length && a->second.elements != detail::type_decode<T>::len_t * length) {
            THROW exception::OutOfBoundsException("Environmental property with name '%s', base length (%u) does not match provided base length (%u), "
                "in EnvironmentManager::setProperty().",
                property_name.c_str(), a->second.elements, detail::type_decode<T>::len_t * length);
        } else if (setter && a->second.isConst) {
            THROW exception::ReadOnlyEnvProperty("Environmental property with name '%s' is marked as const and cannot be changed, "
                "in EnvironmentManager::setProperty().",
                property_name.c_str());
        }
        // Check this here, rather than in 4 separate methods
        if (setter)
            d_buffer_ready = false;
        return a->second;
    }
    THROW exception::InvalidEnvProperty("Environmental property with name '%s' does not exist, "
        "in EnvironmentManager::find().",
        property_name.c_str());
}
template<typename T>
T EnvironmentManager::setProperty(const std::string &name, const T value) {
    const EnvProp &prop = findProperty<T>(name, true, 1);
    // Copy old data to return
    T rtn;
    char* const dest_ptr = h_buffer + prop.offset;
    memcpy(&rtn, dest_ptr, sizeof(T));
    // Store data
    memcpy(dest_ptr, &value, sizeof(T));
    // Notify children
    propagateMappedPropertyValue(name, dest_ptr);
    return rtn;
}
template<typename T, flamegpu::size_type N>
std::array<T, N> EnvironmentManager::setProperty(const std::string &name, const std::array<T, N> &value) {
    const EnvProp& prop = findProperty<T>(name, true, N);
    // Copy old data to return
    std::array<T, N> rtn;
    char* const dest_ptr = h_buffer + prop.offset;
    memcpy(rtn.data(), dest_ptr, sizeof(T) * N);
    // Store data
    memcpy(dest_ptr, value.data(), sizeof(T) * N);
    // Notify children
    propagateMappedPropertyValue(name, dest_ptr);
    return rtn;
}
template<typename T, flamegpu::size_type N>
T EnvironmentManager::setProperty(const std::string &name, const size_type index, const T value) {
    const EnvProp& prop = findProperty<T>(name, true, 0);
    if (N && N != prop.elements / detail::type_decode<T>::len_t) {
        THROW exception::OutOfBoundsException("Environmental property with name '%s', array length mismatch (%u != %u), "
            "in EnvironmentManager::setProperty().",
            name.c_str(), N, prop.elements / detail::type_decode<T>::len_t);
    } else if (index >= prop.elements / detail::type_decode<T>::len_t) {
        THROW exception::OutOfBoundsException("Environmental property with name '%s', index (%u) exceeds named environmental property array's length (%u), "
            "in EnvironmentManager::setProperty().",
            name.c_str(), index, prop.elements / detail::type_decode<T>::len_t);
    }
    // Copy old data to return
    T rtn;
    char* const dest_ptr = h_buffer + prop.offset;
    memcpy(&rtn, dest_ptr + index * sizeof(T), sizeof(T));
    // Store data
    memcpy(dest_ptr + index * sizeof(T), &value, sizeof(T));
    // Notify children
    propagateMappedPropertyValue(name, dest_ptr);
    return rtn;
}
#ifdef SWIG
template<typename T>
std::vector<T> EnvironmentManager::setPropertyArray(const std::string& name, const std::vector<T>& value) {
    const EnvProp& prop = findProperty<T>(name, true, static_cast<unsigned int>(value.size()));
    // Copy old data to return
    std::vector<T> rtn;
    rtn.resize(value.size());
    char* const dest_ptr = h_buffer + prop.offset;
    memcpy(rtn.data(), dest_ptr, sizeof(T) * value.size());
    // Store data
    memcpy(dest_ptr, value.data(), sizeof(T) * value.size());
    // Notify children
    propagateMappedPropertyValue(name, dest_ptr);
    return rtn;
}
#endif
template<typename T>
T EnvironmentManager::getProperty(const std::string &name) {
    const EnvProp& prop = findProperty<T>(name, false, 1);
    // Copy data to return
    T rtn;
    memcpy(&rtn, h_buffer + prop.offset, sizeof(T));
    return rtn;
}
template<typename T, flamegpu::size_type N>
std::array<T, N> EnvironmentManager::getProperty(const std::string &name) {
    const EnvProp& prop = findProperty<T>(name, false, N);
    // Copy old data to return
    std::array<T, N> rtn;
    memcpy(rtn.data(), h_buffer + prop.offset, sizeof(T) * N);
    return rtn;
}
template<typename T, flamegpu::size_type N>
T EnvironmentManager::getProperty(const std::string &name, const size_type index) {
    const EnvProp& prop = findProperty<T>(name, false, 0);
    if (N && N != prop.elements / detail::type_decode<T>::len_t) {
        THROW exception::OutOfBoundsException("Environmental property with name '%s', array length mismatch (%u != %u), "
            "in EnvironmentManager::getProperty().",
            name.c_str(), N, prop.elements / detail::type_decode<T>::len_t);
    } else if (index >= prop.elements / detail::type_decode<T>::len_t) {
        THROW exception::OutOfBoundsException("Environmental property with name '%s', index (%u) exceeds named environmental property array's length (%u), "
            "in EnvironmentManager::getProperty().",
            name.c_str(), index, prop.elements / detail::type_decode<T>::len_t);
    }
    // Copy old data to return
    T rtn;
    memcpy(&rtn, h_buffer + prop.offset + index * sizeof(T), sizeof(T));
    return rtn;
}
#ifdef SWIG
template<typename T>
std::vector<T> EnvironmentManager::getPropertyArray(const std::string &name) {
    const EnvProp& prop = findProperty<T>(name, false, 0);
    // Copy old data to return
    const unsigned int N = prop.elements / detail::type_decode<T>::len_t;
    std::vector<T> rtn;
    rtn.resize(N);
    memcpy(rtn.data(), h_buffer + prop.offset, sizeof(T) * N);
    return rtn;
}
#endif

}  // namespace detail
}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_SIMULATION_DETAIL_ENVIRONMENTMANAGER_CUH_