Skip to content

Commit

Permalink
feat(ECS): Added support for serialisation names
Browse files Browse the repository at this point in the history
Add support for ECS serialisation names.
  • Loading branch information
capnkenny authored Apr 25, 2022
2 parents c0cd932 + adcd3f4 commit d81cbc2
Show file tree
Hide file tree
Showing 24 changed files with 138 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ extern "C"
void* deleteInstructionState,
size_t sizeOfDataTypeInBytes,
NrtComponentUpdateFnPtr fnPtr,
const char* serialisedTypeName,
void* context);

void Nrt_ComponentBufferMemoryContainer_PrepContainerForFrame(NrtComponentBufferMemoryContainerHandle container,
Expand Down
1 change: 1 addition & 0 deletions include/NovelRT.Interop/Ecs/NrtComponentCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extern "C"
size_t sizeOfDataType,
const void* deleteInstructionState,
NrtComponentUpdateFnPtr updateFnPtr,
const char* serialisedTypeName,
void* context,
NrtComponentTypeId* outputResult);

Expand Down
16 changes: 14 additions & 2 deletions include/NovelRT/Ecs/ComponentBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,20 @@ namespace NovelRT::Ecs
*
*
* @param poolSize The amount of worker threads being utilised in this instance of the ECS.
* @param serialisedTypeName The type name to use for serialisation of this data type.
* @param deleteInstructionState The component state to treat as the delete instruction. When this state is
* passed in during an update, the ComponentBuffer will delete the component from the target entity during
* resolution.
*/
ComponentBuffer(size_t poolSize, T deleteInstructionState) noexcept
ComponentBuffer(size_t poolSize, T deleteInstructionState, const std::string& serialisedTypeName) noexcept
: _innerContainer(std::make_shared<ComponentBufferMemoryContainer>(
poolSize,
&deleteInstructionState,
sizeof(T),
[](auto rootComponent, auto updateComponent, auto) {
*reinterpret_cast<T*>(rootComponent) += *reinterpret_cast<const T*>(updateComponent);
}))
},
serialisedTypeName))
{
static_assert(std::is_trivially_copyable<T>::value,
"Value type must be trivially copyable for use with a ComponentBuffer. See the documentation "
Expand Down Expand Up @@ -197,6 +199,16 @@ namespace NovelRT::Ecs
return _innerContainer->HasComponent(entity);
}

/**
* @brief Gets the serialised type name used for the loading and unloading to and from serialised data.
*
* @return The serialised type name as a string.
*/
[[nodiscard]] const std::string& GetSerialisedTypeName() const noexcept
{
return _innerContainer->GetSerialisedTypeName();
}

/**
* @brief Gets the length of the current immutable data snapshot within the buffer.
*
Expand Down
9 changes: 8 additions & 1 deletion include/NovelRT/Ecs/ComponentBufferMemoryContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace NovelRT::Ecs
std::vector<uint8_t> _deleteInstructionState;
size_t _sizeOfDataTypeInBytes;
std::function<void(void*, const void*, size_t)> _componentUpdateLogic;
std::string _serialisedTypeName;

public:
class ImmutableDataView
Expand Down Expand Up @@ -46,7 +47,8 @@ namespace NovelRT::Ecs
ComponentBufferMemoryContainer(size_t poolSize,
const void* deleteInstructionState,
size_t sizeOfDataTypeInBytes,
std::function<void(void*, const void*, size_t)> componentUpdateLogic) noexcept;
std::function<void(void*, const void*, size_t)> componentUpdateLogic,
const std::string& serialisedTypeName) noexcept;

void PrepContainerForFrame(const std::vector<EntityId>& destroyedEntities) noexcept;

Expand All @@ -61,6 +63,11 @@ namespace NovelRT::Ecs

[[nodiscard]] size_t GetImmutableDataLength() const noexcept;

[[nodiscard]] inline const std::string& GetSerialisedTypeName() const noexcept
{
return _serialisedTypeName;
}

[[nodiscard]] SparseSetMemoryContainer::ConstIterator begin() const noexcept;

[[nodiscard]] SparseSetMemoryContainer::ConstIterator end() const noexcept;
Expand Down
17 changes: 11 additions & 6 deletions include/NovelRT/Ecs/ComponentCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ namespace NovelRT::Ecs
std::shared_ptr<ComponentBufferMemoryContainer> CreateContainer(
size_t sizeOfDataType,
const void* deleteInstructionState,
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic) const;
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic,
const std::string& serialisedTypeName) const;

public:
/**
Expand All @@ -49,6 +50,7 @@ namespace NovelRT::Ecs
* @param sizeOfDataType The size of the object type, in bytes.
* @param deleteInstructionState The object state that indicates that the component should be deleted.
* @param componentUpdateLogic The function to use for concurrent update consolidation.
* @param serialisedTypeName The type name to use for data serialisation.
* @return the ID of the new component type and associated ComponentBufferMemoryContainer
* instance.
*
Expand All @@ -58,7 +60,8 @@ namespace NovelRT::Ecs
[[nodiscard]] ComponentTypeId RegisterComponentTypeUnsafe(
size_t sizeOfDataType,
const void* deleteInstructionState,
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic);
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic,
const std::string& serialisedTypeName);

/**
* @brief Registers a new component type to the cache.
Expand All @@ -74,12 +77,14 @@ namespace NovelRT::Ecs
* @exception std::bad_alloc when a ComponentBuffer could not be allocated in memory for the given component
* type.
*/
template<typename T> void RegisterComponentType(T deleteInstructionState)
template<typename T> void RegisterComponentType(T deleteInstructionState, const std::string& serialisedTypeName)
{
std::shared_ptr<ComponentBufferMemoryContainer> ptr =
CreateContainer(sizeof(T), &deleteInstructionState, [](auto rootComponent, auto updateComponent, auto) {
std::shared_ptr<ComponentBufferMemoryContainer> ptr = CreateContainer(
sizeof(T), &deleteInstructionState,
[](auto rootComponent, auto updateComponent, auto) {
*reinterpret_cast<T*>(rootComponent) += *reinterpret_cast<const T*>(updateComponent);
});
},
serialisedTypeName);
_bufferPrepEvent += [ptr](auto vec) { ptr->PrepContainerForFrame(vec); };
_componentMap.emplace(GetComponentTypeId<T>(), ptr);
}
Expand Down
32 changes: 21 additions & 11 deletions include/NovelRT/Ecs/Configurator.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,28 @@ namespace NovelRT::Ecs

inline void AddDefaultComponentsAndSystems(SystemScheduler& target)
{
target.GetComponentCache().RegisterComponentType(Graphics::RenderComponent{0, 0, 0, 0, true});
target.GetComponentCache().RegisterComponentType(Graphics::RenderComponent{0, 0, 0, 0, true},
"NovelRT::Ecs::Graphics::RenderComponent");

target.GetComponentCache().RegisterComponentType(EntityGraphComponent{
false, std::numeric_limits<EntityId>::max(), std::numeric_limits<EntityId>::max()});
target.GetComponentCache().RegisterComponentType(
EntityGraphComponent{false, std::numeric_limits<EntityId>::max(), std::numeric_limits<EntityId>::max()},
"NovelRT::Ecs::EntityGraphComponent");

target.GetComponentCache().RegisterComponentType(LinkedEntityListNodeComponent{
false, std::numeric_limits<EntityId>::max(), std::numeric_limits<EntityId>::max()});
target.GetComponentCache().RegisterComponentType(
LinkedEntityListNodeComponent{false, std::numeric_limits<EntityId>::max(),
std::numeric_limits<EntityId>::max()},
"NovelRT::Ecs::LinkedEntityListNodeComponent");

target.GetComponentCache().RegisterComponentType(
TransformComponent{Maths::GeoVector3F::uniform(NAN), Maths::GeoVector2F::uniform(NAN), NAN});
TransformComponent{Maths::GeoVector3F::uniform(NAN), Maths::GeoVector2F::uniform(NAN), NAN},
"NovelRT::Ecs::TransformComponent");

target.RegisterSystem(std::make_shared<Ecs::Graphics::DefaultRenderingSystem>(
_graphicsPluginProvider, _windowingPluginProvider, _resourceManagementPluginProvider));

target.GetComponentCache().RegisterComponentType(
Ecs::Input::InputEventComponent{0, NovelRT::Input::KeyState::Idle, 0, 0});
Input::InputEventComponent{0, NovelRT::Input::KeyState::Idle, 0, 0},
"NovelRT::Ecs::Input::InputEventComponent");

