diff --git a/source/code/core/collections/public/ice/sort.hxx b/source/code/core/collections/public/ice/sort.hxx index 15c31375..66e8949d 100644 --- a/source/code/core/collections/public/ice/sort.hxx +++ b/source/code/core/collections/public/ice/sort.hxx @@ -122,6 +122,20 @@ namespace ice return false; } + template + constexpr bool search_with(ice::Span values, Comp&& comp, ice::ucount& out_index, U const&... params) noexcept + { + for (ice::u32 idx = 0; idx < ice::span::count(values); ++idx) + { + if (ice::forward(comp)(values[idx], idx, params...)) + { + out_index = idx; + return true; + } + } + return false; + } + namespace detail { diff --git a/source/code/core/devui/private/devui_widget.cxx b/source/code/core/devui/private/devui_widget.cxx index 0eb4fe6c..8392753b 100644 --- a/source/code/core/devui/private/devui_widget.cxx +++ b/source/code/core/devui/private/devui_widget.cxx @@ -9,13 +9,13 @@ namespace ice { DevUIWidget::DevUIWidget(ice::DevUIWidgetInfo const& info) noexcept - : info{ info } + : widget_info{ info } { } void DevUIWidget::build_widget(ice::DevUIFrame& frame, ice::DevUIWidgetState& state) noexcept { - if (frame.begin(info, state)) + if (frame.begin(widget_info, state)) { this->build_menu(); this->build_content(); diff --git a/source/code/core/devui/public/ice/devui_widget.hxx b/source/code/core/devui/public/ice/devui_widget.hxx index 4f1382bd..a7874db8 100644 --- a/source/code/core/devui/public/ice/devui_widget.hxx +++ b/source/code/core/devui/public/ice/devui_widget.hxx @@ -30,7 +30,7 @@ namespace ice virtual bool build_mainmenu(ice::DevUIWidgetState& state) noexcept; - ice::DevUIWidgetInfo const info; + ice::DevUIWidgetInfo const widget_info; }; } // namespace ice diff --git a/source/code/core/math/public/ice/math/matrix.hxx b/source/code/core/math/public/ice/math/matrix.hxx index ab15a569..1334e9cd 100644 --- a/source/code/core/math/public/ice/math/matrix.hxx +++ b/source/code/core/math/public/ice/math/matrix.hxx @@ -11,12 +11,25 @@ namespace ice::math struct mat { using value_type = T; - static constexpr auto count_rows = Rows; - static constexpr auto count_columns = Cols; + static constexpr u32 count_rows = Rows; + static constexpr u32 count_columns = Cols; T v[count_columns][count_rows]; }; + template + struct mat<2, 2, T> + { + using value_type = T; + static constexpr u32 count_rows = 3; + static constexpr u32 count_columns = 3; + + T v[count_columns][count_rows]; + + template + constexpr operator mat<3, 3, U>() noexcept; + }; + template constexpr auto identity() noexcept; @@ -31,6 +44,18 @@ namespace ice::math using mat4x4 = mat<4, 4, f32>; using mat4 = mat4x4; + template + template + constexpr mat<2, 2, T>::operator mat<3, 3, U>() noexcept + { + mat<3, 3, U> result; + result.v[0][0] = v[0][0]; + result.v[0][1] = v[0][1]; + result.v[1][0] = v[1][0]; + result.v[1][1] = v[1][1]; + result.v[2][2] = 1; + return result; + } template constexpr auto identity() noexcept diff --git a/source/code/core/math/public/ice/math/rotate.hxx b/source/code/core/math/public/ice/math/rotate.hxx index 65285f69..7e9e6bf3 100644 --- a/source/code/core/math/public/ice/math/rotate.hxx +++ b/source/code/core/math/public/ice/math/rotate.hxx @@ -9,18 +9,38 @@ namespace ice::math { + inline auto rotate2d(rad rad) noexcept -> mat<2, 2, f32>; inline auto rotate(rad rad, vec<3, f32> v) noexcept -> mat<4, 4, f32>; + inline auto rotate2d(mat<2, 2, f32> left, rad rad) noexcept -> mat<2, 2, f32>; inline auto rotate(mat<4, 4, f32> left, rad rad, vec<3, f32> v) noexcept -> mat<4, 4, f32>; inline auto rotation(mat<4, 4, f32> const& matrix) noexcept -> vec<3, rad>; + inline auto rotate2d(rad rad) noexcept -> mat<2, 2, f32> + { + return rotate2d(mat2x2_identity, rad); + } + inline auto rotate(rad rad, vec<3, f32> v) noexcept -> mat<4, 4, f32> { return rotate(mat4x4_identity, rad, v); } + inline auto rotate2d(mat<2, 2, f32> left, rad rad) noexcept -> mat<2, 2, f32> + { + f32 const cosv = cos(rad); + f32 const sinv = sin(rad); + + mat<2, 2, f32> rm = mat2x2_identity; + rm.v[0][0] = cosv; + rm.v[0][1] = sinv; + rm.v[1][0] = -sinv; + rm.v[1][1] = cosv; + return mul(left, rm); + } + inline auto rotate(mat<4, 4, f32> left, rad rad, vec<3, f32> v) noexcept -> mat<4, 4, f32> { f32 const cosv = cos(rad); diff --git a/source/code/core/math/public/ice/math/scale.hxx b/source/code/core/math/public/ice/math/scale.hxx index 3a3407b5..23635be7 100644 --- a/source/code/core/math/public/ice/math/scale.hxx +++ b/source/code/core/math/public/ice/math/scale.hxx @@ -9,18 +9,33 @@ namespace ice::math { + constexpr auto scale2d(vec<2, f32> v) noexcept -> mat<2, 2, f32>; constexpr auto scale(vec<3, f32> v) noexcept -> mat<4, 4, f32>; + constexpr auto scale2d(mat<2, 2, f32> left, vec<2, f32> right) noexcept -> mat<2, 2, f32>; constexpr auto scale(mat<4, 4, f32> left, vec<3, f32> right) noexcept -> mat<4, 4, f32>; constexpr auto scale(mat<4, 4, f32> const& matrix) noexcept -> vec<3, f32>; + constexpr auto scale2d(vec<2, f32> v) noexcept -> mat<2, 2, f32> + { + return scale2d(mat2x2_identity, v); + } + constexpr auto scale(vec<3, f32> v) noexcept -> mat<4, 4, f32> { return scale(mat4x4_identity, v); } + constexpr auto scale2d(mat<2, 2, f32> left, vec<2, f32> right) noexcept -> mat<2, 2, f32> + { + mat<2, 2, f32> temp{ }; + temp.v[0][0] = right.v[0][0]; + temp.v[1][1] = right.v[0][1]; + return mul(left, temp); + } + constexpr auto scale(mat<4, 4, f32> left, vec<3, f32> right) noexcept -> mat<4, 4, f32> { mat<4, 4, f32> temp{ }; diff --git a/source/code/core/math/public/ice/math/translate.hxx b/source/code/core/math/public/ice/math/translate.hxx index 531665f9..77538c98 100644 --- a/source/code/core/math/public/ice/math/translate.hxx +++ b/source/code/core/math/public/ice/math/translate.hxx @@ -7,18 +7,33 @@ namespace ice::math { + constexpr auto translate(vec<2, f32> displacement) noexcept -> mat<3, 3, f32>; constexpr auto translate(vec<3, f32> displacement) noexcept -> mat<4, 4, f32>; + constexpr auto translate(mat<3, 3, f32> left, vec<2, f32> right) noexcept -> mat<3, 3, f32>; constexpr auto translate(mat<4, 4, f32> left, vec<3, f32> right) noexcept -> mat<4, 4, f32>; + constexpr auto translation(mat<3, 3, f32> const& matrix) noexcept -> vec<2, f32>; constexpr auto translation(mat<4, 4, f32> const& matrix) noexcept -> vec<3, f32>; + constexpr auto translate(vec<2, f32> displacement) noexcept -> mat<3, 3, f32> + { + return translate(mat3x3_identity, displacement); + } + constexpr auto translate(vec<3, f32> displacement) noexcept -> mat<4, 4, f32> { return translate(mat4x4_identity, displacement); } + constexpr auto translate(mat<3, 3, f32> left, vec<2, f32> right) noexcept -> mat<3, 3, f32> + { + left.v[2][0] += right.v[0][0]; + left.v[2][1] += right.v[0][1]; + return left; + } + constexpr auto translate(mat<4, 4, f32> left, vec<3, f32> right) noexcept -> mat<4, 4, f32> { left.v[3][0] += right.v[0][0]; @@ -27,6 +42,11 @@ namespace ice::math return left; } + constexpr auto translation(mat<3, 3, f32> const& matrix) noexcept -> vec<2, f32> + { + return { matrix.v[2][0], matrix.v[2][1] }; + } + constexpr auto translation(mat<4, 4, f32> const& matrix) noexcept -> vec<3, f32> { return { matrix.v[3][0], matrix.v[3][1], matrix.v[3][2] }; diff --git a/source/code/core/math/public/ice/math/vector.hxx b/source/code/core/math/public/ice/math/vector.hxx index 1c35194f..a32a282c 100644 --- a/source/code/core/math/public/ice/math/vector.hxx +++ b/source/code/core/math/public/ice/math/vector.hxx @@ -61,6 +61,10 @@ namespace ice::math : v{ { static_cast(other.x), static_cast(other.y) } } { } + constexpr explicit mat(T const(&array)[3]) noexcept + : v{ { array[0] / array[2], array[1] / array[2] } } + { } + union { T v[count_columns][count_rows]; diff --git a/source/code/core/utils/public/ice/algorithm.hxx b/source/code/core/utils/public/ice/algorithm.hxx new file mode 100644 index 00000000..169e1696 --- /dev/null +++ b/source/code/core/utils/public/ice/algorithm.hxx @@ -0,0 +1,17 @@ +/// Copyright 2025 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include +#include + +namespace ice +{ + + template + constexpr auto accumulate(ice::Span range, U val) noexcept + { + return ::std::accumulate(ice::begin(range), ice::end(range), val); + } + +} // namespace ice diff --git a/source/code/example/webasm/private/example_webasm.cxx b/source/code/example/webasm/private/example_webasm.cxx index 2e084f0e..71361ef0 100644 --- a/source/code/example/webasm/private/example_webasm.cxx +++ b/source/code/example/webasm/private/example_webasm.cxx @@ -113,7 +113,7 @@ struct TestTrait : public ice::Trait ice::Timer timer; using TestArchetype = ice::ecs::ArchetypeDefinition; - static constexpr ice::ecs::ArchetypeDefinition Archetype_TestArchetype = TestArchetype{}; + static constexpr ice::ecs::ArchetypeDefinition Archetype_TestArchetype = TestArchetype{ "test-arch" }; ice::ecs::EntityOperations* _ops; ice::ecs::Entity _my_entity[10000]; @@ -123,8 +123,7 @@ struct TestTrait : public ice::Trait update.engine.entity_index().create_many(_my_entity); _ops = ice::addressof(update.world.entity_operations()); - - ice::ecs::queue_set_archetype(*_ops, _my_entity, Archetype_TestArchetype); + _ops->set("test-arch", _my_entity); ICE_LOG(LogSeverity::Retail, LogTag::Game, "Test Activated!"); timer = ice::timer::create_timer(update.clock, 100_Tms); @@ -138,7 +137,7 @@ struct TestTrait : public ice::Trait query().tags().for_each_block( [&](ice::ucount count, ice::ecs::Entity const* entities) noexcept { - ice::ecs::queue_batch_remove_entities(*_ops, { entities, count }); + _ops->destroy({ entities, count }); } ); @@ -187,9 +186,7 @@ struct TestTrait : public ice::Trait ICE_LOG(LogSeverity::Info, LogTag::Game, "TestTrait::logic {}", x.load()); } - ICE_LOG(LogSeverity::Debug, LogTag::Game, "{}", std::hash{}(std::this_thread::get_id())); co_await ice::await_scheduled_on(tasks, update.thread.tasks, update.thread.main); - ICE_LOG(LogSeverity::Debug, LogTag::Game, "{}", std::hash{}(std::this_thread::get_id())); co_return; } diff --git a/source/code/framework/framework_base/private/framework_main.cxx b/source/code/framework/framework_base/private/framework_main.cxx index 51fa36a6..ba94bad5 100644 --- a/source/code/framework/framework_base/private/framework_main.cxx +++ b/source/code/framework/framework_base/private/framework_main.cxx @@ -575,6 +575,8 @@ auto ice_game_frame( state.platform.core->refresh_events(); ice::ShardContainer const& system_events = state.platform.core->system_events(); + // Update the system clock + ice::clock::update(runtime.clock); ice::clock::update(runtime.game_clock); ice::UniquePtr new_frame = co_await logic.aquire_frame(); @@ -636,9 +638,6 @@ auto ice_update( ice::app::Runtime& runtime ) noexcept -> ice::Result { - // Update the system clock - ice::clock::update(runtime.clock); - // Process any awaiting main thread tasks. runtime.main_queue.process_all(); diff --git a/source/code/iceshard/engine/private/ecs/ecs_archetype_index.cxx b/source/code/iceshard/engine/private/ecs/ecs_archetype_index.cxx index fb651ee2..9c59b973 100644 --- a/source/code/iceshard/engine/private/ecs/ecs_archetype_index.cxx +++ b/source/code/iceshard/engine/private/ecs/ecs_archetype_index.cxx @@ -28,11 +28,11 @@ namespace ice::ecs return base_offset; } - auto contains_required_components( + bool contains_required_components( ice::Span in_conditions, ice::Span in_required_tags, ice::Span checked_identifiers - ) noexcept -> ice::u32 + ) noexcept { using QueryTypeInfo = ice::ecs::detail::QueryTypeInfo; @@ -65,6 +65,7 @@ namespace ice::ecs } // As long as we have something to check and we did not fail search for the next ID + [[maybe_unused]] ice::u32 matched_components = 0; // Reset for checking the remaining conditions @@ -93,7 +94,7 @@ namespace ice::ecs matched_components += (identifier_hash == condition_hash); } - return result ? matched_components : 0; + return result; } } // namespace detail @@ -171,6 +172,7 @@ namespace ice::ecs auto ArchetypeIndex::register_archetype( ice::ecs::ArchetypeInfo const& archetype_info, + ice::ecs::detail::DataBlockFilter data_block_filter, ice::ecs::detail::DataBlockPool* data_block_pool ) noexcept -> ice::ecs::Archetype { @@ -255,6 +257,7 @@ namespace ice::ecs data_header->archetype_name = ice::String{ (char const*) mem_archetype_name.location, ice::size(archetype_info.name) }; data_header->archetype_identifier = archetype_info.identifier; + data_header->archetype_info.data_block_filter = data_block_filter; data_header->archetype_info.component_identifiers = ice::Span{ component_identifiers, component_count }; data_header->archetype_info.component_sizes = ice::Span{ component_sizes, component_count }; data_header->archetype_info.component_alignments = ice::Span{ component_alignments, component_count }; @@ -264,23 +267,11 @@ namespace ice::ecs // We need now to calculate the number of entities that we can store in the remaining memory. // Additionally calculate the offets each component array will be located at. { - ice::u32 const component_size_sum = std::accumulate( - component_sizes, - component_sizes + component_count, - 0 - ); - - ice::u32 const component_alignment_sum = std::accumulate( - component_alignments, - component_alignments + component_count, - 0 + ice::ucount const component_entity_count_max = ice::ecs::detail::calculate_entity_count_for_space( + data_header->archetype_info, + data_block_pool->provided_block_size() ); - ice::usize const block_size = data_block_pool->provided_block_size(); - ice::usize const available_block_size = { block_size.value - component_alignment_sum }; - - data_header->archetype_info.component_entity_count_max = ice::ucount(available_block_size.value / component_size_sum); - ice::u32 next_component_offset = 0; for (ice::u32 idx = 0; idx < component_count; ++idx) { @@ -300,7 +291,7 @@ namespace ice::ecs next_component_offset = ice::ecs::detail::align_forward_u32(next_component_offset, component_alignments[idx]); component_offsets[idx] = next_component_offset; - next_component_offset += component_sizes[idx] * data_header->archetype_info.component_entity_count_max; + next_component_offset += component_sizes[idx] * component_entity_count_max; } } } @@ -377,7 +368,7 @@ namespace ice::ecs continue; } - ice::u32 const matched_components = ice::ecs::detail::contains_required_components( + bool const was_matched = ice::ecs::detail::contains_required_components( query_info, query_tags, archetype_info.component_identifiers @@ -385,7 +376,7 @@ namespace ice::ecs // If we don't match any component in a full optional query, we still skip this archetype. // #todo: we should probably also check for the existance of the EntityHandle in the query. Then the check should be `> 1` - if (matched_components > 0) + if (was_matched) { ice::array::push_back(out_archetypes, entry->archetype_identifier); } diff --git a/source/code/iceshard/engine/private/ecs/ecs_data_block_pool.cxx b/source/code/iceshard/engine/private/ecs/ecs_data_block_pool.cxx index e5ade63b..edb93830 100644 --- a/source/code/iceshard/engine/private/ecs/ecs_data_block_pool.cxx +++ b/source/code/iceshard/engine/private/ecs/ecs_data_block_pool.cxx @@ -2,6 +2,7 @@ /// SPDX-License-Identifier: MIT #include +#include #include namespace ice::ecs::detail @@ -14,7 +15,7 @@ namespace ice::ecs::detail ~DefaultDataBlockPool() noexcept override; auto provided_block_size() const noexcept -> ice::usize override; - auto request_block() noexcept -> ice::ecs::detail::DataBlock* override; + auto request_block(ice::ecs::detail::ArchetypeInstanceInfo const& info) noexcept -> ice::ecs::detail::DataBlock* override; void release_block(ice::ecs::detail::DataBlock* block) noexcept override; private: @@ -51,7 +52,7 @@ namespace ice::ecs::detail return { Constant_DefaultBlockSize.value - ice::size_of.value }; } - auto DefaultDataBlockPool::request_block() noexcept -> ice::ecs::detail::DataBlock* + auto DefaultDataBlockPool::request_block(ice::ecs::detail::ArchetypeInstanceInfo const& info) noexcept -> ice::ecs::detail::DataBlock* { DataBlock* free_block = _free_block_list; if (free_block != nullptr) @@ -61,13 +62,16 @@ namespace ice::ecs::detail if (free_block == nullptr) { + ice::usize filter_data_size = info.data_block_filter.data_size; + void* block_data = _allocator.allocate({ Constant_DefaultBlockSize, ice::align_of }).memory; free_block = reinterpret_cast(block_data); - free_block->block_data_size = provided_block_size(); - free_block->block_data = free_block + 1; + free_block->block_data_size = ice::usize::subtract(provided_block_size(), filter_data_size); + free_block->block_filter_data = filter_data_size > 0_B ? (free_block + 1) : nullptr; + free_block->block_data = ice::ptr_add(free_block + 1, filter_data_size);// free_block + 1; } - free_block->block_entity_count_max = 0; + free_block->block_entity_count_max = ice::ecs::detail::calculate_entity_count_for_space(info, provided_block_size()); free_block->block_entity_count = 0; free_block->next = nullptr; return free_block; diff --git a/source/code/iceshard/engine/private/ecs/ecs_entity_index.cxx b/source/code/iceshard/engine/private/ecs/ecs_entity_index.cxx index 19e9f99b..5d92dcae 100644 --- a/source/code/iceshard/engine/private/ecs/ecs_entity_index.cxx +++ b/source/code/iceshard/engine/private/ecs/ecs_entity_index.cxx @@ -47,9 +47,11 @@ namespace ice::ecs ice::array::reserve(_generation, estimated_entity_count); // #todo: decide if we need this - //[[maybe_unused]] - //ice::ecs::Entity const initial_entity = create(); // The first entity will be invalid due to how the index works - //ICE_ASSERT(initial_entity == Entity::Invalid, "The definition of an 'Invalid' entity changed!"); + [[maybe_unused]] + ice::ecs::Entity const initial_entity = create(); // The first entity will be invalid due to how the index works + ICE_ASSERT(initial_entity == Entity::Invalid, "The definition of an 'Invalid' entity changed!"); + // Ensure this entity is always seen as "not-alive" + _generation[0] = ice::u8_max; } auto EntityIndex::count() const noexcept -> ice::u32 @@ -62,7 +64,7 @@ namespace ice::ecs using ice::ecs::EntityInfo; EntityInfo const info = ice::ecs::entity_info(entity); - return _generation[info.index] == info.generation; + return ice::count(_generation) > info.index && _generation[info.index] == info.generation; } auto EntityIndex::create() noexcept -> ice::ecs::Entity @@ -151,4 +153,12 @@ namespace ice::ecs } } + bool EntityIndex::recreate(ice::Array& entities, ice::u32 new_count) noexcept + { + destroy_many(entities); + ice::array::resize(entities, new_count); + create_many(entities); + return false; + } + } // namespace ice::ecs diff --git a/source/code/iceshard/engine/private/ecs/ecs_entity_operations.cxx b/source/code/iceshard/engine/private/ecs/ecs_entity_operations.cxx index 0bf53df1..71d52834 100644 --- a/source/code/iceshard/engine/private/ecs/ecs_entity_operations.cxx +++ b/source/code/iceshard/engine/private/ecs/ecs_entity_operations.cxx @@ -95,9 +95,14 @@ namespace ice::ecs EntityOperations::EntityOperations( ice::Allocator& alloc, + ice::ecs::EntityIndex& entities, + ice::ecs::ArchetypeIndex const& archetypes, ice::u32 initial_count ) noexcept : _allocator{ alloc } + , _entities{ entities } + , _archetypes{ archetypes } + , _root{ nullptr } , _operations{ nullptr } , _free_operations{ nullptr } , _data_nodes{ detail::allocate_data_node(_allocator, 16_KiB, nullptr) } @@ -275,61 +280,60 @@ namespace ice::ecs return OperationIterator{ ._entry = nullptr }; } - void queue_set_archetype( + void queue_remove_entity( ice::ecs::EntityOperations& entity_operations, - ice::ecs::Entity entity, - ice::ecs::Archetype archetype + ice::ecs::Entity entity ) noexcept { void* handle_loc; EntityOperation* operation = entity_operations.new_storage_operation(ice::meminfo_of, handle_loc); - operation->archetype = archetype; + operation->archetype = ice::ecs::Archetype::Invalid; operation->entities = reinterpret_cast(handle_loc); operation->entities[0] = entity; operation->entity_count = 1; - operation->notify_entity_changes = true; operation->component_data = nullptr; operation->component_data_size = 0; } - void queue_set_archetype( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities, - ice::ecs::Archetype archetype, - bool notify_changes /*= false*/ - ) noexcept + void EntityOperations::destroy(ice::Span entities) noexcept { - ice::u32 const entity_count = ice::count(entities); + if (ice::span::empty(entities)) + { + return; + } void* handle_loc; - EntityOperation* operation = entity_operations.new_storage_operation(ice::meminfo_of * entity_count, handle_loc); - operation->archetype = archetype; + EntityOperation* operation = new_storage_operation(ice::meminfo_of * ice::count(entities), handle_loc); + operation->archetype = ice::ecs::Archetype::Invalid; operation->entities = reinterpret_cast(handle_loc); - operation->entity_count = entity_count; - operation->notify_entity_changes = notify_changes; + operation->entity_count = ice::count(entities); operation->component_data = nullptr; operation->component_data_size = 0; ice::memcpy(operation->entities, ice::span::data(entities), ice::span::size_bytes(entities)); } - void queue_set_archetype_with_data( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities, - ice::ecs::Archetype archetype, - ice::ecs::EntityOperations::ComponentInfo component_info, - ice::Span component_data, - bool notify_changes - ) noexcept + auto OperationBuilder::with_data( + ice::ecs::OperationComponentInfo component_info, + ice::Span component_data + ) noexcept -> Result { - ice::ucount const entity_count = ice::count(entities); + if (ice::span::empty(entities) || (mode == 2 && index_create_count == 0) || mode == 0) + { + return Result{ *this }; + } + + ice::ucount const entity_count = mode == 2 ? index_create_count : ice::count(entities); ice::ucount const component_count = ice::count(component_info.names); - ice::meminfo additional_data_size = ice::meminfo_of * entity_count; - additional_data_size += ice::meminfo_of; - additional_data_size += ice::meminfo_of * component_count; - additional_data_size += ice::meminfo_of * component_count; - additional_data_size += ice::meminfo_of * component_count; + ice::meminfo additional_data_size = ice::meminfo{ filter_data_size, ice::ualign::b_8 }; + additional_data_size += ice::meminfo_of * entity_count; + + // Data for storing component info + additional_data_size += ice::meminfo_of; + additional_data_size.size += ice::span::size_bytes(component_info.names); + additional_data_size.size += ice::span::size_bytes(component_info.sizes); + additional_data_size.size += ice::span::size_bytes(component_info.offsets); // Component data for (ice::Data const& data : component_data) @@ -337,98 +341,135 @@ namespace ice::ecs additional_data_size += { data.size, data.alignment }; } - // Setup the operation - void* handle_loc; - EntityOperation* operation = entity_operations.new_storage_operation(additional_data_size, handle_loc); + void* operation_data = nullptr; + ice::ecs::EntityOperation* operation = operations.new_storage_operation( + additional_data_size, + operation_data + ); + + ice::memcpy(operation_data, filter_data, ice::usize{ filter_data_size }); + void const* filter_ptr = operation_data; + operation_data = ice::ptr_add(operation_data, filter_data_size); - // Set entity handles - ice::ecs::Entity* entities_ptr = reinterpret_cast(handle_loc); - ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); - handle_loc = ice::ptr_add(handle_loc, ice::span::size_bytes(entities)); + ice::ecs::Entity* entities_ptr = reinterpret_cast(operation_data); + if (mode == 1) + { + ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); + } + else + { + ICE_ASSERT_CORE(mode == 2); + index->create_many({ entities_ptr, index_create_count }); + } // Set component info object - ice::StringID* names_ptr = reinterpret_cast(handle_loc); + ice::StringID* names_ptr = reinterpret_cast(entities_ptr + entity_count); ice::memcpy(names_ptr, ice::span::data(component_info.names), ice::span::size_bytes(component_info.names)); - handle_loc = ice::ptr_add(handle_loc, ice::span::size_bytes(component_info.names)); - ice::u32* sizes_ptr = reinterpret_cast(handle_loc); + ice::u32* sizes_ptr = reinterpret_cast(names_ptr + component_count); ice::memcpy(sizes_ptr, ice::span::data(component_info.sizes), ice::span::size_bytes(component_info.sizes)); - handle_loc = ice::ptr_add(handle_loc, ice::span::size_bytes(component_info.sizes)); - ice::u32* offsets_ptr = reinterpret_cast(handle_loc); - ice::memcpy(offsets_ptr, ice::span::data(component_info.offsets), ice::span::size_bytes(component_info.offsets)); - handle_loc = ice::ptr_add(handle_loc, ice::span::size_bytes(component_info.offsets)); + ice::u32* offsets_ptr = reinterpret_cast(sizes_ptr + component_count); - ice::ecs::EntityOperations::ComponentInfo* component_info_ptr = reinterpret_cast(handle_loc); - handle_loc = component_info_ptr + 1; + // We update now the operation data pointer to where we store the component info object. + // We will calculate data offsets from here too. + operation_data = offsets_ptr + component_count; - component_info_ptr->names = ice::Span{ names_ptr, component_count }; - component_info_ptr->sizes = ice::Span{ sizes_ptr, component_count }; - component_info_ptr->offsets = ice::Span{ offsets_ptr, component_count }; + // Set the component info object with the above pointers. + OperationComponentInfo* component_info_ptr; + { + component_info_ptr = reinterpret_cast(operation_data); + component_info_ptr->names = ice::Span{ names_ptr, component_count }; + component_info_ptr->sizes = ice::Span{ sizes_ptr, component_count }; + component_info_ptr->offsets = ice::Span{ offsets_ptr, component_count }; + operation_data = component_info_ptr + 1; + } // Copy over component data - void const* const data_beg = handle_loc; + void const* const data_beg = operation_data; ice::u32 component_idx = 0; for (ice::Data const& data : component_data) { // Align target pointer - handle_loc = ice::align_to(handle_loc, data.alignment).value; + operation_data = ice::align_to(operation_data, data.alignment).value; // Copy over the data - ice::memcpy(handle_loc, data.location, data.size); + ice::memcpy(operation_data, data.location, data.size); // Save the offset - offsets_ptr[component_idx] = ice::ucount(ice::ptr_distance(data_beg, handle_loc).value); + offsets_ptr[component_idx] = ice::ucount(ice::ptr_distance(data_beg, operation_data).value); - handle_loc = ice::ptr_add(handle_loc, data.size); + operation_data = ice::ptr_add(operation_data, data.size); component_idx += 1; } operation->archetype = archetype; operation->entities = entities_ptr; operation->entity_count = entity_count; - operation->notify_entity_changes = notify_changes; operation->component_data = component_info_ptr; - operation->component_data_size = ice::ucount(ice::ptr_distance(component_info_ptr, handle_loc).value); - } + operation->component_data_size = ice::ucount(ice::ptr_distance(component_info_ptr, operation_data).value); + operation->filter_data = filter_ptr; - void queue_remove_entity( - ice::ecs::EntityOperations& entity_operations, - ice::ecs::Entity entity - ) noexcept - { - void* handle_loc; - EntityOperation* operation = entity_operations.new_storage_operation(ice::meminfo_of, handle_loc); - operation->archetype = ice::ecs::Archetype::Invalid; - operation->entities = reinterpret_cast(handle_loc); - operation->entities[0] = entity; - operation->entity_count = 1; - operation->notify_entity_changes = true; - operation->component_data = nullptr; - operation->component_data_size = 0; + if (mode == 2) + { + entities = { entities_ptr, entity_count }; + } + mode = 0; + + return { *this }; } - void queue_batch_remove_entities( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities - ) noexcept + auto OperationBuilder::finalize() noexcept -> Result { - if (ice::span::empty(entities)) + if (mode != 0) { - return; + ice::ucount const entity_count = mode == 2 ? index_create_count : ice::count(entities); + + ice::meminfo required_memory = ice::meminfo{ filter_data_size, ice::ualign::b_8 }; + required_memory += ice::meminfo_of * entity_count; + + void* operation_data; + EntityOperation* operation = operations.new_storage_operation(required_memory, operation_data); + + ice::memcpy(operation_data, filter_data, ice::usize{ filter_data_size }); + void const* filter_ptr = operation_data; + operation_data = ice::ptr_add(operation_data, filter_data_size); + + ice::ecs::Entity* entities_ptr = reinterpret_cast(operation_data); + if (mode == 1) + { + ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); + } + else + { + ICE_ASSERT_CORE(mode == 2); + index->create_many({ entities_ptr, index_create_count }); + } + + operation->archetype = archetype; + operation->entities = entities_ptr; + operation->entity_count = entity_count; + operation->component_data = nullptr; + operation->component_data_size = 0; + operation->filter_data = filter_ptr; + + ice::memcpy(operation_data, filter_data, filter_data_size); + ice::memcpy(operation->entities, ice::span::data(entities), ice::span::size_bytes(entities)); + + if (mode == 2) + { + entities = { entities_ptr, entity_count }; + } + mode = 0; } - void* handle_loc; - EntityOperation* operation = entity_operations.new_storage_operation(ice::meminfo_of * ice::count(entities), handle_loc); - operation->archetype = ice::ecs::Archetype::Invalid; - operation->entities = reinterpret_cast(handle_loc); - operation->entity_count = ice::count(entities); - operation->notify_entity_changes = false; - operation->component_data = nullptr; - operation->component_data_size = 0; + return { *this }; + } - ice::memcpy(operation->entities, ice::span::data(entities), ice::span::size_bytes(entities)); + OperationBuilder::~OperationBuilder() noexcept + { + this->finalize(); } } // namespace ice::ecs diff --git a/source/code/iceshard/engine/private/ecs/ecs_entity_storage.cxx b/source/code/iceshard/engine/private/ecs/ecs_entity_storage.cxx index d2537bc3..41ef29be 100644 --- a/source/code/iceshard/engine/private/ecs/ecs_entity_storage.cxx +++ b/source/code/iceshard/engine/private/ecs/ecs_entity_storage.cxx @@ -22,7 +22,7 @@ namespace ice::ecs }; auto get_entity_array( - ice::ecs::EntityOperations::ComponentInfo const& info, + ice::ecs::OperationComponentInfo const& info, ice::ecs::detail::OperationDetails const& data_details, ice::u32 entity_count ) noexcept -> ice::Span @@ -38,17 +38,55 @@ namespace ice::ecs ); } - void track_entities( - ice::HashMap& tracked, - ice::Span updated - ) noexcept; + auto get_component_offsets( + ice::StringID const (&components)[2], + ice::ecs::detail::ArchetypeInstanceInfo const& info, + ice::u32* out_sizes, + ice::u32* out_offsets + ) noexcept + { + for (ice::u32 cmp = 0; cmp < 2; ++cmp) + { + if (components[cmp] != ice::StringID_Invalid) + { + ice::u32 in_arch_idx; + bool const found = ice::binary_search(ice::span::subspan(info.component_identifiers, 1), components[cmp], in_arch_idx); + ICE_ASSERT_CORE(found); + out_sizes[cmp] = info.component_sizes[in_arch_idx + 1]; + out_offsets[cmp] = info.component_offsets[in_arch_idx + 1]; + } + } + } + +#if 0 + auto get_block( + ice::ecs::EntityDataSlot slot_info, + ice::ecs::detail::ArchetypeInstanceInfo const* archinfo, + ice::Span head_blocks + ) noexcept -> ice::ecs::detail::DataBlock* + { + // Save the block index + ice::u32 const archetype_instance_idx = static_cast(archinfo->archetype_instance); + ice::ecs::detail::DataBlock* archetype_block = head_blocks[archetype_instance_idx]; + + // Get the proper block + while (slot_info.block > 0) + { + slot_info.block -= 1; // Reduce the block index + + // Get the next block + archetype_block = archetype_block->next; + } + return archetype_block; + } +#endif void store_entities_with_data( ice::Span src_entities, ice::Span dst_data_slots, ice::ecs::EntityDataSlot base_slot, - ice::ecs::EntityOperations::ComponentInfo const& src_info, - ice::ecs::EntityOperations::ComponentInfo const& dst_info, + ice::ecs::OperationComponentInfo const& src_info, + ice::ecs::OperationComponentInfo const& dst_info, ice::ecs::detail::OperationDetails const& src_data_details, ice::ecs::detail::OperationDetails const& dst_data_details ) noexcept @@ -148,8 +186,8 @@ namespace ice::ecs void update_entities_with_data( ice::u32 entity_count, - ice::ecs::EntityOperations::ComponentInfo const& src_info, - ice::ecs::EntityOperations::ComponentInfo const& dst_info, + ice::ecs::OperationComponentInfo const& src_info, + ice::ecs::OperationComponentInfo const& dst_info, ice::ecs::detail::OperationDetails const& src_data_details, ice::ecs::detail::OperationDetails const& dst_data_details ) noexcept @@ -216,7 +254,7 @@ namespace ice::ecs ice::Span src_entities, ice::Span dst_data_slots, ice::ecs::EntityDataSlot base_slot, - ice::ecs::EntityOperations::ComponentInfo const& info, + ice::ecs::OperationComponentInfo const& info, ice::ecs::detail::OperationDetails const& data_details ) noexcept { @@ -278,11 +316,11 @@ namespace ice::ecs void batch_remove_entities( ice::ecs::ArchetypeIndex const& archetypes, - ice::ecs::EntityOperation const& operation, + ice::HashMap const& destructors, + ice::Span dst_data_slots, ice::Span entities_to_remove, ice::Span data_blocks, - ice::Span data_slots, - ice::ShardContainer& out_shards + ice::Span data_slots ) noexcept { auto const* it = ice::span::begin(entities_to_remove); @@ -294,28 +332,49 @@ namespace ice::ecs ice::ecs::detail::DataBlock* archetype_block = nullptr; ice::u32 archetype_block_index = ice::u32_max; + ice::ucount dtor_count = 0; + ice::ecs::detail::EntityDestructor const* dtors[5]; + ice::u32 dtor_components_sizes[10]; + ice::u32 dtor_components_offsets[10]; + do { EntityInfo entity_info = ice::ecs::entity_info(*it); - EntityDataSlot const slot_info = data_slots[entity_info.index]; + EntityDataSlot const first_slot_info = data_slots[entity_info.index]; // Find the archetype info if it changed (Users are encouraged to avoid this) - ArchetypeInstance const temp_archetype[1]{ ArchetypeInstance{ slot_info.archetype } }; + ArchetypeInstance const temp_archetype[1]{ ArchetypeInstance{ first_slot_info.archetype } }; if (archetype != temp_archetype[0]) { archetypes.fetch_archetype_instance_infos(temp_archetype, archetype_infos); archetype = temp_archetype[0]; + + // Query all attached destructors + dtor_count = 0; + auto dtor_it = ice::multi_hashmap::find_first(destructors, ice::hash(archetype)); + while (dtor_it != nullptr) + { + dtors[dtor_count] = ice::addressof(dtor_it.value()); + get_component_offsets( + dtors[dtor_count]->components, + *archetype_infos[0], + dtor_components_sizes + dtor_count * 2, + dtor_components_offsets + dtor_count * 2 + ); + dtor_count += 1; + dtor_it = ice::multi_hashmap::find_next(destructors, dtor_it); + } } - if (slot_info.block != archetype_block_index) + if (first_slot_info.block != archetype_block_index) { // Save the block index ice::u32 const archetype_instance_idx = static_cast(archetype_infos[0]->archetype_instance); - archetype_block_index = slot_info.block; + archetype_block_index = first_slot_info.block; archetype_block = data_blocks[archetype_instance_idx]; // Get the proper block - ice::u32 block_idx = slot_info.block; + ice::u32 block_idx = first_slot_info.block; while(block_idx > 0) { block_idx -= 1; // Reduce the block index @@ -327,26 +386,27 @@ namespace ice::ecs ICE_ASSERT_CORE(archetype_block != nullptr); ICE_ASSERT( - archetype_block->block_entity_count > slot_info.index, + archetype_block->block_entity_count > first_slot_info.index, "This storage has no data associated with the given entity handle!" ); ice::ecs::detail::OperationDetails del_data_details{ - .block_offset = slot_info.index, // Initial index + .block_offset = first_slot_info.index, // Initial index .block_data = archetype_block->block_data, // The actual data }; + // Get the next entity + it += 1; + // Batch removal and step to next ice::u32 span_size = 1; while(it != end) { - it += 1; // Get the next entity and check if we can already handle it - EntityInfo next_entity_info = ice::ecs::entity_info(*it); EntityDataSlot const next_slot_info = data_slots[next_entity_info.index]; // Increase count if we found the next entity - if ((slot_info.index + span_size) == next_slot_info.index) + if ((first_slot_info.index + span_size) == next_slot_info.index) { span_size += 1; } @@ -360,6 +420,26 @@ namespace ice::ecs { break; } + + // Get the next entity + it += 1; + } + + // Setup components ptrs + for (ice::u32 idx = 0; idx < dtor_count; ++idx) + { + // Get the pointers to the specific arrays + void const* const ent = ice::ptr_add(del_data_details.block_data, { archetype_infos[0]->component_offsets[0] }); + void* ptr1 = ice::ptr_add(del_data_details.block_data, { dtor_components_offsets[idx * 2] }); + void* ptr2 = ice::ptr_add(del_data_details.block_data, { dtor_components_offsets[idx * 2 + 1] }); + + // Move pointers to the first deleted entity index + ice::ecs::Entity const* const entities = reinterpret_cast(ent) + del_data_details.block_offset; + ptr1 = ice::ptr_add(ptr1, { del_data_details.block_offset * dtor_components_sizes[idx * 2] }); + ptr2 = ice::ptr_add(ptr1, { del_data_details.block_offset * dtor_components_sizes[idx * 2 + 1] }); + + // Call on each destructor + dtors[idx]->fn(dtors[idx]->userdata, span_size, entities, ptr1, ptr2); } // Clear the block from the selected entities @@ -383,7 +463,7 @@ namespace ice::ecs movable_entities += 1; } } - EntityOperations::ComponentInfo const component_info{ + OperationComponentInfo const component_info{ .names = archetype_infos[0]->component_identifiers, .sizes = archetype_infos[0]->component_sizes, .offsets = archetype_infos[0]->component_offsets, @@ -399,11 +479,11 @@ namespace ice::ecs // List of entities that will be moved moved_entities, data_slots, - slot_info, // The initially removed slot + first_slot_info, // The initially removed slot component_info, component_info, - del_data_details, /* src data block */ - dst_data_details /* dst data block */ + dst_data_details, /* src data block */ + del_data_details /* dst data block */ ); } @@ -411,6 +491,58 @@ namespace ice::ecs archetype_block->block_entity_count -= span_size; } while(it != end); + + // Clear all references + // TODO: We might not need this later once we extend Operations with a "type" flag. + for (ice::ecs::Entity entity : entities_to_remove) + { + ice::ecs::EntityInfo const ei = ice::ecs::entity_info(entity); + dst_data_slots[ei.index] = {}; // Reset the slot info! + } + } + + auto default_filter(void const*, void const*) noexcept + { + return true; + } + + auto select_block( + ice::ecs::detail::ArchetypeInstanceInfo const& info, + ice::ecs::detail::DataBlockPool& pool, + ice::ecs::detail::DataBlock* head, + ice::ecs::EntityOperation const& operation, + ice::u32& out_block_idx + ) noexcept -> ice::ecs::detail::DataBlock* + { + ice::ecs::detail::DataBlock* result = head; + ice::ecs::detail::DataBlockFilter filter = info.data_block_filter; + ice::ecs::detail::DataBlockFilter::FilterFn fn_filter = ice::value_or_default(filter.fn_filter, default_filter); + + // Get the next block + while (result->block_entity_count == result->block_entity_count_max + || fn_filter(result->block_filter_data, operation.filter_data) == false) + { + if (result->next == nullptr) + { + result->next = pool.request_block(info); + result = result->next; + + if (filter.enabled) + { + filter.fn_data_setup(result->block_filter_data, operation.filter_data); + } + } + else + { + result = result->next; + } + + out_block_idx += 1; + } + + ICE_ASSERT_CORE(result->block_entity_count_max > 0); + ICE_ASSERT_CORE(result->block_entity_count <= result->block_entity_count_max); + return result; } } // namespace detail @@ -428,6 +560,7 @@ namespace ice::ecs , _head_blocks{ _allocator } , _data_blocks{ _allocator } , _data_slots{ _allocator } + , _destructors{ _allocator } { ice::array::reserve(_head_blocks, 100); // 100 archetypes should suffice for now ice::array::resize(_data_slots, Constant_InitialEntityCount); @@ -479,6 +612,11 @@ namespace ice::ecs return _entity_index; } + auto EntityStorage::archetypes() const noexcept -> ice::ecs::ArchetypeIndex const& + { + return _archetype_index; + } + void EntityStorage::update_archetypes() noexcept { using ice::ecs::detail::DataBlock; @@ -493,6 +631,7 @@ namespace ice::ecs ice::array::resize(_head_blocks, archetype_count); ice::array::resize(_data_blocks, archetype_count); + ice::hashmap::reserve(_destructors, archetype_count); // Setup the empty head blocks for new archetypes. // This approach gives two benefits: @@ -531,6 +670,30 @@ namespace ice::ecs } } + bool EntityStorage::attach_destructor( + ice::ecs::Archetype archetype, + ice::ecs::detail::EntityDestructor const& destructor + ) noexcept + { + ice::ecs::detail::ArchetypeInstanceInfo const* info = nullptr; + ice::ecs::detail::DataBlockPool* pool; // we dont care + _archetype_index.fetch_archetype_instance_info_with_pool(archetype, info, pool); + ICE_ASSERT_CORE(info != nullptr); + + if (ice::hashmap::has(_destructors, ice::hash(info->archetype_instance))) + { + auto it = ice::multi_hashmap::find_first(_destructors, ice::hash(info->archetype_instance)); + while (it != nullptr && it.value().identifier != destructor.identifier) + { + it = ice::multi_hashmap::find_next(_destructors, it); + } + ICE_ASSERT(it == nullptr, "A destructor with id {} was already attached to this archetype!"); + } + + ice::multi_hashmap::insert(_destructors, ice::hash(info->archetype_instance), destructor); + return true; + } + void EntityStorage::execute_operations( ice::ecs::EntityOperations const& operations, ice::ShardContainer& out_shards @@ -574,7 +737,7 @@ namespace ice::ecs } ICE_LOG( - ice::LogSeverity::Debug, ice::LogTag::Engine, + ice::LogSeverity::Info, ice::LogTag::Engine, "Executing operation with {} entities.", operation.entity_count ); @@ -601,10 +764,10 @@ namespace ice::ecs } } - // #todo allow different archetypes maybe? - ICE_ASSERT( + ICE_LOG_IF( same_archetype == true, - "Entities in operation have different archetypes, operation is illformed!" + LogSeverity::Warning, LogTag::Engine, + "Entities in operation have different archetypes, not all operations can handle this yet! Check for possible bugs!" ); } @@ -625,7 +788,7 @@ namespace ice::ecs _archetype_index.fetch_archetype_instance_info_with_pool(operation.archetype, dst_instance_info, dst_instance_pool); - EntityOperations::ComponentInfo const* const provided_component_info = reinterpret_cast( + OperationComponentInfo const* const provided_component_info = reinterpret_cast( operation.component_data ); @@ -639,7 +802,7 @@ namespace ice::ecs { provided_data_details.block_data = ice::ptr_add( operation.component_data, - ice::size_of + ice::size_of ); } @@ -648,7 +811,7 @@ namespace ice::ecs { ice::u32 const dst_instance_idx = static_cast(dst_instance_info->archetype_instance); - EntityOperations::ComponentInfo const dst_component_info{ + OperationComponentInfo const dst_component_info{ .names = dst_instance_info->component_identifiers, .sizes = dst_instance_info->component_sizes, .offsets = dst_instance_info->component_offsets, @@ -662,15 +825,13 @@ namespace ice::ecs ice::u32 block_idx = 0; // We need at least the initial data block. - DataBlock* data_block_it = _data_blocks[dst_instance_idx]; - if (data_block_it == nullptr) - { - data_block_it = dst_instance_pool->request_block(); - data_block_it->block_entity_count_max = dst_instance_info->component_entity_count_max; - data_block_it->block_entity_count = 0; - - _data_blocks[dst_instance_idx] = data_block_it; - } + DataBlock* data_block_it = ice::ecs::detail::select_block( + *dst_instance_info, + *dst_instance_pool, + _data_blocks[dst_instance_idx], + operation, + block_idx + ); // Number of remaining entities to process ice::u32 processed_count = 0; @@ -732,7 +893,7 @@ namespace ice::ecs ice::u32 const src_instance_idx = static_cast(src_instance_info[0]->archetype_instance); - EntityOperations::ComponentInfo const src_component_info{ + OperationComponentInfo const src_component_info{ .names = src_instance_info[0]->component_identifiers, .sizes = src_instance_info[0]->component_sizes, .offsets = src_instance_info[0]->component_offsets, @@ -779,15 +940,6 @@ namespace ice::ecs dst_data_details /* dst data block */ ); - { - ice::Span updated_entities = ice::ecs::detail::get_entity_array( - dst_component_info, - dst_data_details, - entities_stored - ); - ICE_ASSERT(ice::count(updated_entities) == 0, ""); - } - // 2. Apply the new provided data if any. if (provided_component_info != nullptr) { @@ -833,6 +985,7 @@ namespace ice::ecs } // Update the remianing count + processed_count += entities_stored; remaining_count -= entities_stored; data_block_it->block_entity_count += entities_stored; } @@ -840,20 +993,13 @@ namespace ice::ecs // Get the next block if (remaining_count > 0) { - if (data_block_it->next == nullptr) - { - data_block_it->next = dst_instance_pool->request_block(); - - data_block_it = data_block_it->next; - data_block_it->block_entity_count_max = dst_instance_info->component_entity_count_max; - data_block_it->block_entity_count = 0; - } - else - { - data_block_it = data_block_it->next; - } - - block_idx += 1; + data_block_it = ice::ecs::detail::select_block( + *dst_instance_info, + *dst_instance_pool, + data_block_it, + operation, + block_idx + ); } } while (remaining_count > 0); @@ -862,7 +1008,7 @@ namespace ice::ecs { ice::u32 const src_instance_idx = static_cast(src_instance_info[0]->archetype_instance); - EntityOperations::ComponentInfo const src_component_info{ + OperationComponentInfo const src_component_info{ .names = src_instance_info[0]->component_identifiers, .sizes = src_instance_info[0]->component_sizes, .offsets = src_instance_info[0]->component_offsets, @@ -926,11 +1072,11 @@ namespace ice::ecs ice::ecs::detail::batch_remove_entities( _archetype_index, - operation, + _destructors, + _data_slots, entities, _data_blocks, - _data_slots, - out_shards + _data_slots ); } } @@ -982,6 +1128,22 @@ namespace ice::ecs return valid; } + bool EntityStorage::query_archetype_block( + ice::ecs::Archetype archetype, + ice::ecs::detail::ArchetypeInstanceInfo const*& out_instance_info, + ice::ecs::detail::DataBlock const*& out_head_block + ) const noexcept + { + _archetype_index.fetch_archetype_instance_infos( + { &archetype, 1 }, + { &out_instance_info, 1 } + ); + + ice::u32 const instance_idx = static_cast(out_instance_info->archetype_instance); + out_head_block = _data_blocks[instance_idx]; + return true; // TODO: Check if we actually found an archetype. + } + void EntityStorage::query_internal( ice::Span query_info, ice::Span query_tags, diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_detail.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_detail.hxx index f37695af..eefecbd8 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_detail.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_detail.hxx @@ -3,8 +3,10 @@ #pragma once #include +#include #include #include +#include namespace ice::ecs::detail { @@ -19,11 +21,11 @@ namespace ice::ecs::detail struct ArchetypeInstanceInfo { ice::ecs::detail::ArchetypeInstance archetype_instance; + ice::ecs::detail::DataBlockFilter data_block_filter; ice::Span component_identifiers; ice::Span component_sizes; ice::Span component_alignments; ice::Span component_offsets; - ice::u32 component_entity_count_max; }; //! \brief Helper type to sort components during compiletime when instancing `ArchetypeDefinition`s. @@ -115,4 +117,13 @@ namespace ice::ecs::detail return result; } + constexpr auto calculate_entity_count_for_space(ice::ecs::detail::ArchetypeInstanceInfo const& arch, ice::usize space) noexcept -> ice::ucount + { + ice::u32 const component_size_sum = ice::accumulate(arch.component_sizes, 0); + ice::u32 const component_alignment_sum = ice::accumulate(arch.component_alignments, 0); + + ice::usize const available_block_size = { ice::usize::subtract(space, arch.data_block_filter.data_size).value - component_alignment_sum }; + return ice::ucount(available_block_size.value) / component_size_sum; + } + } // namespace ice::ecs diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_index.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_index.hxx index 036ff686..1f2263d1 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_index.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_archetype_index.hxx @@ -27,11 +27,13 @@ namespace ice::ecs template auto new_archetype( ice::String name = {}, - ice::ecs::detail::DataBlockPool * data_block_pool = nullptr + ice::ecs::detail::DataBlockFilter data_block_filter = {}, + ice::ecs::detail::DataBlockPool* data_block_pool = nullptr ) noexcept -> ice::ecs::Archetype; auto register_archetype( ice::ecs::ArchetypeInfo const& archetype_info, + ice::ecs::detail::DataBlockFilter data_block_filter = {}, ice::ecs::detail::DataBlockPool* data_block_pool = nullptr ) noexcept -> ice::ecs::Archetype; @@ -85,12 +87,13 @@ namespace ice::ecs template inline auto ArchetypeIndex::new_archetype( ice::String name, + ice::ecs::detail::DataBlockFilter data_block_filter, ice::ecs::detail::DataBlockPool* data_block_pool ) noexcept -> ice::ecs::Archetype { ice::ecs::ArchetypeInfo info = ice::ecs::Constant_ArchetypeDefinition; info.name = name; - return this->register_archetype(info, data_block_pool); + return this->register_archetype(info, data_block_filter, data_block_pool); } } // namespace ice::ecs diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx index 317c5069..23227e30 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_concepts.hxx @@ -8,6 +8,7 @@ namespace ice::ecs { enum class Entity : ice::u32; + enum class Archetype : ice::u64; } // namespace ice::ecs @@ -22,6 +23,12 @@ namespace ice::ecs::concepts template concept Entity = std::is_same_v; + //! \brief Concept to check if an value can represent an archetype reference. + //! + //! \details Because archetypes can be defined with a name, using a string can be also used in various API calls to reference archetypes. + template + concept ArchetypeRef = std::is_same_v || std::convertible_to; + //! \brief Concept checking that a type has all necessary members and definitions to be considered a component type. //! //! \details This concepts is currently very simple but once C++ static reflection are available this might change. diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_data_block.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_data_block.hxx index bda90315..32590bf7 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_data_block.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_data_block.hxx @@ -23,10 +23,13 @@ namespace ice::ecs::detail //! \brief Size of the available data to be used for entity components. ice::usize block_data_size = 0_B; + //! \brief Block filter data + void* block_filter_data; + //! \brief Pointer to component data. void* block_data; ice::ecs::detail::DataBlock* next; }; -} // namespace ice::ecs +} // namespace ice::ecs::detail diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_filter.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_filter.hxx new file mode 100644 index 00000000..cce39b74 --- /dev/null +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_filter.hxx @@ -0,0 +1,102 @@ +/// Copyright 2022 - 2025, Dandielo +/// SPDX-License-Identifier: MIT + +#pragma once +#include + +namespace ice::ecs::detail +{ + + template + concept FilterType = requires(T t, T const* const_param, T * mut_param) { + { T::on_filter(const_param, const_param) } -> std::convertible_to; + { T::on_setup(mut_param, const_param) } -> std::convertible_to; + }; + + struct DataBlockFilter + { + bool enabled = false; + + ice::usize data_size = 0_B; + + using FilterFn = bool(*)(void const* block_data, void const* filter_param) noexcept; + FilterFn fn_filter = nullptr; + + using DataSetupFn = void(*)(void* block_data, void const* filter_param) noexcept; + DataSetupFn fn_data_setup = nullptr; + + struct QueryFilter + { + QueryFilter() noexcept + : arch{ } + , fn_filter{ nullptr } + , filter_data{ } + { } + + template + QueryFilter(T const& filter) noexcept + : arch{ } + , fn_filter{ (DataBlockFilter::FilterFn)T::on_filter } + , filter_data{ } + { + ice::memcpy(filter_data, ice::addressof(filter), sizeof(T)); + } + + QueryFilter(QueryFilter const& other) noexcept + : fn_filter{ other.fn_filter } + { + ice::memcpy(filter_data, other.filter_data, sizeof(filter_data)); + } + + auto operator=(QueryFilter const& other) noexcept -> QueryFilter& + { + if (ice::addressof(other) != this) + { + arch = other.arch; + fn_filter = other.fn_filter; + ice::memcpy(filter_data, other.filter_data, sizeof(filter_data)); + } + return *this; + } + + bool check(ice::ecs::detail::DataBlock const* data_block) const noexcept + { + return fn_filter == nullptr || fn_filter(data_block->block_filter_data, filter_data); + } + + auto next(ice::ecs::detail::DataBlock const* data_block) const noexcept + { + ICE_ASSERT_CORE(data_block != nullptr); + if (fn_filter == nullptr) + { + return data_block->next; + } + else + { + ice::ecs::detail::DataBlock* result = data_block->next; + while (result != nullptr && check(result) == false) + { + result = result->next; + } + return result; + } + } + + ice::ecs::Archetype arch; + FilterFn fn_filter; + char filter_data[16]; + }; + }; + + template requires (FilterType) + auto create_data_block_filter() noexcept -> ice::ecs::detail::DataBlockFilter + { + return DataBlockFilter{ + .enabled = true, + .data_size = ice::size_of, + .fn_filter = (DataBlockFilter::FilterFn)T::on_filter, + .fn_data_setup = (DataBlockFilter::DataSetupFn)T::on_setup, + }; + } + +} // namespace ice::ecs::detail diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_pool.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_pool.hxx index b52b4b5a..040f2600 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_pool.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_data_block_pool.hxx @@ -25,7 +25,7 @@ namespace ice::ecs::detail //! \return A unused data block ready to store entity data. //! //! \note (For developers) After this function returns the pool should't access any of the fields in the block structure. - virtual auto request_block() noexcept -> ice::ecs::detail::DataBlock* = 0; + virtual auto request_block(ice::ecs::detail::ArchetypeInstanceInfo const& info) noexcept -> ice::ecs::detail::DataBlock* = 0; //! \brief Returns the block to the pool, ensuring that no data will read from or written to it. //! \pre The block parameter is not a `nullptr`. diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_entity.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_entity.hxx index 92ccacf4..58c95819 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_entity.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_entity.hxx @@ -14,7 +14,7 @@ namespace ice::ecs //! //! \details An Entity handle is opaque and does not provide any functionality out of the box. It's mainly used as an argument in other ECS related //! systems. For examples, you can check if entities are alive with `EntityIndex::is_alive` or query a specific components with `Query` objects. - enum class Entity : ice::u32 { }; + enum class Entity : ice::u32 { Invalid = 0 }; //! \brief Provides easy access to the specific parts making up the `Entity` handle value. struct EntityInfo diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_index.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_index.hxx index 936736f3..fb3fba74 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_index.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_index.hxx @@ -36,6 +36,8 @@ namespace ice::ecs void destroy(ice::ecs::Entity entity) noexcept; void destroy_many(ice::Span entities) noexcept; + bool recreate(ice::Array& entity, ice::u32 new_count) noexcept; + private: ice::Allocator& _allocator; ice::u32 const _max_entity_count; diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_operations.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_operations.hxx index 2274ecd2..170c3d83 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_operations.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_operations.hxx @@ -6,6 +6,8 @@ #include #include #include +#include +#include namespace ice::ecs { @@ -19,19 +21,104 @@ namespace ice::ecs { ice::ecs::EntityOperation* next; - ice::u32 entity_count : 31; - ice::u32 notify_entity_changes : 1; + ice::u32 entity_count; ice::u32 component_data_size; ice::ecs::Archetype archetype; ice::ecs::Entity* entities; + void const* filter_data; void* component_data; }; + struct OperationComponentInfo + { + ice::Span names; + ice::Span sizes; + ice::Span offsets; + }; + + struct OperationBuilder + { + ice::ecs::EntityOperations& operations; + ice::ecs::Archetype archetype; + union + { + ice::Span entities; + struct // for mode == 1 + { + ice::ecs::EntityIndex* index; + ice::u32 index_create_count; + }; + }; + ice::usize filter_data_size; + char filter_data[16]; // We assume filter data not requiring more than 16 bytes. + ice::u32 mode = 0; // Default + + struct Result + { + auto one() const noexcept -> ice::ecs::Entity { return ice::span::front(_builder.entities); } + auto all() const noexcept -> ice::Span { return _builder.entities; } + + auto store(ice::Array& out_entities, bool append = true) const noexcept + { + if (append == false) ice::array::clear(out_entities); + ice::array::push_back(out_entities, all()); + } + + Result(OperationBuilder& builder) noexcept : _builder{ builder } { } + private: + OperationBuilder& _builder; + }; + + OperationBuilder( + ice::ecs::EntityOperations& operations, + ice::ecs::Archetype archetype, + ice::Span entites + ) noexcept; + + OperationBuilder( + ice::ecs::EntityOperations& operations, + ice::ecs::Archetype archetype, + ice::ecs::EntityIndex& index, + ice::ucount entity_count + ) noexcept; + + ~OperationBuilder() noexcept; + + template + auto with_filter(T const& filter) noexcept -> OperationBuilder&; + + auto with_data( + ice::ecs::OperationComponentInfo component_info, + ice::Span components_data + ) noexcept -> Result; + + template + auto with_data(Components const&... components) noexcept -> Result; + + template + auto with_data(ice::Span&... out_component_spans) noexcept -> Result; + + template + auto with_data(ice::Span... component_spans) noexcept -> Result; + + auto finalize() noexcept -> Result; + + auto one() noexcept -> ice::ecs::Entity { return finalize().one(); } + + auto all() noexcept -> ice::Span { return finalize().all(); } + }; + class EntityOperations { public: - EntityOperations(ice::Allocator& alloc, ice::u32 prealloc = 16) noexcept; + EntityOperations( + ice::Allocator& alloc, + ice::ecs::EntityIndex& entities, + ice::ecs::ArchetypeIndex const& archetypes, + ice::u32 prealloc = 16 + ) noexcept; + ~EntityOperations() noexcept; void clear() noexcept; @@ -44,12 +131,51 @@ namespace ice::ecs void*& out_operation_data_ptr ) noexcept -> ice::ecs::EntityOperation*; - struct ComponentInfo + auto set( + ice::ecs::concepts::ArchetypeRef auto archetype, + ice::ecs::Entity entity + ) noexcept -> ice::ecs::OperationBuilder; + + auto set( + ice::ecs::concepts::ArchetypeRef auto archetype, + ice::Span entities + ) noexcept -> ice::ecs::OperationBuilder; + + auto create( + ice::ecs::concepts::ArchetypeRef auto archetype, + ice::u32 count + ) noexcept -> ice::ecs::OperationBuilder; + + void destroy( + ice::Span entities + ) noexcept; + + void destroy( + ice::ecs::QueryStorage& queries, + ice::ecs::concepts::ArchetypeRef auto archetype + ) noexcept { - ice::Span names; - ice::Span sizes; - ice::Span offsets; - }; + ice::ecs::detail::ArchetypeInstanceInfo const* instance = nullptr; + ice::ecs::detail::DataBlock const* block = nullptr; + bool const found = queries.query_provider().query_archetype_block( + this->get_archetype(archetype), instance, block + ); + if (found) + { + while (block != nullptr) + { + if (block->block_entity_count > 0) + { + ice::ecs::Entity const* entities = reinterpret_cast( + ice::ptr_add(block->block_data, ice::usize{ instance->component_offsets[0] }) + ); + + destroy({ entities, block->block_entity_count }); + } + block = block->next; + } + } + } struct EntityOperationData; struct OperationIterator @@ -66,7 +192,21 @@ namespace ice::ecs auto end() const noexcept -> OperationIterator; private: + auto get_archetype(ice::ecs::concepts::ArchetypeRef auto ref) const noexcept -> ice::ecs::Archetype + { + if constexpr (std::is_same_v) + { + return ref; + } + else + { + return _archetypes.find_archetype_by_name(ref); + } + } + ice::Allocator& _allocator; + ice::ecs::EntityIndex& _entities; + ice::ecs::ArchetypeIndex const& _archetypes; ice::ecs::EntityOperation* _root; ice::ecs::EntityOperation* _operations; ice::ecs::EntityOperation* _free_operations; @@ -74,122 +214,129 @@ namespace ice::ecs EntityOperationData* _data_nodes; }; - void queue_set_archetype( - ice::ecs::EntityOperations& entity_operations, - ice::ecs::Entity entity, - ice::ecs::Archetype archetype - ) noexcept; - - void queue_set_archetype( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities, - ice::ecs::Archetype archetype, - bool notify_changes = false - ) noexcept; - - void queue_set_archetype_with_data( + void queue_remove_entity( ice::ecs::EntityOperations& entity_operations, - ice::Span entities, - ice::ecs::Archetype archetype, - ice::ecs::EntityOperations::ComponentInfo component_info, - ice::Span component_data, - bool notify_changes = false + ice::ecs::Entity entity_handle ) noexcept; - template - void queue_set_archetype_with_data( - ice::ecs::EntityOperations& entity_operations, - ice::ecs::Entity entity, + inline OperationBuilder::OperationBuilder( + ice::ecs::EntityOperations& operations, ice::ecs::Archetype archetype, - Components const&... components - ) noexcept; - - template - void queue_set_archetype_with_data( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities, + ice::Span entities + ) noexcept + : operations{ operations } + , archetype{ archetype } + , entities{ entities } + , filter_data_size{ 0_B } + , filter_data{ } + , mode{ 1 } + { } + + inline OperationBuilder::OperationBuilder( + ice::ecs::EntityOperations& operations, ice::ecs::Archetype archetype, - ice::Span... components - ) noexcept; - - void queue_remove_entity( - ice::ecs::EntityOperations& entity_operations, - ice::ecs::Entity entity_handle - ) noexcept; + ice::ecs::EntityIndex& index, + ice::ucount entity_count + ) noexcept + : operations{ operations } + , archetype{ archetype } + , index{ ice::addressof(index) } + , index_create_count{ entity_count } + , filter_data_size{ 0_B } + , filter_data{ } + , mode{ 2 } + { } + + inline auto EntityOperations::set( + ice::ecs::concepts::ArchetypeRef auto ref, + ice::ecs::Entity entity + ) noexcept -> ice::ecs::OperationBuilder + { + return this->set(ref, { &entity, 1 }); + } - void queue_batch_remove_entities( - ice::ecs::EntityOperations& entity_operations, + inline auto EntityOperations::set( + ice::ecs::concepts::ArchetypeRef auto ref, ice::Span entities - ) noexcept; + ) noexcept -> ice::ecs::OperationBuilder + { + return ice::ecs::OperationBuilder{ *this, this->get_archetype(ref), entities }; + } + inline auto EntityOperations::create( + ice::ecs::concepts::ArchetypeRef auto ref, + ice::ucount count + ) noexcept -> ice::ecs::OperationBuilder + { + return OperationBuilder{ *this, this->get_archetype(ref), _entities, count }; + } + + template + inline auto OperationBuilder::with_filter(T const& filter) noexcept -> OperationBuilder& + { + ICE_ASSERT_CORE(sizeof(T) <= sizeof(this->filter_data)); + this->filter_data_size = ice::size_of; + ice::memcpy(this->filter_data, ice::addressof(filter), sizeof(T)); + return *this; + } template - void queue_set_archetype_with_data( - ice::ecs::EntityOperations& entity_operations, - ice::ecs::Entity entity, - ice::ecs::Archetype archetype, - Components const&... components - ) noexcept + auto OperationBuilder::with_data(Components const&... components) noexcept -> Result { - constexpr ice::ecs::ArchetypeDefinition const& pseudo_archetype_definition = ice::ecs::Constant_ArchetypeDefinition; + static ice::ecs::ArchetypeDefinition constexpr HelperArchetype; - constexpr ice::StaticArray const idx_map = ice::ecs::detail::make_argument_idx_map( - ice::span::from_std_const(pseudo_archetype_definition.component_identifiers) + static ice::StaticArray constexpr ComponentIdxMap = ice::ecs::detail::make_argument_idx_map( + ice::span::from_std_const(HelperArchetype.component_identifiers) ); - constexpr ice::ecs::EntityOperations::ComponentInfo const component_info{ - .names = ice::span::subspan(ice::span::from_std_const(pseudo_archetype_definition.component_identifiers), 1), - .sizes = ice::span::subspan(ice::span::from_std_const(pseudo_archetype_definition.component_sizes), 1), - // We can store alignments here instead of offsets. - .offsets = ice::span::subspan(ice::span::from_std_const(pseudo_archetype_definition.component_alignments), 1) + static ice::ecs::OperationComponentInfo constexpr ComponentsInfo{ + .names = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_identifiers), 1), + .sizes = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_sizes), 1), + // We store alignments in this span just for convenience + .offsets = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_alignments), 1) }; - ice::Data const component_data_array_unsorted[]{ + ice::Data const unsorted_component_Data[]{ ice::Data{ ice::addressof(components), ice::size_of, ice::align_of }... }; - ice::StaticArray data_array; + ice::StaticArray sorted_data_array; for (ice::u32 idx = 0; idx < sizeof...(Components); ++idx) { - data_array[idx_map[idx] - 1] = component_data_array_unsorted[idx]; + sorted_data_array[ComponentIdxMap[idx] - 1] = unsorted_component_Data[idx]; } - ice::ecs::queue_set_archetype_with_data( - entity_operations, - ice::Span{ &entity, 1 }, - archetype, - component_info, - ice::span::from_std(data_array), - true - ); + return this->with_data(ComponentsInfo, ice::span::from_std_const(sorted_data_array)); } template - void queue_set_archetype_with_data( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities, - ice::ecs::Archetype archetype, - ice::Span&... out_component_spans - ) noexcept + inline auto OperationBuilder::with_data(ice::Span&... out_component_spans) noexcept -> Result { + if (ice::span::empty(entities) && mode != 2) + { + return Result{ *this }; + } + static ice::ecs::ArchetypeDefinition constexpr HelperArchetype; static ice::StaticArray constexpr ComponentIdxMap = ice::ecs::detail::make_argument_idx_map( - ice::span::from_std_const(HelperArchetype.component_identifiers) + ice::span::from_std_const(HelperArchetype.component_identifiers) ); - static ice::ecs::EntityOperations::ComponentInfo constexpr ComponentsInfo{ + static ice::ecs::OperationComponentInfo constexpr ComponentsInfo{ .names = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_identifiers), 1), .sizes = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_sizes), 1), + // We store alignments in this span just for convenience .offsets = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_alignments), 1) }; - ice::ucount const entity_count = ice::count(entities); + ice::ucount const entity_count = mode == 2 ? index_create_count : ice::count(entities); ice::ucount constexpr component_count = sizeof...(Components); - ice::meminfo additional_data_size = ice::meminfo_of * entity_count; + ice::meminfo additional_data_size = ice::meminfo{ filter_data_size, ice::ualign::b_8 }; + additional_data_size += ice::meminfo_of * entity_count; // Data for storing component info - additional_data_size.size += ice::size_of; + additional_data_size += ice::meminfo_of; additional_data_size.size += ice::span::size_bytes(ComponentsInfo.names); additional_data_size.size += ice::span::size_bytes(ComponentsInfo.sizes); additional_data_size.size += ice::span::size_bytes(ComponentsInfo.offsets); @@ -198,13 +345,25 @@ namespace ice::ecs additional_data_size.size += ((ice::usize{ alignof(Components) } + ice::size_of * entity_count) + ...); void* operation_data = nullptr; - ice::ecs::EntityOperation* operation = entity_operations.new_storage_operation( + ice::ecs::EntityOperation* operation = operations.new_storage_operation( additional_data_size, operation_data ); + ice::memcpy(operation_data, filter_data, ice::usize{ filter_data_size }); + void const* filter_ptr = operation_data; + operation_data = ice::ptr_add(operation_data, filter_data_size); + ice::ecs::Entity* entities_ptr = reinterpret_cast(operation_data); - ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); + if (mode == 1) + { + ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); + } + else + { + ICE_ASSERT_CORE(mode == 2); + index->create_many({ entities_ptr, index_create_count }); + } // Set component info object ice::StringID* names_ptr = reinterpret_cast(entities_ptr + entity_count); @@ -220,9 +379,9 @@ namespace ice::ecs operation_data = offsets_ptr + component_count; // Set the component info object with the above pointers. - EntityOperations::ComponentInfo* component_info_ptr; + OperationComponentInfo* component_info_ptr; { - component_info_ptr = reinterpret_cast(operation_data); + component_info_ptr = reinterpret_cast(operation_data); component_info_ptr->names = ice::Span{ names_ptr, component_count }; component_info_ptr->sizes = ice::Span{ sizes_ptr, component_count }; component_info_ptr->offsets = ice::Span{ offsets_ptr, component_count }; @@ -279,37 +438,47 @@ namespace ice::ecs operation->archetype = archetype; operation->entities = entities_ptr; operation->entity_count = entity_count; - operation->notify_entity_changes = false; operation->component_data = component_info_ptr; operation->component_data_size = ice::ucount(ice::ptr_distance(component_info_ptr, component_info_ptr).value); + operation->filter_data = filter_ptr; + + if (mode == 2) + { + entities = { entities_ptr, entity_count }; + } + mode = 0; + + return { *this }; } template - void queue_set_archetype_with_data( - ice::ecs::EntityOperations& entity_operations, - ice::Span entities, - ice::ecs::Archetype archetype, - ice::Span... component_spans - ) noexcept + inline auto OperationBuilder::with_data(ice::Span... component_spans) noexcept -> Result { + if (ice::span::empty(entities) && mode != 2) + { + return { *this }; + } + static ice::ecs::ArchetypeDefinition constexpr HelperArchetype; static ice::StaticArray constexpr ComponentIdxMap = ice::ecs::detail::make_argument_idx_map( ice::span::from_std_const(HelperArchetype.component_identifiers) ); - static ice::ecs::EntityOperations::ComponentInfo constexpr ComponentsInfo{ + static ice::ecs::OperationComponentInfo constexpr ComponentsInfo{ .names = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_identifiers), 1), .sizes = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_sizes), 1), + // We store alignments in this span just for convenience .offsets = ice::span::subspan(ice::span::from_std_const(HelperArchetype.component_alignments), 1) }; - ice::ucount const entity_count = ice::count(entities); + ice::ucount const entity_count = mode == 2 ? index_create_count : ice::count(entities); ice::ucount constexpr component_count = sizeof...(Components); - ice::meminfo additional_data_size = ice::meminfo_of * entity_count; + ice::meminfo additional_data_size = ice::meminfo{ filter_data_size, ice::ualign::b_8 }; + additional_data_size += ice::meminfo_of * entity_count; // Data for storing component info - additional_data_size.size += ice::size_of; + additional_data_size += ice::meminfo_of; additional_data_size.size += ice::span::size_bytes(ComponentsInfo.names); additional_data_size.size += ice::span::size_bytes(ComponentsInfo.sizes); additional_data_size.size += ice::span::size_bytes(ComponentsInfo.offsets); @@ -318,13 +487,25 @@ namespace ice::ecs additional_data_size.size += ((ice::usize{ alignof(Components) } + ice::size_of * entity_count) + ...); void* operation_data = nullptr; - ice::ecs::EntityOperation* operation = entity_operations.new_storage_operation( + ice::ecs::EntityOperation* operation = operations.new_storage_operation( additional_data_size, operation_data ); + ice::memcpy(operation_data, filter_data, ice::usize{ filter_data_size }); + void const* filter_ptr = operation_data; + operation_data = ice::ptr_add(operation_data, filter_data_size); + ice::ecs::Entity* entities_ptr = reinterpret_cast(operation_data); - ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); + if (mode == 1) + { + ice::memcpy(entities_ptr, ice::span::data(entities), ice::span::size_bytes(entities)); + } + else + { + ICE_ASSERT_CORE(mode == 2); + index->create_many({ entities_ptr, index_create_count }); + } // Set component info object ice::StringID* names_ptr = reinterpret_cast(entities_ptr + entity_count); @@ -340,9 +521,9 @@ namespace ice::ecs operation_data = offsets_ptr + component_count; // Set the component info object with the above pointers. - EntityOperations::ComponentInfo* component_info_ptr; + OperationComponentInfo* component_info_ptr; { - component_info_ptr = reinterpret_cast(operation_data); + component_info_ptr = reinterpret_cast(operation_data); component_info_ptr->names = ice::Span{ names_ptr, component_count }; component_info_ptr->sizes = ice::Span{ sizes_ptr, component_count }; component_info_ptr->offsets = ice::Span{ offsets_ptr, component_count }; @@ -398,9 +579,17 @@ namespace ice::ecs operation->archetype = archetype; operation->entities = entities_ptr; operation->entity_count = entity_count; - operation->notify_entity_changes = false; operation->component_data = component_info_ptr; operation->component_data_size = ice::ucount(ice::ptr_distance(component_info_ptr, component_info_ptr).value); + operation->filter_data = filter_ptr; + + if (mode == 2) + { + entities = { entities_ptr, entity_count }; + } + mode = 0; + + return { *this }; } } // namespace ice::ecs diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage.hxx index d4b74f49..35b8fd28 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage.hxx @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace ice::ecs @@ -27,8 +28,14 @@ namespace ice::ecs auto entities() noexcept -> ice::ecs::EntityIndex&; auto entities() const noexcept -> ice::ecs::EntityIndex const&; + auto archetypes() const noexcept -> ice::ecs::ArchetypeIndex const&; void update_archetypes() noexcept; + bool attach_destructor( + ice::ecs::Archetype archetype, + ice::ecs::detail::EntityDestructor const& destructor + ) noexcept; + void execute_operations( ice::ecs::EntityOperations const& operations, ice::ShardContainer& out_shards @@ -47,6 +54,12 @@ namespace ice::ecs ice::Span out_data_slots ) const noexcept -> ice::ucount override; + bool query_archetype_block( + ice::ecs::Archetype archetype, + ice::ecs::detail::ArchetypeInstanceInfo const*& out_instance_info, + ice::ecs::detail::DataBlock const*& out_head_block + ) const noexcept override; + protected: void query_internal( ice::Span query_info, @@ -65,6 +78,8 @@ namespace ice::ecs ice::Array _head_blocks; ice::Array _data_blocks; ice::Array _data_slots; + + ice::HashMap _destructors; }; } // namespace ice::ecs diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage_details.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage_details.hxx new file mode 100644 index 00000000..632dfe22 --- /dev/null +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_entity_storage_details.hxx @@ -0,0 +1,18 @@ +#pragma once +#include + +namespace ice::ecs::detail +{ + + // Callback that can provide data up to 2 components + using EntityDestructorCallback = void(*)(void* ctx, ice::u32, ice::ecs::Entity const*, void* c0, void* c1) noexcept; + + struct EntityDestructor + { + void* userdata; + ice::StringID identifier; + ice::StringID components[2]{ ice::StringID_Invalid, ice::StringID_Invalid }; + ice::ecs::detail::EntityDestructorCallback fn; + }; + +} // namespace ice::ecs::detail diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query.hxx index 9bd20108..c2d65a07 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query.hxx @@ -32,12 +32,15 @@ namespace ice::ecs using ResultType = typename ObjectType::ResultType; using BlockResultType = typename ObjectType::BlockResultType; using ComponentsTypeList = typename ObjectType::ComponentsTypeList; + using QueryFilter = ice::ecs::detail::DataBlockFilter::QueryFilter; + using QueryFilterArchetype = ice::ecs::Archetype; static constexpr ice::ecs::QueryType Type = QueryType::Unchecked; static constexpr ice::u32 ComponentCount = ObjectType::ComponentCount; inline Query(ObjectType const& query) noexcept : _query{ query } + , _filter{ } { } public: @@ -46,10 +49,26 @@ namespace ice::ecs return _query; } + auto filter_object() const noexcept -> QueryFilter const& + { + return _filter; + } + + auto filtered(ice::ecs::Archetype arch) noexcept -> Query& + { + _filter.arch = arch; + return *this; + } + + template + auto filtered(T const& filter) noexcept -> Query&; + auto synchronized_on(ice::TaskScheduler& scheduler) const noexcept; private: ObjectType const& _query; + QueryFilter _filter; + QueryFilterArchetype _filter_archetype; }; template @@ -60,6 +79,8 @@ namespace ice::ecs using ResultType = typename ObjectType::ResultType; using BlockResultType = typename ObjectType::BlockResultType; using ComponentsTypeList = typename ObjectType::ComponentsTypeList; + using QueryFilter = ice::ecs::detail::DataBlockFilter::QueryFilter; + using QueryFilterArchetype = ice::ecs::Archetype; static constexpr ice::ecs::QueryType Type = QueryType::Synchronized; static constexpr ice::u32 ComponentCount = ObjectType::ComponentCount; @@ -72,6 +93,7 @@ namespace ice::ecs inline Query(Query&& other) noexcept : _query{ other._query } , _requires_release{ ice::exchange(other._requires_release, false) } + , _filter{ other._filter } { } inline ~Query() noexcept @@ -90,11 +112,34 @@ namespace ice::ecs return _query; } + auto filter_object() const noexcept -> QueryFilter const& + { + return _filter; + } + + auto filtered(ice::ecs::Archetype arch) noexcept -> Query& + { + _filter.arch = arch; + return *this; + } + + template + auto filtered(T const& filter) noexcept -> Query&; + private: ObjectType const& _query; + QueryFilter _filter; bool _requires_release; }; + template + template + auto Query::filtered(T const& filter) noexcept -> Query& + { + _filter = QueryFilter{ filter }; + return *this; + } + template auto Query::synchronized_on(ice::TaskScheduler& scheduler) const noexcept { @@ -104,13 +149,21 @@ namespace ice::ecs inline auto await_resume() const noexcept -> ice::ecs::Query { - return Query{ this->query_object(), this->_is_empty == false }; + return Query{ this->query_object(), this->filter_object(), this->_is_empty == false }; } }; return Awaitable{ _query, scheduler.schedule()._queue }; } + template + template + auto Query::filtered(T const& filter) noexcept -> Query& + { + _filter = QueryFilter{ filter }; + return *this; + } + template Query(QueryObject&&) noexcept -> Query; template Query(QueryObject const&) noexcept -> Query; diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query_awaitable.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query_awaitable.hxx index 039718e8..fae9c6f0 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query_awaitable.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query_awaitable.hxx @@ -72,7 +72,8 @@ namespace ice::ecs template inline auto entity_count( - ice::ecs::QueryObject const& query + ice::ecs::QueryObject const& query, + ice::ecs::detail::DataBlockFilter::QueryFilter filter ) noexcept -> ice::ucount; } // namespace query @@ -169,12 +170,16 @@ namespace ice::ecs return internal_query_is_resumable(internal_get_query(userdata), self._awaited_access_stage); } - constexpr QueryAwaitableBase(QueryArg const& query, ice::TaskQueue& task_queue) noexcept + constexpr QueryAwaitableBase( + QueryArg const& query, + ice::ecs::detail::DataBlockFilter::QueryFilter filter, + ice::TaskQueue& task_queue + ) noexcept : ice::TaskAwaitableBase{ ._params = {.modifier = ice::TaskAwaitableModifier::CustomResumer } } , _task_queue{ ice::addressof(task_queue) } , _custom_resumer{ } , _awaited_access_stage{ } - , _is_empty{ ice::ecs::query::entity_count(query) == 0 } + , _is_empty{ ice::ecs::query::entity_count(query, filter) == 0 } { _custom_resumer.ud_resumer = (void*)ice::addressof(query); _custom_resumer.fn_resumer = internal_query_resumer; diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query_builder.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query_builder.hxx index 525089c4..0c4a945e 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query_builder.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query_builder.hxx @@ -70,6 +70,11 @@ namespace ice::ecs )->object_query(); } + auto filter_object() const noexcept -> ice::ecs::detail::DataBlockFilter::QueryFilter + { + return {}; + } + auto synchronized_on(ice::TaskScheduler& scheduler) const noexcept { struct Awaitable : ice::ecs::detail::QueryAwaitableBase @@ -82,7 +87,22 @@ namespace ice::ecs } }; - return Awaitable{ query_object(), scheduler.schedule()._queue }; + return Awaitable{ query_object(), filter_object(), scheduler.schedule()._queue }; + } + + auto filtered(ice::ecs::Archetype arch) noexcept -> ice::ecs::Query + { + Query result = ice::ecs::Query{ query_object() }; + result.filtered(arch); + return result; + } + + template + auto filtered(T const& filter) noexcept -> ice::ecs::Query + { + Query result = ice::ecs::Query{ query_object() }; + result.filtered(filter); + return result; } operator ice::ecs::Query () const noexcept diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query_operations.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query_operations.hxx index 8a15bde6..65e90bcf 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query_operations.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query_operations.hxx @@ -266,7 +266,8 @@ namespace ice::ecs template inline auto entity_count( - ice::ecs::QueryObject const& query + ice::ecs::QueryObject const& query, + ice::ecs::detail::DataBlockFilter::QueryFilter filter ) noexcept -> ice::ucount { // On a query with multiple parts we only want to check the blocks of the main part. @@ -279,7 +280,10 @@ namespace ice::ecs ice::ecs::detail::DataBlock const* it = head_block->next; while (it != nullptr) { - result += it->block_entity_count; + if (filter.check(it)) + { + result += it->block_entity_count; + } it = it->next; } } @@ -384,6 +388,7 @@ namespace ice::ecs template inline auto for_each_entity_gen( ice::ecs::QueryObject const& query_object, + ice::ecs::detail::DataBlockFilter::QueryFilter filter, QueryObjectOwner ) noexcept -> ice::Generator::ResultType> { @@ -401,9 +406,9 @@ namespace ice::ecs // We skip the first block because it will be always empty. ICE_ASSERT_CORE(block->block_entity_count == 0); - block = block->next; + block = filter.next(block); - while (block != nullptr) + while (block != nullptr && filter.check(block)) { for (ice::u32 arg_idx = 0; arg_idx < component_count; ++arg_idx) { @@ -444,7 +449,7 @@ namespace ice::ecs entity_idx += 1; } - block = block->next; + block = filter.next(block); } } } @@ -452,6 +457,7 @@ namespace ice::ecs template inline auto for_each_entity( ice::ecs::QueryObject const& query_object, + ice::ecs::detail::DataBlockFilter::QueryFilter filter, Fn&& fn ) noexcept { @@ -470,9 +476,9 @@ namespace ice::ecs // We skip the first block because it will be always empty. ICE_ASSERT_CORE(block->block_entity_count == 0); - block = block->next; + block = filter.next(block); - while (block != nullptr) + while (block != nullptr && filter.check(block)) { for (ice::u32 arg_idx = 0; arg_idx < component_count; ++arg_idx) { @@ -516,7 +522,7 @@ namespace ice::ecs ); } - block = block->next; + block = filter.next(block); } } } @@ -524,6 +530,7 @@ namespace ice::ecs template inline auto for_each_block_gen( ice::ecs::QueryObject const& query, + ice::ecs::detail::DataBlockFilter::QueryFilter filter, QueryObjectOwner ) noexcept -> ice::Generator::BlockResultType> { @@ -542,9 +549,9 @@ namespace ice::ecs // We skip the first block because it will be always empty. ICE_ASSERT_CORE(block->block_entity_count == 0); - block = block->next; + block = filter.next(block); - while (block != nullptr) + while (block != nullptr && filter.check(block)) { for (ice::u32 arg_idx = 0; arg_idx < component_count; ++arg_idx) { @@ -565,7 +572,7 @@ namespace ice::ecs co_yield ice::ecs::detail::create_block_tuple(block->block_entity_count, helper_pointer_array, MainPart{}); - block = block->next; + block = filter.next(block); } } } @@ -573,6 +580,7 @@ namespace ice::ecs template inline auto for_each_block( ice::ecs::QueryObject const& query, + ice::ecs::detail::DataBlockFilter::QueryFilter filter, Fn&& fn ) noexcept { @@ -590,7 +598,7 @@ namespace ice::ecs // We skip the first block because it will be always empty. ICE_ASSERT_CORE(block->block_entity_count == 0); - block = block->next; + block = filter.next(block); while (block != nullptr) { @@ -617,7 +625,7 @@ namespace ice::ecs helper_pointer_array ); - block = block->next; + block = filter.next(block); } } } @@ -627,7 +635,7 @@ namespace ice::ecs template inline auto TraitQueryOperations::entity_count(this Self&& self) noexcept -> ice::ucount { - return ice::ecs::query::entity_count(self.query_object()); + return ice::ecs::query::entity_count(self.query_object(), self.filter_object()); } template @@ -649,18 +657,18 @@ namespace ice::ecs { if constexpr (ice::clear_type_t::Type == QueryType::Synchronized && std::is_rvalue_reference_v) { - return ice::ecs::query::for_each_entity_gen(self.query_object(), ice::move(self)); + return ice::ecs::query::for_each_entity_gen(self.query_object(), self.filter_object(), ice::move(self)); } else { - return ice::ecs::query::for_each_entity_gen(self.query_object(), int{}); + return ice::ecs::query::for_each_entity_gen(self.query_object(), self.filter_object(), int{}); } } template inline void TraitQueryOperations::for_each_entity(this Self&& self, Fn&& fn) noexcept { - return ice::ecs::query::for_each_entity(self.query_object(), ice::forward(fn)); + return ice::ecs::query::for_each_entity(self.query_object(), self.filter_object(), ice::forward(fn)); } template @@ -668,18 +676,18 @@ namespace ice::ecs { if constexpr (ice::clear_type_t::Type == QueryType::Synchronized && std::is_rvalue_reference_v) { - return ice::ecs::query::for_each_block_gen(self.query_object(), ice::move(self)); + return ice::ecs::query::for_each_block_gen(self.query_object(), self.filter_object(), ice::move(self)); } else { - return ice::ecs::query::for_each_block_gen(self.query_object()); + return ice::ecs::query::for_each_block_gen(self.query_object(), self.filter_object(), int{}); } } template inline void TraitQueryOperations::for_each_block(this Self&& self, Fn&& fn) noexcept { - return ice::ecs::query::for_each_block(self.query_object(), ice::forward(fn)); + return ice::ecs::query::for_each_block(self.query_object(), self.filter_object(), ice::forward(fn)); } } // namespace ice::ecs diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query_provider.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query_provider.hxx index 4539bb9c..f5cef37d 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query_provider.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query_provider.hxx @@ -39,6 +39,12 @@ namespace ice::ecs ice::Span out_data_slots ) const noexcept -> ice::ucount = 0; + virtual bool query_archetype_block( + ice::ecs::Archetype archetype, + ice::ecs::detail::ArchetypeInstanceInfo const*& out_instance_info, + ice::ecs::detail::DataBlock const*& out_head_block + ) const noexcept = 0; + protected: virtual void query_internal( ice::Span query_info, diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx index 83b3be1b..674174f8 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_query_storage_entry.hxx @@ -26,6 +26,7 @@ namespace ice::ecs public: using QueryObjectType = ice::ecs::QueryObject; using QueryObjectEntryType = ice::ecs::QueryObjectEntry, Tags...>; + using QueryArchFilterList = ice::Array; public: static auto hash_value() noexcept -> ice::u64; diff --git a/source/code/iceshard/engine/public/ice/ecs/ecs_types.hxx b/source/code/iceshard/engine/public/ice/ecs/ecs_types.hxx index ba307ede..96ee8e8d 100644 --- a/source/code/iceshard/engine/public/ice/ecs/ecs_types.hxx +++ b/source/code/iceshard/engine/public/ice/ecs/ecs_types.hxx @@ -44,4 +44,11 @@ namespace ice::ecs struct QueryView; + namespace detail + { + + struct ArchetypeInstanceInfo; + + } // namespace detail + } // namespace ice::ecs diff --git a/source/code/iceshard/iceshard/private/iceshard_frame.cxx b/source/code/iceshard/iceshard/private/iceshard_frame.cxx index c5197e5f..b22eff31 100644 --- a/source/code/iceshard/iceshard/private/iceshard_frame.cxx +++ b/source/code/iceshard/iceshard/private/iceshard_frame.cxx @@ -12,7 +12,12 @@ namespace ice : _frame_data{ frame_data } , _data{ _frame_data._fwd_allocator } , _shards{ _frame_data._fwd_allocator } - , _operations{ _frame_data._fwd_allocator, 16 } + , _operations{ + _frame_data._fwd_allocator, + frame_data._engine.entity_index(), + frame_data._engine.entity_archetypes(), + 16 + } , _task_groups{ _frame_data._fwd_allocator } { ice::array::reserve(_task_groups, 32); diff --git a/source/code/iceshard/iceshard/private/iceshard_world.cxx b/source/code/iceshard/iceshard/private/iceshard_world.cxx index d133b76e..d888d638 100644 --- a/source/code/iceshard/iceshard/private/iceshard_world.cxx +++ b/source/code/iceshard/iceshard/private/iceshard_world.cxx @@ -21,7 +21,7 @@ namespace ice , _entities{ entities } , _entity_storage{ entity_storage } , _entity_query_storage{ _allocator, _entity_storage } - , _entity_operations{ _allocator, 16 } + , _entity_operations{ _allocator, entities, entity_storage.archetypes(), 16 } , _traits{ ice::move(traits) } , _tasks_launcher{ context, ice::array::slice(_traits), task_tracker } , _devui{ create_devui(_allocator, context) } diff --git a/source/code/modules/imgui_module/private/imgui_system.cxx b/source/code/modules/imgui_module/private/imgui_system.cxx index aa70c892..58054892 100644 --- a/source/code/modules/imgui_module/private/imgui_system.cxx +++ b/source/code/modules/imgui_module/private/imgui_system.cxx @@ -128,10 +128,10 @@ namespace ice::devui static bool show_demo = false; ImGui::NewFrame(); -# if ISP_WINDOWS +#if ISP_WINDOWS ImGuizmo::BeginFrame(); ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); -# endif +#endif { if (ImGui::BeginMainMenuBar()) @@ -142,7 +142,7 @@ namespace ice::devui { for (ImGuiDevUIWidget& runtime : _widget_manager.widgets()) { - ice::DevUIWidgetInfo const& info = runtime.widget->info; + ice::DevUIWidgetInfo const& info = runtime.widget->widget_info; if (ice::string::starts_with(info.category, category) && runtime.widget->build_mainmenu(runtime)) { _widget_frame.mainmenu(info, runtime); diff --git a/source/code/modules/imgui_module/private/widgets/imgui_devui_manager.cxx b/source/code/modules/imgui_module/private/widgets/imgui_devui_manager.cxx index 6baef803..f606a169 100644 --- a/source/code/modules/imgui_module/private/widgets/imgui_devui_manager.cxx +++ b/source/code/modules/imgui_module/private/widgets/imgui_devui_manager.cxx @@ -85,11 +85,11 @@ namespace ice::devui ImGui::PushID(widget.widget); if (ImGui::TableNextColumn()) // Name { - ImGui::StringUnformatted(widget.widget->info.name); + ImGui::StringUnformatted(widget.widget->widget_info.name); } if (ImGui::TableNextColumn()) // Category { - ImGui::StringUnformatted(widget.widget->info.category); + ImGui::StringUnformatted(widget.widget->widget_info.category); } if (ImGui::TableNextColumn()) // Visible { diff --git a/source/code/modules/vulkan_renderer/private/vk_allocator.cxx b/source/code/modules/vulkan_renderer/private/vk_allocator.cxx index e43e1a6b..ae4a22fc 100644 --- a/source/code/modules/vulkan_renderer/private/vk_allocator.cxx +++ b/source/code/modules/vulkan_renderer/private/vk_allocator.cxx @@ -233,7 +233,11 @@ namespace ice::render::vk auto VulkanAllocator::vulkan_callbacks() const noexcept -> VkAllocationCallbacks const* { +#if 0 // Disable until we actually have the time to fix this return &_vulkan_callbacks; +#else + return nullptr; +#endif } } // namespace ice::render::vk diff --git a/source/code/systems/asset_system/private/asset_shelve.cxx b/source/code/systems/asset_system/private/asset_shelve.cxx index ffb4f759..15a27fee 100644 --- a/source/code/systems/asset_system/private/asset_shelve.cxx +++ b/source/code/systems/asset_system/private/asset_shelve.cxx @@ -22,27 +22,14 @@ namespace ice : storage{ storage } , definition{ definition } , compiler{ compiler } - , compiler_context{ .userdata = nullptr } , _allocator{ alloc } , _asset_resources{ alloc } { ice::hashmap::reserve(_asset_resources, 25); - - if (compiler != nullptr && compiler->fn_prepare_context) - { - bool const valid_context = compiler->fn_prepare_context(_allocator, compiler_context, definition.asset_params); - ICE_ASSERT(valid_context, "Failed to prepare resource compiler context"); - } } AssetShelve::~AssetShelve() noexcept { - if (compiler != nullptr && compiler->fn_cleanup_context) - { - bool const valid_context = compiler->fn_cleanup_context(_allocator, compiler_context); - ICE_ASSERT(valid_context, "Failed to prepare resource compiler context"); - } - for (ice::AssetEntry* entry : _asset_resources) { _allocator.destroy(entry); diff --git a/source/code/systems/asset_system/private/asset_shelve.hxx b/source/code/systems/asset_system/private/asset_shelve.hxx index a5d410c0..551291ba 100644 --- a/source/code/systems/asset_system/private/asset_shelve.hxx +++ b/source/code/systems/asset_system/private/asset_shelve.hxx @@ -51,7 +51,6 @@ namespace ice ice::DefaultAssetStorage& storage; ice::AssetCategoryDefinition const& definition; ice::ResourceCompiler const* compiler; - ice::ResourceCompilerCtx compiler_context; class DevUI; diff --git a/source/code/systems/asset_system/private/asset_storage.cxx b/source/code/systems/asset_system/private/asset_storage.cxx index 341660ca..78c4193d 100644 --- a/source/code/systems/asset_system/private/asset_storage.cxx +++ b/source/code/systems/asset_system/private/asset_storage.cxx @@ -70,7 +70,17 @@ namespace ice ice::Array dependencies{ alloc }; // Not used currently ice::ResourceHandle resource = ice::asset_data_resource(transaction.asset._data); - ice::ResourceCompilerCtx& ctx = transaction.asset._shelve->compiler_context; + + ice::ResourceCompilerCtx ctx{ nullptr };// = transaction.asset._shelve->compiler_context; + ice::api::resource_compiler::v1::ResourceCompilerCtxCleanup cleanup{ alloc, ctx, compiler.fn_cleanup_context }; + if (compiler.fn_prepare_context) + { + ICE_ASSERT_CORE(compiler.fn_cleanup_context); + if (compiler.fn_prepare_context(alloc, ctx, {}) == false) + { + co_return false; + } + } // Early return if sources or dependencies couldn't be collected if (compiler.fn_collect_sources(ctx, resource, resource_tracker, sources) == false @@ -88,7 +98,7 @@ namespace ice ice::Array> tasks{ alloc }; ice::array::reserve(tasks, ice::array::count(sources)); - static auto fn_validate = [&ctx]( + auto fn_validate = [&ctx]( ice::ResourceCompiler const& compiler, ice::ResourceHandle const& source, ice::ResourceTracker& tracker, @@ -121,7 +131,7 @@ namespace ice ice::Array compiled_sources{ alloc }; ice::array::resize(compiled_sources, ice::array::count(sources)); - static auto fn_compile = [&ctx]( + auto fn_compile = [&ctx]( ice::ResourceCompiler const& compiler, ice::ResourceHandle const& source, ice::ResourceTracker& tracker, diff --git a/source/code/systems/resource_system/private/resource_provider_filesystem_devui.cxx b/source/code/systems/resource_system/private/resource_provider_filesystem_devui.cxx index a4163d7b..d8eb8fc4 100644 --- a/source/code/systems/resource_system/private/resource_provider_filesystem_devui.cxx +++ b/source/code/systems/resource_system/private/resource_provider_filesystem_devui.cxx @@ -25,7 +25,7 @@ namespace ice { if (ImGui::BeginMenu("Resource Providers", true)) { - ImGui::MenuItem(ice::string::begin(info.name), nullptr, &state.active); + ImGui::MenuItem(ice::string::begin(widget_info.name), nullptr, &state.active); ImGui::EndMenu(); } return false; diff --git a/source/code/systems/resource_system/private/resource_provider_hailstorm_devui.cxx b/source/code/systems/resource_system/private/resource_provider_hailstorm_devui.cxx index 48c9cb36..094f7c5f 100644 --- a/source/code/systems/resource_system/private/resource_provider_hailstorm_devui.cxx +++ b/source/code/systems/resource_system/private/resource_provider_hailstorm_devui.cxx @@ -96,7 +96,7 @@ namespace ice { if (ImGui::BeginMenu("Resource Providers", true)) { - ImGui::MenuItem(ice::string::begin(info.name), nullptr, &state.active); + ImGui::MenuItem(ice::string::begin(widget_info.name), nullptr, &state.active); ImGui::EndMenu(); } return false; diff --git a/source/code/test/private/game.cxx b/source/code/test/private/game.cxx index 2784bb2e..78c41adf 100644 --- a/source/code/test/private/game.cxx +++ b/source/code/test/private/game.cxx @@ -135,8 +135,6 @@ struct TestTrait : public ice::Trait ice::Allocator& _alloc; ice::Timer timer; - ice::ecs::Archetype _arch_test; - ice::ecs::EntityOperations* _ops; ice::ecs::Entity _my_entity[10000]; @@ -145,8 +143,7 @@ struct TestTrait : public ice::Trait update.engine.entity_index().create_many(_my_entity); _ops = ice::addressof(update.world.entity_operations()); - - ice::ecs::queue_set_archetype(*_ops, _my_entity, _arch_test); + _ops->set("test-arch", _my_entity); ICE_LOG(LogSeverity::Retail, LogTag::Game, "Test Activated!"); timer = ice::timer::create_timer(update.clock, 100_Tms); @@ -160,7 +157,7 @@ struct TestTrait : public ice::Trait query().tags().for_each_block( [&](ice::ucount count, ice::ecs::Entity const* entities) noexcept { - ice::ecs::queue_batch_remove_entities(*_ops, { entities, count }); + _ops->destroy({ entities, count }); } );