Program Listing for File CUDAMacroEnvironment.h

Return to documentation for file (include/flamegpu/simulation/detail/CUDAMacroEnvironment.h)

#ifndef INCLUDE_FLAMEGPU_SIMULATION_DETAIL_CUDAMACROENVIRONMENT_H_
#define INCLUDE_FLAMEGPU_SIMULATION_DETAIL_CUDAMACROENVIRONMENT_H_

#include <cuda_runtime.h>

#include <map>
#include <utility>
#include <string>
#include <typeindex>
#include <array>
#include <vector>
#include <memory>

#include "flamegpu/simulation/detail/CUDAErrorChecking.cuh"
#include "flamegpu/runtime/detail/curve/HostCurve.cuh"
#include "flamegpu/runtime/environment/HostMacroProperty.cuh"
#include "flamegpu/detail/cuda.cuh"

// forward declare classes from other modules

namespace flamegpu {
struct SubEnvironmentData;
struct AgentFunctionData;
struct EnvironmentData;
class CUDASimulation;
namespace detail {
namespace curve {
class CurveRTCHost;
}  // namespace curve


class CUDAMacroEnvironment {
 public:
    struct MacroEnvProp {
        MacroEnvProp(const std::type_index& _type, const size_t _type_size, const std::array<unsigned int, 4> &_elements)
            : type(_type)
            , type_size(_type_size)
            , elements(_elements)
            , d_ptr(nullptr)
            , is_sub(false) { }
        ~MacroEnvProp() {
            if (d_ptr && !is_sub) {
                gpuErrchk(flamegpu::detail::cuda::cudaFree(d_ptr));
            }
        }
        MacroEnvProp(const MacroEnvProp& other) = delete;
        MacroEnvProp(MacroEnvProp&& other)
            : type(other.type)
            , type_size(other.type_size)
            , elements(other.elements)
            , d_ptr(other.d_ptr)
            , is_sub(other.is_sub) {
            other.d_ptr = nullptr;
        }
        std::type_index type;
        size_t type_size;
        std::array<unsigned int, 4> elements;
        void *d_ptr;
        // Denotes whether d_ptr is owned by this struct or not
        bool is_sub;
        // ptrdiff_t rtc_offset;  // This is set by buildRTCOffsets();
    };

    CUDAMacroEnvironment(const EnvironmentData& description, const CUDASimulation& _cudaSimulation);
    CUDAMacroEnvironment(CUDAMacroEnvironment&) = delete;
    CUDAMacroEnvironment(CUDAMacroEnvironment&&) = delete;
    void init(cudaStream_t stream);
    void init(const SubEnvironmentData& mapping, std::shared_ptr<const detail::CUDAMacroEnvironment> master_macro_env, cudaStream_t stream);
    void free();
    void mapRTCVariables(detail::curve::CurveRTCHost& curve_header) const;
    void unmapRTCVariables(detail::curve::CurveRTCHost& curve_header) const;
    void registerCurveVariables(detail::curve::HostCurve &curve) const;

#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
    void resetFlagsAsync(const std::vector<cudaStream_t>& streams);
    bool getDeviceReadFlag(const std::string& property_name);
    bool getDeviceWriteFlag(const std::string& property_name);
    unsigned int getDeviceRWFlags(const std::string& property_name);
#endif
    template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
    HostMacroProperty<T, I, J, K, W> getProperty(const std::string& name);
#ifdef SWIG
    template<typename T>
    HostMacroProperty_swig<T> getProperty_swig(const std::string& name);
#endif
    const std::map<std::string, MacroEnvProp>& getPropertiesMap() const;
    std::shared_ptr<HostMacroProperty_MetaData> getHostPropertyMetadata(const std::string property_name);

 private:
    const CUDASimulation& cudaSimulation;
    std::map<std::string, MacroEnvProp> properties;
    std::map<std::string, std::weak_ptr<HostMacroProperty_MetaData>> host_cache;
    cudaStream_t stream = nullptr;
};

template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W> CUDAMacroEnvironment::getProperty(const std::string& name) {
    // Validation
    auto prop = properties.find(name);
    if (prop == properties.end()) {
        THROW flamegpu::exception::InvalidEnvProperty("Environment macro property with name '%s' not found, "
            "in HostEnvironment::getMacroProperty()\n",
            name.c_str());
    } else if (prop->second.type != std::type_index(typeid(T))) {
        THROW flamegpu::exception::InvalidEnvProperty("Environment macro property '%s' type mismatch '%s' != '%s', "
            "in HostEnvironment::getMacroProperty()\n",
            name.c_str(), std::type_index(typeid(T)).name(), prop->second.type.name());
    } else if (prop->second.elements != std::array<unsigned int, 4>{I, J, K, W}) {
        THROW flamegpu::exception::InvalidEnvProperty("Environment macro property '%s' dimensions mismatch (%u, %u, %u, %u) != (%u, %u, %u, %u), "
            "in HostEnvironment::getMacroProperty()\n",
            name.c_str(), I, J, K, W, prop->second.elements[0], prop->second.elements[1], prop->second.elements[2], prop->second.elements[3]);
    }
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
    const unsigned int flags = getDeviceWriteFlag(name);
    if (flags & (1 << 1)) {
        THROW flamegpu::exception::InvalidOperation("Environment macro property '%s' was written to by an agent function in the same layer, "
            "accessing it with a host function in the same layer could cause a race condition, in CUDAMacroEnvironment::getProperty().",
            name.c_str());
    }
    const bool read_flag = flags & (1 << 0);
#else
    const bool read_flag = false;
#endif
    // See if there is a live metadata in cache
    auto cache = host_cache.find(name);
    if (cache != host_cache.end()) {
        if (cache->second.lock()) {
            return HostMacroProperty<T, I, J, K, W>(cache->second.lock());
        }
        host_cache.erase(cache);
    }
    auto ret = std::make_shared<HostMacroProperty_MetaData>(prop->second.d_ptr, prop->second.elements, sizeof(T), read_flag, name, stream);
    host_cache.emplace(name, ret);
    return HostMacroProperty<T, I, J, K, W>(ret);
}

#ifdef SWIG
template<typename T>
HostMacroProperty_swig<T> CUDAMacroEnvironment::getProperty_swig(const std::string& name) {
    // Validation
    auto prop = properties.find(name);
    if (prop == properties.end()) {
        THROW flamegpu::exception::InvalidEnvProperty("Environment macro property with name '%s' not found, "
            "in HostEnvironment::getMacroProperty()\n",
            name.c_str());
    } else if (prop->second.type != std::type_index(typeid(T))) {
        THROW flamegpu::exception::InvalidEnvProperty("Environment macro property '%s' type mismatch '%s' != '%s', "
            "in HostEnvironment::getMacroProperty()\n",
            name.c_str(), std::type_index(typeid(T)).name(), prop->second.type.name());
    }
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
    const unsigned int flags = getDeviceWriteFlag(name);
    if (flags & (1 << 1)) {
        THROW flamegpu::exception::InvalidOperation("Environment macro property '%s' was written to by an agent function in the same layer, "
            "accessing it with a host function in the same layer could cause a race condition, in CUDAMacroEnvironment::getProperty().",
            name.c_str());
    }
    const bool read_flag = flags & (1 << 0);
#else
    const bool read_flag = false;
#endif
    // See if there is a live metadata in cache
    auto cache = host_cache.find(name);
    if (cache != host_cache.end()) {
        if (cache->second.lock()) {
            return HostMacroProperty_swig<T>(cache->second.lock());
        }
        host_cache.erase(cache);
    }
    auto ret = std::make_shared<HostMacroProperty_MetaData>(prop->second.d_ptr, prop->second.elements, sizeof(T), read_flag, name, stream);
    host_cache.emplace(name, ret);
    return HostMacroProperty_swig<T>(ret);
}
#endif

}  // namespace detail
}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_SIMULATION_DETAIL_CUDAMACROENVIRONMENT_H_