target.RegisterSystem(
std::make_shared<Ecs::Input::InputSystem>(_windowingPluginProvider, _inputPluginProvider));
Expand Down Expand Up @@ -179,12 +185,14 @@ namespace NovelRT::Ecs
* This is the final method you should call to obtain the ECS instance.
*
* @tparam TComponentTypes List of component types to register with this ECS instance.
* @param deleteInstructionStates The state of the given component type that signals this component is to be
* deleted to the ECS.
* @tparam Names List of the names to used for type serialisation.
* @param deleteInstructionStatesAndSerialisedTypeNames The state of the given component type that signals this
* component is to be, accompanied by the serialised type name. deleted to the ECS.
* @returns An instance of the ECS SystemScheduler root object based on the provided configuration.
*/
template<typename... TComponentTypes>
[[nodiscard]] SystemScheduler InitialiseAndRegisterComponents(TComponentTypes... deleteInstructionStates)
[[nodiscard]] SystemScheduler InitialiseAndRegisterComponents(
std::tuple<TComponentTypes, std::string>... deleteInstructionStatesAndSerialisedTypeNames)
{
SystemScheduler scheduler(_threadCount.value_or(0));

Expand All @@ -198,7 +206,9 @@ namespace NovelRT::Ecs
scheduler.RegisterSystem(system);
}

