Program Listing for File HostMacroProperty.cuh

Return to documentation for file (include/flamegpu/runtime/environment/HostMacroProperty.cuh)

#ifndef INCLUDE_FLAMEGPU_RUNTIME_ENVIRONMENT_HOSTMACROPROPERTY_CUH_
#define INCLUDE_FLAMEGPU_RUNTIME_ENVIRONMENT_HOSTMACROPROPERTY_CUH_

#include <cstdint>
#include <algorithm>
#include <array>
#include <memory>
#include <cmath>
#include <string>

namespace flamegpu {

struct HostMacroProperty_MetaData {
    HostMacroProperty_MetaData(void* _d_base_ptr, const std::array<unsigned int, 4>& _dims, size_t _type_size,
        bool _device_read_flag, const std::string &name, cudaStream_t _stream)
        : h_base_ptr(nullptr)
        , d_base_ptr(static_cast<char*>(_d_base_ptr))
        , dims(_dims)
        , elements(dims[0] * dims[1] * dims[2] * dims[3])
        , type_size(_type_size)
        , has_changed(false)
        , device_read_flag(_device_read_flag)
        , property_name(name)
        , stream(_stream)
    { }
    ~HostMacroProperty_MetaData() {
        upload();
        if (h_base_ptr)
            std::free(h_base_ptr);
    }
    void download() {
        if (!h_base_ptr) {
            force_download();
        }
    }

    void force_download() {
        if (!h_base_ptr) {
            h_base_ptr = static_cast<char*>(malloc(elements * type_size));
        }
        gpuErrchk(cudaMemcpyAsync(h_base_ptr, d_base_ptr, elements * type_size, cudaMemcpyDeviceToHost, stream));
        gpuErrchk(cudaStreamSynchronize(stream));
        has_changed = false;
    }
    void upload() {
        if (h_base_ptr && has_changed) {
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
            if (device_read_flag) {
                THROW flamegpu::exception::InvalidEnvProperty("The environment macro property '%s' was not found, "
                    "in HostMacroProperty_MetaData::upload()\n",
                    property_name.c_str());
            }
#endif
            gpuErrchk(cudaMemcpyAsync(d_base_ptr, h_base_ptr, elements * type_size, cudaMemcpyHostToDevice, stream));
            gpuErrchk(cudaStreamSynchronize(stream));
            has_changed = false;
        }
    }
    char* h_base_ptr;
    char* d_base_ptr;
    std::array<unsigned int, 4> dims;
    unsigned int elements;
    size_t type_size;
    bool has_changed;
    bool device_read_flag;
    std::string property_name;
    cudaStream_t stream;
};

template<typename T, unsigned int I = 1, unsigned int J = 1, unsigned int K = 1, unsigned int W = 1>
class HostMacroProperty {
    ptrdiff_t offset;
    std::shared_ptr<HostMacroProperty_MetaData> metadata;

 public:
    explicit HostMacroProperty(const std::shared_ptr<HostMacroProperty_MetaData>& _metadata);
    HostMacroProperty(const std::shared_ptr<HostMacroProperty_MetaData> &_metadata, const ptrdiff_t& _offset);

    HostMacroProperty<T, J, K, W, 1> operator[](unsigned int i) const;
    operator T();
    operator T() const;
    HostMacroProperty<T, I, J, K, W>& operator=(T val);
    void zero();

    HostMacroProperty<T, I, J, K, W>& operator++();
    T operator++(int);
    HostMacroProperty<T, I, J, K, W>& operator--();
    T operator--(int);

// Swig breaks if it see's auto return type
#ifndef SWIG
    template<typename T2>
    auto operator+(T2 b) const;
    template<typename T2>
    auto operator-(T2 b) const;
    template<typename T2>
    auto operator*(T2 b) const;
    template<typename T2>
    auto operator/(T2 b) const;
    template<typename T2>
    auto operator%(T2 b) const;
#endif
    // HostMacroProperty<T, I, J, K, W>& operator=(T b);  // Defined above
    template<typename T2>
    HostMacroProperty<T, I, J, K, W>& operator+=(T2 b);
    template<typename T2>
    HostMacroProperty<T, I, J, K, W>& operator-=(T2 b);
    template<typename T2>
    HostMacroProperty<T, I, J, K, W>& operator*=(T2 b);
    template<typename T2>
    HostMacroProperty<T, I, J, K, W>& operator/=(T2 b);
    template<typename T2>
    HostMacroProperty<T, I, J, K, W>& operator%=(T2 b);

 private:
    T& _get();
    T& _get() const;
};

#ifdef SWIG
template<typename T>
class HostMacroProperty_swig {
    const std::array<unsigned int, 4> dimensions;
    ptrdiff_t offset;
    std::shared_ptr<HostMacroProperty_MetaData> metadata;

