Skip to content

Commit

Permalink
particle system multi-threaded
Browse files Browse the repository at this point in the history
  • Loading branch information
beaumanvienna committed Nov 6, 2024
1 parent 2119626 commit 2058710
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 34 deletions.
58 changes: 38 additions & 20 deletions application/lucre/particleSystem/snow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@
#include "auxiliary/file.h"
#include "auxiliary/random.h"
#include "particleSystem/snow.h"
#include "renderer/instanceBuffer.h"
#include "renderer/builder/fastgltfBuilder.h"

using namespace simdjson;

namespace GfxRenderEngine
{
Snow::Snow(Scene& scene, std::string const& jsonFile)
Expand All @@ -35,58 +38,77 @@ namespace GfxRenderEngine
ParseSysDescription(jsonFile);
if (!m_Initialized)
{
LOG_CORE_CRITICAL("Snow::Snow failed to initialize!");
LOG_CORE_CRITICAL("Snow::Snow failed to initialize! (ParseSysDescription)");
return;
}

// load model with m_PoolSize instances
std::vector<entt::entity> snowflakeFirstInstances;
{
FastgltfBuilder builder(m_SysDescription.m_Model.value(), m_Scene);
builder.SetDictionaryPrefix("snow");
builder.Load(m_SysDescription.m_PoolSize.value());
auto& dictionaryPrefix = m_SysDescription.m_DictionaryPrefix.value();
auto& model = m_SysDescription.m_Model.value();
auto& sceneGraph = m_Scene.GetSceneGraph();
auto& numberOfInstances = m_SysDescription.m_PoolSize.value();

// create group node
auto entity = m_Registry.Create();
auto name = dictionaryPrefix + "::" + model + "::root";
int groupNode = sceneGraph.CreateNode(SceneGraph::ROOT_NODE, entity, name, m_Dictionary);
TransformComponent transform{};
m_Registry.emplace<TransformComponent>(entity, transform);

FastgltfBuilder builder(model, m_Scene, groupNode);
builder.SetDictionaryPrefix(dictionaryPrefix);
m_Initialized = builder.Load(numberOfInstances, snowflakeFirstInstances);
}

if (!m_Initialized || !snowflakeFirstInstances.size())
{
LOG_CORE_CRITICAL("Snow::Snow failed to initialize! (load model)");
return;
}

// set up particles
{
m_ParticlePool.resize(m_SysDescription.m_PoolSize.value());
auto prefix = std::string(m_SysDescription.m_DictionaryPrefix.value()) + "::";
auto& model = m_SysDescription.m_Model.value();
auto& vertex1 = m_SysDescription.m_Vertex1.value();
auto& vertex2 = m_SysDescription.m_Vertex2.value();
auto volumeSize = (vertex2 - vertex1) / 2.0f;
uint idx{0};
for (auto& particle : m_ParticlePool)
auto& snowflake = snowflakeFirstInstances[0];

if (snowflake != entt::null)
{
auto key = prefix + model + "::" + std::to_string(idx) + "::root";
entt::entity snowflake = m_Dictionary.Retrieve(key);
if (snowflake != entt::null)
auto& instances = m_Registry.get<InstanceTag>(snowflake).m_Instances;
uint index{0};
for (auto& particle : m_ParticlePool)
{
particle.m_Entity = snowflake;
particle.m_RotationSpeed = {0.0f, 0.0f, EngineCore::RandomPlusMinusOne()};
particle.m_Velocity = {0.0f, -1.0f + EngineCore::RandomPlusMinusOne(), 0.0f};

auto& transform = m_Registry.get<TransformComponent>(snowflake);
auto& transform = m_Registry.get<TransformComponent>(instances[index]);
particle.m_Transform = &transform;
transform.SetRotation(
{glm::half_pi<float>(), 0.0f, glm::pi<float>() * EngineCore::RandomPlusMinusOne()});
transform.SetTranslation(
{(vertex1.x + volumeSize.x) + (volumeSize.x * EngineCore::RandomPlusMinusOne()),
(vertex1.y + volumeSize.y) + (volumeSize.y * EngineCore::RandomPlusMinusOne()),
(vertex1.z + volumeSize.z) + (volumeSize.z * EngineCore::RandomPlusMinusOne())});
transform.SetScale(0.014f);
++index;
}
++idx;
}
}
}