scheduler.GetComponentCache().RegisterComponentType<TComponentTypes...>(deleteInstructionStates...);
scheduler.GetComponentCache().RegisterComponentType<TComponentTypes...>(
std::get<0>(deleteInstructionStatesAndSerialisedTypeNames)...,
std::get<1>(deleteInstructionStatesAndSerialisedTypeNames)...);
scheduler.SpinThreads();

return scheduler;
Expand Down
5 changes: 3 additions & 2 deletions src/NovelRT.Interop/Ecs/Audio/NrtAudioSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ extern "C"

auto sys = reinterpret_cast<Ecs::SystemScheduler*>(system);
auto deleteState = Ecs::Audio::AudioEmitterComponent();
sys->GetComponentCache().RegisterComponentType(deleteState);
sys->GetComponentCache().RegisterComponentType(deleteState, "NovelRT::Ecs::Audio::AudioEmitterComponent");
sys->GetComponentCache().RegisterComponentType(
Ecs::Audio::AudioEmitterStateComponent{Ecs::Audio::AudioEmitterState::Done});
Ecs::Audio::AudioEmitterStateComponent{Ecs::Audio::AudioEmitterState::Done},
"NovelRT::Ecs::Audio::AudioEmitterStateComponent");
return NRT_SUCCESS;
}

Expand Down
5 changes: 3 additions & 2 deletions src/NovelRT.Interop/Ecs/NrtComponentBufferMemoryContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ extern "C"
void* deleteInstructionState,
size_t sizeOfDataTypeInBytes,
NrtComponentUpdateFnPtr fnPtr,
const char* serialisedTypeName,
void* context)
{
auto func = [=](void* lhs, const void* rhs, size_t size) { fnPtr(lhs, rhs, size, context); };

return reinterpret_cast<NrtComponentBufferMemoryContainerHandle>(
new ComponentBufferMemoryContainer(poolSize, deleteInstructionState, sizeOfDataTypeInBytes, func));
return reinterpret_cast<NrtComponentBufferMemoryContainerHandle>(new ComponentBufferMemoryContainer(
poolSize, deleteInstructionState, sizeOfDataTypeInBytes, func, std::string(serialisedTypeName)));
}

// TODO: Not sure if I should add safety here?
Expand Down
7 changes: 5 additions & 2 deletions src/NovelRT.Interop/Ecs/NrtComponentCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extern "C"
size_t sizeOfDataType,
const void* deleteInstructionState,
NrtComponentUpdateFnPtr updateFnPtr,
const char* serialisedTypeName,
void* context,
NrtComponentTypeId* outputResult)
{
Expand All @@ -35,11 +36,13 @@ extern "C"
*outputResult =
reinterpret_cast<ComponentCache*>(componentCache)
->RegisterComponentTypeUnsafe(
sizeOfDataType, deleteInstructionState, [=](auto lhs, auto rhs, auto size) {
sizeOfDataType, deleteInstructionState,
[=](auto lhs, auto rhs, auto size) {
updateFnPtr(reinterpret_cast<NrtSparseSetMemoryContainer_ByteIteratorViewHandle>(&lhs),
reinterpret_cast<NrtSparseSetMemoryContainer_ByteIteratorViewHandle>(&rhs),
size, context);
});
},
std::string(serialisedTypeName));