 public:
    explicit HostMacroProperty_swig(const std::shared_ptr<HostMacroProperty_MetaData> &_metadata);
    HostMacroProperty_swig(const std::shared_ptr<HostMacroProperty_MetaData>& _metadata, const ptrdiff_t& _offset, const std::array<unsigned int, 4>& _dimensions);
    HostMacroProperty_swig<T> __getitem__(unsigned int i) const;
    void __setitem__(unsigned int i,  T val);
    void set(T val);

    int __int__();
    int64_t __long__();
    double __float__();
    bool __bool__();

    bool __eq__(T other) const;
    bool __ne__(T other) const;
    bool __lt__(T other) const;
    bool __le__(T other) const;
    bool __gt__(T other) const;
    bool __ge__(T other) const;

    unsigned int __len__() const { return dimensions[0]; }

    T get() const;
    void zero();
};
#endif

template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>::HostMacroProperty(const std::shared_ptr<HostMacroProperty_MetaData> &_metadata)
: offset(0u)
, metadata(_metadata)
{ }
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>::HostMacroProperty(const std::shared_ptr<HostMacroProperty_MetaData> &_metadata, const ptrdiff_t & _offset)
    : offset(_offset)
    , metadata(_metadata)
{ }
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, J, K, W, 1> HostMacroProperty<T, I, J, K, W>::operator[](unsigned int i) const {
    if (I == 1 && J == 1 && K == 1 && W == 1) {
        THROW exception::InvalidOperation("Indexing error, property has less dimensions.\n");
    } else if (i >= I) {
        THROW exception::OutOfBoundsException("Indexing error, out of bounds %u >= %u.\n", i, I);
    }
    // (i * J * K * W) + (j * K * W) + (k * W) + w
    return HostMacroProperty<T, J, K, W, 1>(metadata, offset + (i * J * K * W));
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>::operator T() {
    if (I != 1 || J != 1 || K != 1 || W != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    // Must assume changed
    metadata->has_changed = true;
    return *(reinterpret_cast<T*>(metadata->h_base_ptr) + offset);
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>::operator T() const {
    metadata->download();
    return *(reinterpret_cast<T*>(metadata->h_base_ptr) + offset);
}

template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
void HostMacroProperty<T, I, J, K, W>::zero() {
    if (metadata->h_base_ptr) {
        // Memset on host
        memset(reinterpret_cast<T*>(metadata->h_base_ptr) + offset, 0, I * J * K * W * metadata->type_size);
        metadata->has_changed = true;
    } else {
#if !defined(FLAMEGPU_SEATBELTS) || FLAMEGPU_SEATBELTS
        if (metadata->device_read_flag) {
            THROW flamegpu::exception::InvalidEnvProperty("The environment macro property '%s' was not found, "
                "in HostMacroProperty::zero()\n",
                metadata->property_name.c_str());
        }
#endif
        // Memset on device
        gpuErrchk(cudaMemset(reinterpret_cast<T*>(metadata->d_base_ptr) + offset, 0, I * J * K * W * metadata->type_size));
    }
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
T& HostMacroProperty<T, I, J, K, W>::_get() const {
    if (I != 1 || J != 1 || K != 1 || W != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset];
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
T& HostMacroProperty<T, I, J, K, W>::_get() {
    if (I != 1 || J != 1 || K != 1 || W != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    metadata->has_changed = true;
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset];
}


template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator++() {
    ++_get();
    return *this;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
T HostMacroProperty<T, I, J, K, W>::operator++(int) {
    T &t = _get();
    T ret = t;
    ++t;
    return ret;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator--() {
    --_get();
    return *this;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
T HostMacroProperty<T, I, J, K, W>::operator--(int) {
    T& t = _get();
    T ret = t;
    --t;
    return ret;
}

#ifndef SWIG
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
auto HostMacroProperty<T, I, J, K, W>::operator+(const T2 b) const {
    return _get() + b;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
auto HostMacroProperty<T, I, J, K, W>::operator-(const T2 b) const {
    return _get() - b;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
auto HostMacroProperty<T, I, J, K, W>::operator*(const T2 b) const {
    return _get() * b;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
auto HostMacroProperty<T, I, J, K, W>::operator/(const T2 b) const {
    return _get() / b;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
auto HostMacroProperty<T, I, J, K, W>::operator%(const T2 b) const {
    return _get() % b;
}
#endif

template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator+=(const T2 b) {
    _get() += b;
    return *this;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator-=(const T2 b) {
    _get() -= b;
    return *this;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator*=(const T2 b) {
    _get() *= b;
    return *this;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator/=(const T2 b) {
    _get() /= b;
    return *this;
}
template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
template<typename T2>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator%=(const T2 b) {
    _get() %= b;
    return *this;
}

template<typename T, unsigned int I, unsigned int J, unsigned int K, unsigned int W>
HostMacroProperty<T, I, J, K, W>& HostMacroProperty<T, I, J, K, W>::operator=(const T val) {
    if (I != 1 || J != 1 || K != 1 || W != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    reinterpret_cast<T*>(metadata->h_base_ptr)[offset] = val;
    metadata->has_changed = true;
    return *this;
}

// SWIG versions
#ifdef SWIG
template<typename T>
HostMacroProperty_swig<T>::HostMacroProperty_swig(const std::shared_ptr<HostMacroProperty_MetaData>& _metadata)
    : dimensions(_metadata->dims)
    , offset(0u)
    , metadata(_metadata)
{ }
template<typename T>
HostMacroProperty_swig<T>::HostMacroProperty_swig(const std::shared_ptr<HostMacroProperty_MetaData>& _metadata, const ptrdiff_t& _offset, const std::array<unsigned int, 4> &_dimensions)
    : dimensions(_dimensions)
    , offset(_offset)
    , metadata(_metadata)
{ }
template<typename T>
HostMacroProperty_swig<T> HostMacroProperty_swig<T>::__getitem__(unsigned int i) const {
    if (dimensions[0] == 1 && dimensions[1] == 1 && dimensions[2] == 1 && dimensions[3] == 1) {
        THROW exception::InvalidOperation("Indexing error, property has less dimensions.\n");
    } else if (i >= dimensions[0]) {
        THROW exception::OutOfBoundsException("Indexing error, out of bounds %u >= %u.\n", i, dimensions[0]);
    }
    // (i * J * K * W) + (j * K * W) + (k * W) + w
    return HostMacroProperty_swig<T>(metadata, offset + (i * dimensions[1] * dimensions[2] * dimensions[3]), { dimensions[1], dimensions[2], dimensions[3], 1 });
}

template<typename T>
void HostMacroProperty_swig<T>::zero() {
    if (metadata->h_base_ptr) {
        // Memset on host
        memset(reinterpret_cast<T*>(metadata->h_base_ptr) + offset, 0, dimensions[0] * dimensions[1] * dimensions[2] * dimensions[3] * metadata->type_size);
        metadata->has_changed = true;
    } else {
        // Memset on device
        gpuErrchk(cudaMemset(reinterpret_cast<T*>(metadata->d_base_ptr) + offset, 0, dimensions[0] * dimensions[1] * dimensions[2] * dimensions[3] * metadata->type_size));
    }
}
template<typename T>
void HostMacroProperty_swig<T>::set(T val) {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    reinterpret_cast<T*>(metadata->h_base_ptr)[offset] = val;
    metadata->has_changed = true;
}
template<typename T>
void HostMacroProperty_swig<T>::__setitem__(unsigned int i, const T val) {
    if (dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    } else if (i >= dimensions[0]) {
        THROW exception::InvalidOperation("Indexing out of bounds %u >= %u.\n", i, dimensions[0]);
    }
    metadata->download();
    unsigned int t_offset = offset + (i * dimensions[1] * dimensions[2] * dimensions[3]);
    reinterpret_cast<T*>(metadata->h_base_ptr)[t_offset] = val;
    metadata->has_changed = true;
}
template<typename T>
int HostMacroProperty_swig<T>::__int__() {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return static_cast<int>(reinterpret_cast<T*>(metadata->h_base_ptr)[offset]);
}
template<typename T>
int64_t HostMacroProperty_swig<T>::__long__() {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return static_cast<int64_t>(reinterpret_cast<T*>(metadata->h_base_ptr)[offset]);
}
template<typename T>
double HostMacroProperty_swig<T>::__float__() {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return static_cast<double>(reinterpret_cast<T*>(metadata->h_base_ptr)[offset]);
}
template<typename T>
bool HostMacroProperty_swig<T>::__bool__() {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return static_cast<bool>(reinterpret_cast<T*>(metadata->h_base_ptr)[offset]);
}
template<typename T>
bool HostMacroProperty_swig<T>::__eq__(const T other) const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] == other;
}
template<typename T>
bool HostMacroProperty_swig<T>::__ne__(const T other) const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] != other;
}
template<typename T>
bool HostMacroProperty_swig<T>::__lt__(const T other) const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] < other;
}
template<typename T>
bool HostMacroProperty_swig<T>::__le__(const T other) const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] <= other;
}
template<typename T>
bool HostMacroProperty_swig<T>::__gt__(const T other) const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] > other;
}
template<typename T>
bool HostMacroProperty_swig<T>::__ge__(const T other) const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] >= other;
}
// template<typename T>
// T HostMacroProperty_swig<T>::__mod__(const T other) {
//     if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
//         THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
//     }
//     metadata->download();
//     return reinterpret_cast<T*>(metadata->h_base_ptr)[offset] % other;
// }
template<typename T>
T HostMacroProperty_swig<T>::get() const {
    if (dimensions[0] != 1 || dimensions[1] != 1 || dimensions[2] != 1 || dimensions[3] != 1) {
        THROW exception::InvalidOperation("Indexing error, property has more dimensions.\n");
    }
    metadata->download();
    return reinterpret_cast<T*>(metadata->h_base_ptr)[offset];
}
#endif

}  // namespace flamegpu

#endif  // INCLUDE_FLAMEGPU_RUNTIME_ENVIRONMENT_HOSTMACROPROPERTY_CUH_