void Snow::OnUpdate(Timestep timestep, entt::entity camera)
void Snow::OnUpdate(Timestep timestep, TransformComponent& cameraTransform)
{
auto& cameraTransform = m_Registry.get<TransformComponent>(camera);
ZoneScopedNC("Snow::OnUpdate", 0x00ff00);
auto& vertex1 = m_SysDescription.m_Vertex1.value();
auto& vertex2 = m_SysDescription.m_Vertex2.value();
for (auto& particle : m_ParticlePool)
{
auto& transform = m_Registry.get<TransformComponent>(particle.m_Entity);
auto& transform = *particle.m_Transform;
float rotationSpeedZ = particle.m_RotationSpeed.z;
transform.AddRotation({0.0f, 0.0f, timestep * rotationSpeedZ});
transform.SetRotationY(cameraTransform.GetRotation().y);
Expand Down Expand Up @@ -184,11 +206,7 @@ namespace GfxRenderEngine
{
CORE_ASSERT((sceneObject.value().type() == ondemand::json_type::number), "type must be number");
int64 poolSize = sceneObject.value().get_int64();
#ifdef DEBUG
m_SysDescription.m_PoolSize = std::min(poolSize, 500L);
#else
m_SysDescription.m_PoolSize = poolSize;
#endif
}
else if (sceneObjectKey == "prefix dictionary")
{
Expand Down
5 changes: 3 additions & 2 deletions application/lucre/particleSystem/snow.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "engine.h"
#include "scene/scene.h"
#include "scene/components.h"
#include "auxiliary/timestep.h"

namespace GfxRenderEngine
Expand All @@ -33,7 +34,7 @@ namespace GfxRenderEngine

public:
Snow(Scene& scene, std::string const& jsonFile);
void OnUpdate(Timestep timestep, entt::entity camera);
void OnUpdate(Timestep timestep, TransformComponent& cameraTransform);

private:
struct SysDescription
Expand All @@ -49,7 +50,7 @@ namespace GfxRenderEngine
{
glm::vec3 m_Velocity;
glm::vec3 m_RotationSpeed;
entt::entity m_Entity;
TransformComponent* m_Transform;
};

private:
Expand Down
21 changes: 18 additions & 3 deletions application/lucre/scenes/volcanoScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,24 @@ namespace LucreApp
m_CharacterAnimation->OnUpdate(timestep);
}

for (auto& snowParticleSystem : m_SnowParticleSystems)
{
snowParticleSystem.OnUpdate(timestep, m_Camera);
{ // update particle systems
auto& threadpool = Engine::m_Engine->m_PoolPrimary;
auto& cameraTransform = m_Registry.get<TransformComponent>(m_Camera);
uint index{0};
for (auto& snowParticleSystem : m_SnowParticleSystems)
{
auto task = [&]()
{
snowParticleSystem.OnUpdate(timestep, cameraTransform);
return true;
};
m_Futures[index] = threadpool.SubmitTask(task);
++index;
}
for (auto& future : m_Futures)
{
future.get();
}
}

if (m_Water != entt::null)
Expand Down
1 change: 1 addition & 0 deletions application/lucre/scenes/volcanoScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ namespace LucreApp
std::unique_ptr<CharacterAnimation> m_CharacterAnimation;
static constexpr uint NUM_SNOW_PARTICLE_SYSTEMS = 4;
Snow m_SnowParticleSystems[NUM_SNOW_PARTICLE_SYSTEMS];
std::array<std::future<bool>, NUM_SNOW_PARTICLE_SYSTEMS> m_Futures;

private:
struct Group2
Expand Down
5 changes: 4 additions & 1 deletion engine/platform/Vulkan/VKrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,10 @@ namespace GfxRenderEngine
{
if (m_CurrentCommandBuffer)
{
UpdateTransformCache(scene, SceneGraph::ROOT_NODE, glm::mat4(1.0f), false);
{
ZoneScopedNC("UpdateTransformCache", 0xffff00);
UpdateTransformCache(scene, SceneGraph::ROOT_NODE, glm::mat4(1.0f), false);
}

auto& registry = scene.GetRegistry();

Expand Down
38 changes: 30 additions & 8 deletions engine/renderer/builder/fastgltfBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ namespace GfxRenderEngine
{
FastgltfBuilder::FastgltfBuilder(const std::string& filepath, Scene& scene, Resources::ResourceBuffers* resourceBuffers)
: m_Filepath{filepath}, m_SkeletalAnimation{false}, m_Registry{scene.GetRegistry()},
m_SceneGraph{scene.GetSceneGraph()}, m_Dictionary{scene.GetDictionary()}
m_SceneGraph{scene.GetSceneGraph()}, m_Dictionary{scene.GetDictionary()},
m_Basepath{EngineCore::GetPathWithoutFilename(filepath)}, m_GroupNode{Gltf::GLTF_NOT_USED}
{
m_Basepath = EngineCore::GetPathWithoutFilename(filepath);
if (resourceBuffers)
{
// optionally: additional resources not originating from a 3D file can be provided here
Expand All @@ -48,6 +48,13 @@ namespace GfxRenderEngine
}
}

FastgltfBuilder::FastgltfBuilder(const std::string& filepath, Scene& scene, int groupNode)
: m_Filepath{filepath}, m_SkeletalAnimation{false}, m_Registry{scene.GetRegistry()},
m_SceneGraph{scene.GetSceneGraph()}, m_Dictionary{scene.GetDictionary()},
m_Basepath{EngineCore::GetPathWithoutFilename(filepath)}, m_GroupNode{groupNode}
{
}

bool FastgltfBuilder::Load(uint const instanceCount, int const sceneID)
{
PROFILE_SCOPE("FastgltfBuilder::Load");
Expand Down Expand Up @@ -126,14 +133,21 @@ namespace GfxRenderEngine
m_InstanceCount = instanceCount;
for (uint instanceIndex = 0; instanceIndex < m_InstanceCount; ++instanceIndex)
{
// create group game object(s) for all instances to apply transform from JSON file to
auto entity = m_Registry.Create();
auto name = m_DictionaryPrefix + "::" + m_Filepath + "::" + std::to_string(instanceIndex) + "::root";
uint groupNode = m_SceneGraph.CreateNode(SceneGraph::ROOT_NODE, entity, name, m_Dictionary);
uint groupNode;
if (m_GroupNode == Gltf::GLTF_NOT_USED)
{ // create group game object(s) for all instances to apply transform from JSON file to
auto entity = m_Registry.Create();
auto name = m_DictionaryPrefix + "::" + m_Filepath + "::" + std::to_string(instanceIndex) + "::root";
groupNode = m_SceneGraph.CreateNode(SceneGraph::ROOT_NODE, entity, name, m_Dictionary);

{
TransformComponent transform{};
m_Registry.emplace<TransformComponent>(entity, transform);
}
}
else
{
TransformComponent transform{};
m_Registry.emplace<TransformComponent>(entity, transform);
groupNode = static_cast<uint>(m_GroupNode);
}

// a scene ID was provided
Expand All @@ -152,6 +166,13 @@ namespace GfxRenderEngine
return Gltf::GLTF_LOAD_SUCCESS;
}

bool FastgltfBuilder::Load(uint const instanceCount, std::vector<entt::entity>& firstInstances)
{
bool returnValue = Load(instanceCount);
firstInstances = m_FirstInstances;
return returnValue;
}

bool FastgltfBuilder::MarkNode(int const gltfNodeIndex)
{
// each recursive call of this function marks a node in "m_HasMesh" if itself or a child has a mesh
Expand Down Expand Up @@ -287,6 +308,7 @@ namespace GfxRenderEngine
{
std::lock_guard<std::mutex> guard(m_Mutex);
m_InstancedObjects[gltfNodeIndex] = entity;
m_FirstInstances.push_back(entity);
}

// create model for 1st instance
Expand Down
4 changes: 4 additions & 0 deletions engine/renderer/builder/fastgltfBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ namespace GfxRenderEngine
public:
FastgltfBuilder() = delete;
FastgltfBuilder(const std::string& filepath, Scene& scene, Resources::ResourceBuffers* resourceBuffers = nullptr);
FastgltfBuilder(const std::string& filepath, Scene& scene, int groupNode);

bool Load(uint const instanceCount = 1, int const sceneID = Gltf::GLTF_NOT_USED);
bool Load(uint const instanceCount, std::vector<entt::entity>& firstInstances);
void SetDictionaryPrefix(std::string const&);

private:
Expand Down Expand Up @@ -123,6 +125,8 @@ namespace GfxRenderEngine

// scene graph
uint m_InstanceCount{0};
std::vector<entt::entity> m_FirstInstances;
int m_GroupNode;
std::vector<bool> m_HasMesh;
Atomic::Queue<std::future<bool>> m_NodeFuturesQueue;
std::unordered_map<int, entt::entity> m_InstancedObjects;
Expand Down

0 comments on commit 2058710

Please sign in to comment.