return NRT_SUCCESS;
}
Expand Down
6 changes: 4 additions & 2 deletions src/NovelRT/Ecs/ComponentBufferMemoryContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ namespace NovelRT::Ecs
size_t poolSize,
const void* deleteInstructionState,
size_t sizeOfDataTypeInBytes,
std::function<void(void*, const void*, size_t)> componentUpdateLogic) noexcept
std::function<void(void*, const void*, size_t)> componentUpdateLogic,
const std::string& serialisedTypeName) noexcept
: _rootSet(SparseSetMemoryContainer(sizeOfDataTypeInBytes)),
_updateSets(std::vector<SparseSetMemoryContainer>{}),
_deleteInstructionState(std::vector<uint8_t>(sizeOfDataTypeInBytes)),
_sizeOfDataTypeInBytes(sizeOfDataTypeInBytes),
_componentUpdateLogic(std::move(componentUpdateLogic))
_componentUpdateLogic(std::move(componentUpdateLogic)),
_serialisedTypeName(serialisedTypeName)
{
std::memcpy(_deleteInstructionState.data(), deleteInstructionState, _sizeOfDataTypeInBytes);
for (size_t i = 0; i < poolSize; i++)
Expand Down
10 changes: 6 additions & 4 deletions src/NovelRT/Ecs/ComponentCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,24 @@ namespace NovelRT::Ecs
std::shared_ptr<ComponentBufferMemoryContainer> ComponentCache::CreateContainer(
size_t sizeOfDataType,
const void* deleteInstructionState,
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic) const
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic,
const std::string& serialisedTypeName) const
{
return std::make_shared<ComponentBufferMemoryContainer>(_poolSize, deleteInstructionState, sizeOfDataType,
componentUpdateLogic);
componentUpdateLogic, serialisedTypeName);
}

ComponentTypeId ComponentCache::RegisterComponentTypeUnsafe(
size_t sizeOfDataType,
const void* deleteInstructionState,
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic)
const std::function<void(void*, const void*, size_t)>& componentUpdateLogic,
const std::string& serialisedTypeName)
{
static AtomFactory& _componentTypeIdFactory = AtomFactoryDatabase::GetFactory("ComponentTypeId");

ComponentTypeId returnId = _componentTypeIdFactory.GetNext();
std::shared_ptr<ComponentBufferMemoryContainer> ptr =
CreateContainer(sizeOfDataType, deleteInstructionState, componentUpdateLogic);
CreateContainer(sizeOfDataType, deleteInstructionState, componentUpdateLogic, serialisedTypeName);
_bufferPrepEvent += [ptr](auto vec) { ptr->PrepContainerForFrame(vec); };
_componentMap.emplace(returnId, ptr);
return returnId;
Expand Down
6 changes: 3 additions & 3 deletions tests/NovelRT.Tests/Ecs/CatalogueTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ class CatalogueTest : public testing::Test
{
componentCache = ComponentCache(1);
entityCache = EntityCache(1);
componentCache.RegisterComponentType<int32_t>(-1);
componentCache.RegisterComponentType<size_t>(-1);
componentCache.RegisterComponentType<char>('e');
componentCache.RegisterComponentType<int32_t>(-1, "THROW_AWAY");
componentCache.RegisterComponentType<size_t>(-1, "THROW_AWAY_AGAIN");
componentCache.RegisterComponentType<char>('e', "THROW_AWAY_AGAIN_AGAIN");

componentCache.GetComponentBuffer<int32_t>().PushComponentUpdateInstruction(0, 0, 10);
componentCache.GetComponentBuffer<size_t>().PushComponentUpdateInstruction(0, 0, 100);
Expand Down
Loading

0 comments on commit d81cbc2

Please sign in to comment.