Program Listing for File EnvironmentManager.cu

Return to documentation for file (src/flamegpu/simulation/detail/EnvironmentManager.cu)

#include "flamegpu/simulation/detail/EnvironmentManager.cuh"

#include <iostream>

#include "flamegpu/model/EnvironmentData.h"
#include "flamegpu/model/SubEnvironmentData.h"
#include "flamegpu/simulation/detail/CUDAErrorChecking.cuh"
#include "flamegpu/exception/FLAMEGPUException.h"
#include "flamegpu/util/nvtx.h"
#include "flamegpu/detail/cuda.cuh"

namespace flamegpu {
namespace detail {

void EnvironmentManager::init(const EnvironmentData& desc) {
    if (properties.size()) {
        THROW exception::EnvDescriptionAlreadyLoaded("Environment manager has already been initialised, in EnvironmentManager::init().");
    } else if (!desc.properties.size()) {
        return;
    }
    // Build a DefragMap to order the properties in reverse type size for efficient alignment packing
    DefragMap orderedProperties;
    for (auto _i = desc.properties.begin(); _i != desc.properties.end(); ++_i) {
        const auto& i = _i->second;
        DefragProp prop = DefragProp(i.data.ptr, i.data.length, i.isConst, i.data.elements, i.data.type);
        const size_t typeSize = i.data.length / i.data.elements;
        orderedProperties.emplace(std::make_pair(typeSize, _i->first), prop);
    }
    // Iterate the ordered properties to decide their offsets and calculate the required buffer length (with alignment)
    size_t newSize = 0;
    for (auto _i = orderedProperties.rbegin(); _i != orderedProperties.rend(); ++_i) {
        auto& i = _i->second;
        size_t alignmentSize = _i->first.first;  // aka typeSize
        // Handle alignment
        const ptrdiff_t alignmentOffset = newSize % alignmentSize;
        const ptrdiff_t alignmentFix = alignmentOffset != 0 ? alignmentSize - alignmentOffset : 0;
        newSize += alignmentFix;
        i.offset = newSize;
        newSize += i.length;
    }
    h_buffer_len = newSize;
    h_buffer = static_cast<char*>(malloc(newSize));
    // Now copy the items into the newly allocated buffer and build the cache
    for (auto _i = orderedProperties.rbegin(); _i != orderedProperties.rend(); ++_i) {
        auto& i = _i->second;
        const std::string& name = _i->first.second;
        // Setup property in new position
        memcpy(h_buffer + i.offset, i.data, i.length);
        properties.emplace(name, EnvProp(i.offset, i.length, i.isConst, i.elements, i.type));
    }
    gpuErrchk(cudaMalloc(&d_buffer, newSize));
}
void EnvironmentManager::init(const EnvironmentData& desc, const std::shared_ptr<EnvironmentManager>& parent_environment, const SubEnvironmentData& mapping) {
    init(desc);
    // Iterate and link up all mapped properties with parent model's environment
    std::set<std::string> new_mapped_props;
    for (const auto &mapped_prop : mapping.properties) {
        // Notify parent to map this property
        parent_environment->linkMappedProperty(mapped_prop.second, mapped_prop.first, shared_from_this());
        mapped_parent_properties.emplace(mapped_prop.first, MappedProp{mapped_prop.second, parent_environment, desc.properties.at(mapped_prop.first).isConst });
    }
}
void EnvironmentManager::linkMappedProperty(const std::string& parent_name, const std::string& sub_name, const std::shared_ptr<EnvironmentManager>& sub_environment) {
    // Check the mapping hasn't already been linked
    {
        const auto range = mapped_child_properties.equal_range(parent_name);
        for (auto i = range.first; i != range.second; ++i) {
            if (auto e = i->second.remoteEnv.lock()) {
                if (e.get() == sub_environment.get()) {
                    THROW exception::InvalidOperation("Environment property '%s' already has mapping configured with the provided sub_environment, "
                    "in EnvironmentManager::linkMappedProperty.", parent_name.c_str());
                }
            } else {
                THROW exception::ExpiredWeakPtr("Environment property '%s' has a mapping with an expired weak ptr, this should not occur, "
                    "in EnvironmentManager::linkMappedProperty.", parent_name.c_str());
            }
        }
    }
    // Link the mapping
    mapped_child_properties.emplace(parent_name, MappedProp{ sub_name, sub_environment });
    // If we also have this property mapped with a parent, propagate the mapping so the parent can update it directly
    const auto mpp_it = mapped_parent_properties.find(parent_name);
    if (mpp_it != mapped_parent_properties.end()) {
        if (const auto parent_environment = mpp_it->second.remoteEnv.lock()) {
            // Notify parent to map this property
            parent_environment->linkMappedProperty(mpp_it->second.remoteName, sub_name, sub_environment);
        } else {
            THROW exception::ExpiredWeakPtr("Environment property '%s' has a mapping with an expired weak ptr, this should not occur, "
                "in EnvironmentManager::linkMappedProperty.", parent_name.c_str());
        }
    } else {
        // Otherwise, propagate our current value directly to the sub_environment, so it begins with the correct value
        const auto p_it = properties.find(parent_name);
        if (p_it != properties.end()) {
            sub_environment->setPropertyDirect(sub_name, h_buffer + p_it->second.offset);
        } else {
            THROW exception::InvalidEnvProperty("Environment property '%s' was not found, "
                "in EnvironmentManager::linkMappedProperty().", parent_name.c_str());
        }
    }
}
void EnvironmentManager::propagateMappedPropertyValue(const std::string& property_name, const char* const src_ptr) {
    // Propagate all the way up the parent chain, then propagate updates back down
    // The update will be propagated back to child, however this is not an issue
    const auto mp_it = mapped_parent_properties.find(property_name);
    if (mp_it != mapped_parent_properties.end() && !mp_it->second.isConst) {
        if (const auto parent_environment = mp_it->second.remoteEnv.lock()) {
            // Notify parent to map this property
            parent_environment->propagateMappedPropertyValue(mp_it->second.remoteName, src_ptr);
        } else {
            THROW exception::ExpiredWeakPtr("Environment property '%s' has a mapping with an expired weak ptr, this should not occur, "
                "in EnvironmentManager::propagateMappedPropertyValue.", property_name.c_str());
        }
    } else {
        // Propagate locally, and then to children
        setPropertyDirect(property_name, src_ptr);
        const auto range = mapped_child_properties.equal_range(property_name);
        for (auto i = range.first; i != range.second; ++i) {
            if (auto remote_env = i->second.remoteEnv.lock()) {
                remote_env->setPropertyDirect(i->second.remoteName, src_ptr);
            } else {
                THROW exception::ExpiredWeakPtr("Environment property '%s' has a mapping with an expired weak ptr, this should not occur, "
                    "in EnvironmentManager::propagateMappedPropertyValue.", property_name.c_str());
            }
        }
    }
}
void EnvironmentManager::setPropertyDirect(const std::string& property_name, const char * const src_ptr) {
    const auto it = properties.find(property_name);
    if (it != properties.end()) {
        if (h_buffer + it->second.offset != src_ptr)  // Skip self copies
            memcpy(h_buffer + it->second.offset, src_ptr, it->second.length);
        d_buffer_ready = false;
    } else {
        THROW exception::InvalidEnvProperty("Environment property '%s' was not found, "
            "in EnvironmentManager::setProperty().", property_name.c_str());
    }
}
detail::Any EnvironmentManager::getPropertyAny(const std::string& property_name) const {
    const EnvProp &prop = findProperty<void>(property_name, false, 0);
    return detail::Any(h_buffer + prop.offset, prop.length, prop.type, prop.elements);
}

EnvironmentManager::~EnvironmentManager() {
    properties.clear();
    if (h_buffer) {
        free(h_buffer);
        h_buffer = nullptr;
    }
    if (d_buffer) {
        gpuErrchk(flamegpu::detail::cuda::cudaFree(d_buffer));
        d_buffer = nullptr;
    }
    h_buffer_len = 0;
}
void EnvironmentManager::resetModel(const EnvironmentData& desc) {
    for (const auto &dp : desc.properties) {
        if (mapped_parent_properties.find(dp.first) == mapped_parent_properties.end()) {
            // Only reset properties which are not inherited from parent
            const auto& p = properties.at(dp.first);
            char * const dest_ptr = h_buffer + p.offset;
            memcpy(dest_ptr, dp.second.data.ptr, p.length);
            //  Notify children
            propagateMappedPropertyValue(dp.first, dest_ptr);
        }
    }
}
void EnvironmentManager::updateDevice_async(const cudaStream_t stream) const {
    if (!d_buffer_ready) {
        gpuErrchk(cudaMemcpyAsync(d_buffer, h_buffer, h_buffer_len, cudaMemcpyHostToDevice, stream));
        d_buffer_ready = true;
    }
}
}  // namespace detail
}  // namespace flamegpu