Commit ea4e8f7e authored by Gael Rial Costas's avatar Gael Rial Costas
Browse files

Add synapse representation.

parent 967f5afe
......@@ -2,6 +2,25 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MVDTool" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MVDTool/deps/libsonata" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MVDTool/python/pybind11" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/3rdparty/Catch2" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/3rdparty/GSL_LITE" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/3rdparty/HighFive" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/3rdparty/HighFive/deps/catch2" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/3rdparty/ghc_filesystem" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/3rdparty/lexertl14" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/MorphIO/binds/python/pybind11" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/libsonata" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/libsonata/extlib/Catch2" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/libsonata/extlib/HighFive" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/libsonata/extlib/HighFive/deps/catch2" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/libsonata/extlib/fmt" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/brion-src/deps/libsonata/python/pybind11" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/hey-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/brion-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/brion-src/deps/MVDTool" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/brion-src/deps/MVDTool/deps/libsonata" vcs="Git" />
......@@ -21,15 +40,5 @@
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/brion-src/deps/libsonata/extlib/fmt" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/brion-src/deps/libsonata/python/pybind11" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/hey-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/mindset" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/nativefiledialog" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/glslang" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/imgui" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/implot" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/libzippp" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/rush" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/spirv_headers" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/neon/lib/spirv_tools" vcs="Git" />
</component>
</project>
\ No newline at end of file
......@@ -16,7 +16,6 @@ FetchContent_Declare(
neon
GIT_REPOSITORY https://github.com/gaeqs/Neon.git
GIT_TAG master
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/neon
)
FetchContent_MakeAvailable(neon)
......@@ -25,7 +24,6 @@ FetchContent_Declare(
mindset
GIT_REPOSITORY https://gitlab.gmrv.es/g.rial/mindset.git
GIT_TAG master
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/mindset
)
FetchContent_MakeAvailable(mindset)
......@@ -34,7 +32,6 @@ FetchContent_Declare(
nativefiledialog
GIT_REPOSITORY https://github.com/btzy/nativefiledialog-extended.git
GIT_TAG a1a401062819beb8c3da84518ab1fe7de88632db
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/nativefiledialog
)
FetchContent_MakeAvailable(nativefiledialog)
......
neon @ 28774897
Subproject commit 28774897c1903f6d7706efa3ff102e0d12455fa6
......@@ -39,6 +39,10 @@ cmrc_add_resource_library(
resources/shader/selector/selector_resolver.vert
resources/shader/selector/selector_resolver.json
resources/shader/synapse/synapse.task
resources/shader/synapse/synapse.mesh
resources/shader/synapse/synapse.frag
resources/texture/skybox/back.png
resources/texture/skybox/bottom.png
resources/texture/skybox/front.png
......
......@@ -110,12 +110,4 @@ namespace neoneuron
std::string data = _settings.dump(4);
out.write(data.c_str(), data.size());
}
mindset::UID NeoneuronApplication::findSmallestAvailableUID() const
{
mindset::UID smallest = 0;
while (_dataset.getNeuron(smallest).has_value()) {
++smallest;
}
return smallest;
}
} // namespace neoneuron
......@@ -135,9 +135,6 @@ namespace neoneuron
* Saves the settings' json into a file.
*/
void saveSettings() const;
//TODO move this method to mindset.
mindset::UID findSmallestAvailableUID() const;
};
} // namespace neoneuron
......
......@@ -24,14 +24,6 @@ namespace neoneuron
[[nodiscard]] virtual const NeoneuronRender* getRender() const = 0;
[[nodiscard]] virtual size_t getNeuronsAmount() = 0;
[[nodiscard]] virtual size_t getSectionsAmount() = 0;
[[nodiscard]] virtual size_t getJointsAmount() = 0;
[[nodiscard]] virtual size_t getSomasAmount() = 0;
[[nodiscard]] virtual rush::AABB<3, float> getSceneBoundingBox() const = 0;
virtual void refreshNeuronProperty(mindset::UID neuronId, const std::string& propertyName) = 0;
......
......@@ -4,6 +4,8 @@
#include "NeoneuronRender.h"
#include "synapse/SynapseRepresentation.h"
#include <vector>
#include <neon/util/DeferredUtils.h>
......@@ -80,6 +82,7 @@ namespace neoneuron
initSelectionResolver();
_representations.push_back(std::make_unique<ComplexNeuronRepresentation>(this));
_representations.push_back(std::make_unique<SynapseRepresentation>(this));
}
NeoneuronRender::~NeoneuronRender()
......@@ -194,9 +197,6 @@ namespace neoneuron
aabb = rush::AABB<3, float>::fromEdges(min, max);
}
for (auto& rep : _representations) {
}
return aabb;
}
......
......@@ -144,7 +144,6 @@ namespace neoneuron
void ComplexNeuronRepresentation::loadNeuronModel()
{
constexpr size_t INSTANCES = 10000000;
auto* app = &_render->getApplication();
auto drawable = std::make_shared<neon::MeshShaderDrawable>(app, "Neuron", _neuronMaterial);
......@@ -155,7 +154,7 @@ namespace neoneuron
});
neon::ModelCreateInfo modelCreateInfo;
modelCreateInfo.maximumInstances = INSTANCES;
modelCreateInfo.maximumInstances = SEGMENT_INSTANCES;
modelCreateInfo.drawables.push_back(drawable);
modelCreateInfo.uniformBufferBindings[UNIFORM_SET] = neon::ModelBufferBinding::extra(_ubo);
......@@ -175,7 +174,6 @@ namespace neoneuron
void ComplexNeuronRepresentation::loadJointModel()
{
constexpr size_t INSTANCES = 10000000;
auto* app = &_render->getApplication();
auto drawable = std::make_shared<neon::MeshShaderDrawable>(app, "Joint", _jointMaterial);
......@@ -186,7 +184,7 @@ namespace neoneuron
});
neon::ModelCreateInfo modelCreateInfo;
modelCreateInfo.maximumInstances = INSTANCES;
modelCreateInfo.maximumInstances = JOINT_INSTANCES;
modelCreateInfo.drawables.push_back(drawable);
modelCreateInfo.uniformBufferBindings[UNIFORM_SET] = neon::ModelBufferBinding::extra(_ubo);
......@@ -208,7 +206,6 @@ namespace neoneuron
void ComplexNeuronRepresentation::loadSomaModel()
{
constexpr size_t INSTANCES = 100000;
auto* app = &_render->getApplication();
auto drawable = std::make_shared<neon::MeshShaderDrawable>(app, "Soma", _somaMaterial);
......@@ -217,7 +214,7 @@ namespace neoneuron
});
neon::ModelCreateInfo modelCreateInfo;
modelCreateInfo.maximumInstances = INSTANCES;
modelCreateInfo.maximumInstances = SOMA_INSTANCES;
modelCreateInfo.drawables.push_back(drawable);
modelCreateInfo.uniformBufferBindings[UNIFORM_SET] = neon::ModelBufferBinding::extra(_ubo);
......@@ -313,6 +310,7 @@ namespace neoneuron
}
}
recalculateBoundingBox();
updateGPURepresentationData();
}
......@@ -374,7 +372,7 @@ namespace neoneuron
(data + *segment.value().id)->selected = true;
}
}
void ComplexNeuronRepresentation::updateGPURepresentationData()
void ComplexNeuronRepresentation::updateGPURepresentationData() const
{
_ubo->uploadData(REPRESENTATION_BINDING,
ComplexNeuronRepresentationData(getSectionsAmount(), getJointsAmount()));
......@@ -438,22 +436,22 @@ namespace neoneuron
return _neurons;
}
size_t ComplexNeuronRepresentation::getNeuronsAmount()
size_t ComplexNeuronRepresentation::getNeuronsAmount() const
{
return _neurons.size();
}
size_t ComplexNeuronRepresentation::getSectionsAmount()
size_t ComplexNeuronRepresentation::getSectionsAmount() const
{
return _neuronModel->getInstanceData(0)->getInstanceAmount();
}
size_t ComplexNeuronRepresentation::getJointsAmount()
size_t ComplexNeuronRepresentation::getJointsAmount() const
{
return _jointModel->getInstanceData(0)->getInstanceAmount();
}
size_t ComplexNeuronRepresentation::getSomasAmount()
size_t ComplexNeuronRepresentation::getSomasAmount() const
{
return _somaModel->getInstanceData(0)->getInstanceAmount();
}
......
......@@ -41,7 +41,7 @@ namespace neoneuron
static constexpr size_t SOMA_GPU_DATA_BINDING = 6;
static constexpr size_t SEGMENT_INSTANCES = 10000000;
static constexpr size_t JOINT_INSTANCES = 10000000;
static constexpr size_t JOINT_INSTANCES = 1000000;
static constexpr size_t SOMA_INSTANCES = 100000;
static constexpr size_t STORAGE_PER_SOMA = 64 * 64;
static constexpr size_t STORAGE_PER_SECTION = sizeof(uint32_t);
......@@ -116,7 +116,7 @@ namespace neoneuron
void onSelectionEvent(SelectionEvent event);
void updateGPURepresentationData();
void updateGPURepresentationData() const;
public:
ComplexNeuronRepresentation(const ComplexNeuronRepresentation&) = delete;
......@@ -131,13 +131,13 @@ namespace neoneuron
[[nodiscard]] const std::unordered_map<mindset::UID, ComplexNeuron>& getNeurons() const;
[[nodiscard]] size_t getNeuronsAmount() override;
[[nodiscard]] size_t getNeuronsAmount() const;
[[nodiscard]] size_t getSectionsAmount() override;
[[nodiscard]] size_t getSectionsAmount() const;
[[nodiscard]] size_t getJointsAmount() override;
[[nodiscard]] size_t getJointsAmount() const;
[[nodiscard]] size_t getSomasAmount() override;
[[nodiscard]] size_t getSomasAmount() const;
[[nodiscard]] std::optional<ComplexNeuron*> findNeuron(mindset::UID uid);
......
//
// Created by gaeqs on 31/03/25.
//
#include "GPUSynapse.h"
namespace neoneuron
{
GPUSynapse::GPUSynapse(GPUSynapse&& other) noexcept :
_instanceData(other._instanceData),
_instance(other._instance),
_valid(other._valid),
_prePosition(other._prePosition),
_postPosition(other._postPosition)
{
other._valid = false;
}
GPUSynapse::GPUSynapse(const GPUSynapseCreateInfo& info, const mindset::Synapse* synapse) :
_instanceData(info.instanceData),
_valid(true)
{
auto result = info.instanceData->createInstance();
if (!result.isOk()) {
neon::error() << result.getError();
return;
}
_instance = result.getResult();
refreshGPUData(info, synapse);
}
GPUSynapse::~GPUSynapse()
{
if (_valid) {
_instanceData->freeInstance(_instance);
}
}
std::optional<rush::Vec3f> GPUSynapse::getPrePosition() const
{
return _prePosition;
}
std::optional<rush::Vec3f> GPUSynapse::getPostPosition() const
{
return _postPosition;
}
void GPUSynapse::refreshGPUData(const GPUSynapseCreateInfo& info, const mindset::Synapse* synapse)
{
if (!_valid) {
return;
}
_prePosition = synapse->getProperty<rush::Vec3f>(info.prePositionProperty);
_postPosition = synapse->getProperty<rush::Vec3f>(info.postPositionProperty);
GPUSynapseData data;
data.uid = synapse->getUID();
data.preNeuronId = synapse->getPreSynapticNeuron();
data.postNeuronId = synapse->getPostSynapticNeuron();
data.property = 0.0f;
if (_prePosition) {
data.prePosition = rush::Vec4f(*_prePosition, 1.0f);
} else {
data.prePosition = rush::Vec4f(0.0f);
}
if (_postPosition) {
data.postPosition = rush::Vec4f(*_postPosition, 1.0f);
} else {
data.postPosition = rush::Vec4f(0.0f);
}
info.instanceData->uploadData(_instance, 0, data);
}
} // namespace neoneuron
\ No newline at end of file
//
// Created by gaeqs on 31/03/25.
//
#ifndef GPUSYNAPSE_H
#define GPUSYNAPSE_H
#include <mindset/mindset.h>
#include <rush/vector/vec.h>
#include <neon/render/model/InstanceData.h>
#include <neon/render/model/Model.h>
#include <neoneuron/render/synapse/GPUSynapseCreateInfo.h>
namespace neoneuron
{
struct GPUSynapseData
{
mindset::UID uid;
mindset::UID preNeuronId;
mindset::UID postNeuronId;
float property;
rush::Vec4f prePosition; // x, y, z, w = valid
rush::Vec4f postPosition; // x, y, z, w = valid
};
class GPUSynapse
{
neon::InstanceData* _instanceData;
neon::InstanceData::Instance _instance;
bool _valid;
std::optional<rush::Vec3f> _prePosition;
std::optional<rush::Vec3f> _postPosition;
public:
GPUSynapse(GPUSynapse&& other) noexcept;
GPUSynapse(const GPUSynapse& other) = delete;
GPUSynapse(const GPUSynapseCreateInfo& info, const mindset::Synapse* synapse);
~GPUSynapse();
[[nodiscard]] std::optional<rush::Vec3f> getPrePosition() const;
[[nodiscard]] std::optional<rush::Vec3f> getPostPosition() const;
void refreshGPUData(const GPUSynapseCreateInfo& info, const mindset::Synapse* synapse);
};
} // namespace neoneuron
#endif //GPUSYNAPSE_H
//
// Created by gaeqs on 31/03/25.
//
#ifndef GPUSYNAPSECREATEINFO_H
#define GPUSYNAPSECREATEINFO_H
namespace neoneuron
{
struct GPUSynapseCreateInfo
{
mindset::Dataset* dataset;
neon::InstanceData* instanceData;
mindset::UID prePositionProperty;
mindset::UID postPositionProperty;
};
} // namespace neoneuron
#endif //GPUSYNAPSECREATEINFO_H
//
// Created by gaeqs on 31/03/25.
//
#include "SynapseRepresentation.h"
#include "GPUSynapse.h"
#include <neoneuron/application/NeoneuronApplication.h>
#include <neoneuron/render/NeoneuronRender.h>
CMRC_DECLARE(resources);
namespace neoneuron
{
void SynapseRepresentation::loadUniformBuffers()
{
auto* app = &_render->getApplication();
std::vector bindings = {
neon::ShaderUniformBinding::uniformBuffer(sizeof(SynapseRepresentationData)),
neon::ShaderUniformBinding::storageBuffer(sizeof(GPUSynapseData) * SYNAPSE_INSTANCES),
};
_uboDescriptor = std::make_shared<neon::ShaderUniformDescriptor>(app, "neoneuron:synapse_descriptor", bindings);
_ubo = std::make_shared<neon::ShaderUniformBuffer>("neoneuron:synapse_ubo", _uboDescriptor);
}
void SynapseRepresentation::loadShader()
{
auto shader = std::make_shared<neon::ShaderProgram>(&_render->getApplication(), "neoneuron:synapse");
auto fs = cmrc::resources::get_filesystem();
shader->addShader(neon::ShaderType::TASK, fs.open("/shader/synapse/synapse.task"));
shader->addShader(neon::ShaderType::MESH, fs.open("/shader/synapse/synapse.mesh"));
shader->addShader(neon::ShaderType::FRAGMENT, fs.open("/shader/synapse/synapse.frag"));
if (auto result = shader->compile(); result.has_value()) {
neon::error() << result.value();
} else {
_shader = std::move(shader);
}
}
void SynapseRepresentation::loadMaterial()
{
auto* app = &_render->getApplication();
neon::MaterialCreateInfo materialCreateInfo(_render->getRenderFrameBuffer(), _shader);
materialCreateInfo.rasterizer.cullMode = neon::CullMode::BACK;
materialCreateInfo.rasterizer.polygonMode = neon::PolygonMode::FILL;
materialCreateInfo.descriptions.uniformBindings[UNIFORM_SET] = neon::DescriptorBinding::extra(_uboDescriptor);
_material = std::make_shared<neon::Material>(app, "neoneuron:synapse", materialCreateInfo);
}
void SynapseRepresentation::loadModel()
{
auto* app = &_render->getApplication();
auto drawable = std::make_shared<neon::MeshShaderDrawable>(app, "neoneuron:synapse", _material);
drawable->setGroupsSupplier([](const neon::Model& model) {
uint32_t amount = model.getInstanceData(0)->getInstanceAmount();
uint32_t tasks = amount / 1024 + (amount % 1024 != 0);
return rush::Vec<3, uint32_t>{tasks, 1, 1};
});
neon::ModelCreateInfo modelCreateInfo;
modelCreateInfo.maximumInstances = SYNAPSE_INSTANCES;
modelCreateInfo.drawables.push_back(drawable);
modelCreateInfo.uniformBufferBindings[UNIFORM_SET] = neon::ModelBufferBinding::extra(_ubo);
modelCreateInfo.defineInstanceType<GPUSynapseData>();
modelCreateInfo.instanceDataProvider = [this](neon::Application* app, const neon::ModelCreateInfo& info,
const neon::Model* model) {
std::vector indices = {neon::StorageBufferInstanceData::Slot(sizeof(GPUSynapseData), sizeof(GPUSynapseData),
SYNAPSE_BINDING, _ubo.get())};
return std::vector<neon::InstanceData*>{new neon::StorageBufferInstanceData(app, info, indices)};
};
_model = std::make_shared<neon::Model>(app, "neoneuron:synapse", modelCreateInfo);
_render->getRoom()->markUsingModel(_model.get());
}
void SynapseRepresentation::onSynapseAdded(mindset::Synapse* synapse)
{
auto [it, ok] = _gpuSynapses.emplace(std::piecewise_construct, std::forward_as_tuple(synapse->getUID()),
std::forward_as_tuple(_createInfo, synapse));
if (ok) {
if (_gpuSynapses.size() == 1) {
recalculateBoundingBox();
} else {
addSynapseToBoundingBox(it->second);
}
updateGPURepresentationData();
}
}
void SynapseRepresentation::onSynapseRemoved(mindset::UID uid)
{
_gpuSynapses.erase(uid);
recalculateBoundingBox();
updateGPURepresentationData();
}
void SynapseRepresentation::onClear()
{
_gpuSynapses.clear();
_sceneBoundingBox = {};
updateGPURepresentationData();
}
void SynapseRepresentation::recalculateBoundingBox()
{
if (_gpuSynapses.empty()) {
_sceneBoundingBox = {};
return;
}
auto min = rush::Vec3f(std::numeric_limits<float>::max());
auto max = rush::Vec3f(std::numeric_limits<float>::min());
for (auto& synapse : _gpuSynapses | std::views::values) {
auto pre = synapse.getPrePosition();
auto post = synapse.getPostPosition();
if (pre) {
min = rush::min(min, *pre);
max = rush::max(max, *pre);
}
if (post) {
min = rush::min(min, *post);
max = rush::max(max, *post);
}
}
_sceneBoundingBox = rush::AABB<3, float>::fromEdges(min, max);
}
void SynapseRepresentation::addSynapseToBoundingBox(const GPUSynapse& synapse)
{
auto min = _sceneBoundingBox.center - _sceneBoundingBox.radius;
auto max = _sceneBoundingBox.center + _sceneBoundingBox.radius;
auto pre = synapse.getPrePosition();
auto post = synapse.getPostPosition();
if (pre) {
min = rush::min(min, *pre);
max = rush::max(max, *pre);
}
if (post) {
min = rush::min(min, *post);
max = rush::max(max, *post);
}
_sceneBoundingBox = rush::AABB<3, float>::fromEdges(min, max);
}
void SynapseRepresentation::updateGPURepresentationData() const
{
_ubo->uploadData(REPRESENTATION_BINDING, SynapseRepresentationData(_gpuSynapses.size()));
}
SynapseRepresentation::SynapseRepresentation(NeoneuronRender* render) :
_render(render),
_createInfo()
{
loadUniformBuffers();
loadShader();
loadMaterial();
loadModel();
auto& dataset = render->getNeoneuronApplication()->getDataset();
_createInfo =
GPUSynapseCreateInfo(&dataset, _model->getInstanceData(0),
dataset.getProperties().defineProperty(mindset::PROPERTY_SYNAPSE_PRE_POSITION),
dataset.getProperties().defineProperty(mindset::PROPERTY_SYNAPSE_POST_POSITION));
_synapseAddListener = [this](mindset::Synapse* synapse) { onSynapseAdded(synapse); };
_synapseRemoveListener = [this](mindset::UID uid) { onSynapseRemoved(uid); };
_clearListener = [this](void*) { onClear(); };
auto& circuit = render->getNeoneuronApplication()->getDataset().getCircuit();
circuit.getSynapseAddedEvent() += _synapseAddListener;
circuit.getSynapseRemovedEvent() += _synapseRemoveListener;
circuit.getClearEvent() += _clearListener;
}
SynapseRepresentation::~SynapseRepresentation()
{
if (_model != nullptr) {
_render->getRoom()->unmarkUsingModel(_model.get());
}
}
NeoneuronRender* SynapseRepresentation::getRender()
{
return _render;
}
const NeoneuronRender* SynapseRepresentation::getRender() const
{
return _render;
}
rush::AABB<3, float> SynapseRepresentation::getSceneBoundingBox() const
{
return _sceneBoundingBox;
}
void SynapseRepresentation::refreshNeuronProperty(mindset::UID neuronId, const std::string& propertyName)
{
}
} // namespace neoneuron
//
// Created by gaeqs on 31/03/25.
//
#ifndef SYNAPSEREPRESENTATION_H
#define SYNAPSEREPRESENTATION_H
#include <neon/render/model/Model.h>
#include <neon/render/shader/Material.h>
#include <neon/render/shader/ShaderProgram.h>
#include <neoneuron/render/AbstractNeuronRepresentation.h>
#include <neoneuron/render/synapse/GPUSynapse.h>
namespace neoneuron
{
struct SynapseRepresentationData
{
uint32_t synapses;
};
class SynapseRepresentation : public AbstractNeuronRepresentation
{
public:
static constexpr size_t UNIFORM_SET = 2;
static constexpr size_t REPRESENTATION_BINDING = 0;
static constexpr size_t SYNAPSE_BINDING = 1;
static constexpr size_t SYNAPSE_INSTANCES = 10'000'000;
private:
NeoneuronRender* _render;
rush::AABB<3, float> _sceneBoundingBox;
std::shared_ptr<neon::ShaderUniformDescriptor> _uboDescriptor;
std::shared_ptr<neon::ShaderUniformBuffer> _ubo;
std::shared_ptr<neon::ShaderProgram> _shader;
std::shared_ptr<neon::Material> _material;
std::shared_ptr<neon::Model> _model;
/**
* Information required to create a GPUSynapse,
* Conveniently folded for an easy use.
*/
GPUSynapseCreateInfo _createInfo;
hey::Listener<mindset::Synapse*> _synapseAddListener;
hey::Listener<mindset::UID> _synapseRemoveListener;
hey::Listener<void*> _clearListener;
std::unordered_map<mindset::UID, GPUSynapse> _gpuSynapses;
void loadUniformBuffers();
void loadShader();
void loadMaterial();
void loadModel();
void onSynapseAdded(mindset::Synapse* synapse);
void onSynapseRemoved(mindset::UID uid);
void onClear();
void recalculateBoundingBox();
void addSynapseToBoundingBox(const GPUSynapse& synapse);
void updateGPURepresentationData() const;
public:
SynapseRepresentation(const SynapseRepresentation&) = delete;
explicit SynapseRepresentation(NeoneuronRender* render);
~SynapseRepresentation() override;
[[nodiscard]] NeoneuronRender* getRender() override;
[[nodiscard]] const NeoneuronRender* getRender() const override;
[[nodiscard]] rush::AABB<3, float> getSceneBoundingBox() const override;
void refreshNeuronProperty(mindset::UID neuronId, const std::string& propertyName) override;
};
} // namespace neoneuron
#endif //SYNAPSEREPRESENTATION_H
......@@ -29,9 +29,8 @@ namespace neoneuron
if (prototype != nullptr && prop.has_value()) {
auto transform = prototype->getProperty<mindset::NeuronTransform>(prop.value());
if (transform.has_value()) {
auto model = transform.value().getModel();
min = model * rush::Vec4f(min, 1.0f);
max = model * rush::Vec4f(max, 1.0f);
min = transform->positionToGlobalCoordinates(min);
max = transform->positionToGlobalCoordinates(max);
}
}
}
......
......@@ -61,7 +61,7 @@ namespace neoneuron
}
if (ImGui::MenuItem("Duplicate")) {
mindset::Neuron copy(*neuron);
copy.setUID(_render->getNeoneuronApplication()->findSmallestAvailableUID());
copy.setUID(_render->getNeoneuronApplication()->getDataset().findSmallestAvailableNeuronUID());
auto prop = dataset.getProperties().defineProperty(mindset::PROPERTY_NAME);
copy.setProperty(prop, name + " (copy)");
dataset.addNeuron(std::move(copy));
......
......@@ -94,10 +94,10 @@ namespace neoneuron
if (ImGui::Button("Load", ImVec2(120, 0))) {
neon::Chronometer chrono;
auto loader = generateLoader();
loader->addUIDProvider([&] { return _application->findSmallestAvailableUID(); });
loader->addUIDProvider([&] { return _application->getDataset().findSmallestAvailableNeuronUID(); });
if (auto* l = dynamic_cast<mindset::BlueConfigLoader*>(loader.get())) {
l->addTarget("MiniColumn_501");
l->addTarget("felix");
}
auto listener = loader->createListener([](mindset::LoaderStatus status) {
......
#version 460
layout(location = 0) flat in uint fragSynapseType;
layout(location = 1) in vec3 fragCenter;
layout(location = 2) in vec3 fragPosition;
layout(location = 0) out vec4 color;
layout(location = 1) out vec3 ids;
const float PARTICLE_RADIUS = 1.5f;
void main() {
vec3 d = fragPosition - fragCenter;
if (dot(d, d) > PARTICLE_RADIUS * PARTICLE_RADIUS) {
discard;
}
if (fragSynapseType == 0) {
// Presynaptic
color = vec4(1.0f, 0.58f, 0.01f, 1.0f);
} else {
// Postsynaptic
color = vec4(0, 0.22f, 1.0f, 1.0f);
}
ids = vec3(0, 0, 0);
}
\ No newline at end of file
#version 460
#extension GL_EXT_mesh_shader: enable
#extension GL_KHR_shader_subgroup_ballot: enable
const float M_PI = 3.1415926535897932384626433832795f;
const float PARTICLE_RADIUS = 1.5f;
layout (local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
layout (triangles) out;
layout (max_vertices = 256, max_primitives = 128) out;
layout (set = 0, binding = 0) uniform Matrices {
mat4 view;
mat4 viewProjection;
mat4 inverseProjection;
vec4 planes[6];
float near;
float far;
};
layout (set = 0, binding = 2) uniform Scene {
vec3 sceneCenter;
vec3 sceneRadius;
};
layout (set = 0, binding = 1) uniform GlobalData {
float currentTime;
float radiusStrength;
float somaRadiusStrength;
float startClip;
float endClip;
float splitHeight;
float splitArcStrength;
uint rotationIndexOffset;
uint childrenRotationIndexOffset;
uint minChildrenForJoint;
uint verticesPerCircle;
uint somaLatitudes;
uint somaLongitudes;
float somaConnectionPushFactor;
float somaWeightPower;
float somaSphereWeight;
float somaConnectionMaxWeight;
uint lod;
uint frame;
uint savingNeuron;
vec4 defaultColor;
vec4 selectedColor;
};
struct TaskData {
uint synapses[1024];
uint amount;
};
struct Synapse {
uint uid;
uint preNeuronId;
uint postNeuronId;
float property;
vec4 prePosition;
vec4 postPosition;
};
layout (set = 2, binding = 0) uniform Representation {
uint synapsesAmount;
};
layout(std430, set = 2, binding = 1) buffer Synapses {
Synapse synapses[];
};
taskPayloadSharedEXT TaskData td;
layout(location = 0) flat out uint fragSynapseType[];
layout(location = 1) out vec3 fragCenter[];
layout(location = 2) out vec3 fragPosition[];
void generateSquare(uint index, vec3 position, bool presynaptic) {
vec3 camRight = normalize(vec3(view[0][0], view[1][0], view[2][0]));
vec3 camUp = normalize(vec3(view[0][1], view[1][1], view[2][1]));
vec3 v0 = position - PARTICLE_RADIUS * camRight - PARTICLE_RADIUS * camUp;
vec3 v1 = position + PARTICLE_RADIUS * camRight - PARTICLE_RADIUS * camUp;
vec3 v2 = position + PARTICLE_RADIUS * camRight + PARTICLE_RADIUS * camUp;
vec3 v3 = position - PARTICLE_RADIUS * camRight + PARTICLE_RADIUS * camUp;
uint vertexBase = index * 4u;
fragPosition[vertexBase + 0] = v0;
fragPosition[vertexBase + 1] = v1;
fragPosition[vertexBase + 2] = v2;
fragPosition[vertexBase + 3] = v3;
gl_MeshVerticesEXT[vertexBase + 0].gl_Position = viewProjection * vec4(v0, 1.0);
gl_MeshVerticesEXT[vertexBase + 1].gl_Position = viewProjection * vec4(v1, 1.0);
gl_MeshVerticesEXT[vertexBase + 2].gl_Position = viewProjection * vec4(v2, 1.0);
gl_MeshVerticesEXT[vertexBase + 3].gl_Position = viewProjection * vec4(v3, 1.0);
for (uint i = 0; i < 4; ++i) {
fragSynapseType[vertexBase + i] = presynaptic ? 0 : 1;
fragCenter[vertexBase + i] = position;
}
uint primBase = index * 2u;
// Primer triángulo: v0, v1, v2
gl_PrimitiveTriangleIndicesEXT[primBase + 0] = uvec3(vertexBase, vertexBase + 1, vertexBase + 2);
gl_PrimitiveTriangleIndicesEXT[primBase + 1] = uvec3(vertexBase, vertexBase + 2, vertexBase + 3);
}
void main() {
Synapse synapse = synapses[td.synapses[gl_GlobalInvocationID.x]];
bool valid = gl_GlobalInvocationID.x < td.amount;
bool validPre = valid && synapse.prePosition.w > 0.5f;
bool validPost = valid && synapse.postPosition.w > 0.5f;
uvec4 preVote = subgroupBallot(validPre);
if (validPre) {
generateSquare(subgroupBallotExclusiveBitCount(preVote), synapse.prePosition.xyz, true);
}
uint postStart = subgroupBallotBitCount(preVote);
uvec4 postVote = subgroupBallot(validPost);
if (validPost) {
generateSquare(postStart + subgroupBallotExclusiveBitCount(postVote), synapse.postPosition.xyz, false);
}
uint totalAmount = postStart + subgroupBallotBitCount(postVote);
SetMeshOutputsEXT(totalAmount * 4, totalAmount * 2);
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment