diff --git a/README.md b/README.md index d738460..b8da5c4 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ - [Chunk](#chunk) - [Builder](#builder) - [Changelog](#changelog) + - [v1.9.0](#v190) - [v1.8.0](#v180) - [v1.7.0](#v170) - [v1.6.0](#v160) @@ -1565,6 +1566,11 @@ builder_mt:destruction_policy :: id -> builder ## Changelog +### v1.9.0 + +- Performance improvements of the [`evolved.destroy`](#evolveddestroy) and [`evolved.batch_destroy`](#evolvedbatch_destroy) functions +- Ensured deterministic chunk ordering to improve processing consistency across runs + ### v1.8.0 - Added the new [`evolved.REALLOC`](#evolvedrealloc) and [`evolved.COMPMOVE`](#evolvedcompmove) fragment traits that allow customizing component storages diff --git a/develop/ROADMAP.md b/develop/ROADMAP.md index 5b53895..863bc8d 100644 --- a/develop/ROADMAP.md +++ b/develop/ROADMAP.md @@ -5,16 +5,11 @@ - observers and events - add INDEX fragment trait - use compact prefix-tree for chunks -- optional ffi component storages ## Thoughts - We should have a way to not copy components on deferred spawn/clone -- Not all assoc_list_remove operations need to keep order, we can have an unordered variant also -- We still have several places where we use __lua_next without deterministic order, we should fix that - Having a light version of the gargabe collector can be useful for some use-cases -- We can shrink the table pool tables on garbage collection if they are too large -- Should we sort chunk children by fragment id? - Basic default component value as true looks awful, should we use something else? ## Known Issues diff --git a/develop/all.lua b/develop/all.lua index 7e01a14..a4ff731 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -16,6 +16,7 @@ require 'develop.testing.system_as_query_tests' require 'develop.benchmarks.clone_bmarks' require 'develop.benchmarks.common_bmarks' +require 'develop.benchmarks.destroy_bmarks' require 'develop.benchmarks.migration_bmarks' require 'develop.benchmarks.process_bmarks' require 'develop.benchmarks.spawn_bmarks' diff --git a/develop/benchmarks/destroy_bmarks.lua b/develop/benchmarks/destroy_bmarks.lua new file mode 100644 index 0000000..ba38b85 --- /dev/null +++ b/develop/benchmarks/destroy_bmarks.lua @@ -0,0 +1,56 @@ +local evo = require 'evolved' +local basics = require 'develop.basics' + +evo.debug_mode(false) + +local N = 1000 + +print '----------------------------------------' + +basics.describe_bench(string.format('Destroy Benchmarks: Acquire and Release %d ids', N), + function(tables) + local id = evo.id + local destroy = evo.destroy + + for i = 1, N do + tables[i] = id() + end + + for i = 1, N do + destroy(tables[i]) + end + end, function() + return {} + end) + +basics.describe_bench(string.format('Destroy Benchmarks: Acquire and Release %d double ids', N), + function(tables) + local id = evo.id + local destroy = evo.destroy + + for i = 1, N, 2 do + tables[i], tables[i + 1] = id(2) + end + + for i = 1, N, 2 do + destroy(tables[i], tables[i + 1]) + end + end, function() + return {} + end) + +basics.describe_bench(string.format('Destroy Benchmarks: Acquire and Release %d triple ids', N), + function(tables) + local id = evo.id + local destroy = evo.destroy + + for i = 1, N, 3 do + tables[i], tables[i + 1], tables[i + 2] = id(3) + end + + for i = 1, N, 3 do + destroy(tables[i], tables[i + 1], tables[i + 2]) + end + end, function() + return {} + end) diff --git a/develop/testing/main_tests.lua b/develop/testing/main_tests.lua index 9917c5e..8f21ef0 100644 --- a/develop/testing/main_tests.lua +++ b/develop/testing/main_tests.lua @@ -2302,27 +2302,66 @@ do evo.set(e2, f2, 44) do - local iter, state = evo.execute(q) - local chunk = iter(state) - assert(chunk and chunk ~= evo.chunk(f1)) + local e1_count = 0 + local e2_count = 0 + + for _, entity_list, entity_count in evo.execute(q) do + for i = 1, entity_count do + if entity_list[i] == e1 then + e1_count = e1_count + 1 + elseif entity_list[i] == e2 then + e2_count = e2_count + 1 + end + end + end + + assert(e1_count == 1) + assert(e2_count == 1) end evo.set(q, evo.EXCLUDES, { f2 }) do - local iter, state = evo.execute(q) - local chunk = iter(state) - assert(chunk and chunk ~= evo.chunk(f1)) + local e1_count = 0 + local e2_count = 0 + + for chunk, entity_list, entity_count in evo.execute(q) do + assert(not chunk:has(f2)) + + for i = 1, entity_count do + if entity_list[i] == e1 then + e1_count = e1_count + 1 + elseif entity_list[i] == e2 then + e2_count = e2_count + 1 + end + end + end + + assert(e1_count == 1) + assert(e2_count == 0) end evo.set(q, evo.INCLUDES, { f1 }) do - local iter, state = evo.execute(q) - local chunk, entity_list, entity_count = iter(state) - assert(chunk == evo.chunk(f1)) - assert(entity_list and entity_list[1] == e1) - assert(entity_count == 1) + local e1_count = 0 + local e2_count = 0 + + for chunk, entity_list, entity_count in evo.execute(q) do + assert(chunk:has(f1)) + assert(not chunk:has(f2)) + + for i = 1, entity_count do + if entity_list[i] == e1 then + e1_count = e1_count + 1 + elseif entity_list[i] == e2 then + e2_count = e2_count + 1 + end + end + end + + assert(e1_count == 1) + assert(e2_count == 0) end end diff --git a/evolved.lua b/evolved.lua index 6562d7f..36efd2d 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1,7 +1,7 @@ local evolved = { __HOMEPAGE = 'https://github.com/BlackMATov/evolved.lua', __DESCRIPTION = 'Evolved ECS (Entity-Component-System) for Lua', - __VERSION = '1.8.0', + __VERSION = '1.9.0', __LICENSE = [[ MIT License @@ -131,7 +131,10 @@ local __defer_points = {} ---@type integer[] local __defer_length = 0 ---@type integer local __defer_bytecode = {} ---@type any[] -local __root_chunks = {} ---@type table +local __root_set = {} ---@type table +local __root_list = {} ---@type evolved.chunk[] +local __root_count = 0 ---@type integer + local __major_chunks = {} ---@type table> local __minor_chunks = {} ---@type table> @@ -250,7 +253,7 @@ local __lua_table_new = (function() return function() return {} end end)() ----@type fun(tab: table) +---@type fun(tab: table, no_clear_array_part?: boolean, no_clear_hash_part?: boolean) local __lua_table_clear = (function() -- https://luajit.org/extensions.html -- https://create.roblox.com/docs/reference/engine/libraries/table#clear @@ -269,9 +272,16 @@ local __lua_table_clear = (function() end ---@param tab table - return function(tab) - for i = 1, #tab do tab[i] = nil end - for k in __lua_next, tab do tab[k] = nil end + ---@param no_clear_array_part? boolean + ---@param no_clear_hash_part? boolean + return function(tab, no_clear_array_part, no_clear_hash_part) + if not no_clear_array_part then + for i = 1, #tab do tab[i] = nil end + end + + if not no_clear_hash_part then + for k in __lua_next, tab do tab[k] = nil end + end end end)() @@ -692,6 +702,19 @@ local __table_pool_tag = { __count = 9, } +---@type table +local __table_pool_reserve = { + [__table_pool_tag.bytecode] = 16, + [__table_pool_tag.chunk_list] = 16, + [__table_pool_tag.system_list] = 16, + [__table_pool_tag.each_state] = 16, + [__table_pool_tag.execute_state] = 16, + [__table_pool_tag.entity_list] = 16, + [__table_pool_tag.fragment_set] = 16, + [__table_pool_tag.fragment_list] = 16, + [__table_pool_tag.component_table] = 16, +} + ---@class (exact) evolved.table_pool ---@field package __size integer ---@field package [integer] table @@ -699,14 +722,20 @@ local __table_pool_tag = { ---@type table local __tagged_table_pools = (function() local table_pools = __lua_table_new(__table_pool_tag.__count) - local table_pool_reserve = 16 - for tag = 1, __table_pool_tag.__count do + for table_pool_tag = 1, __table_pool_tag.__count do + local table_pool_reserve = __table_pool_reserve[table_pool_tag] + ---@type evolved.table_pool local table_pool = __lua_table_new(table_pool_reserve) - for i = 1, table_pool_reserve do table_pool[i] = {} end + + for table_pool_index = 1, table_pool_reserve do + table_pool[table_pool_index] = {} + end + table_pool.__size = table_pool_reserve - table_pools[tag] = table_pool + + table_pools[table_pool_tag] = table_pool end return table_pools @@ -734,13 +763,14 @@ end ---@param tag evolved.table_pool_tag ---@param table table ----@param no_clear? boolean -local function __release_table(tag, table, no_clear) +---@param no_clear_array_part boolean +---@param no_clear_hash_part boolean +local function __release_table(tag, table, no_clear_array_part, no_clear_hash_part) local table_pool = __tagged_table_pools[tag] local table_pool_size = table_pool.__size - if not no_clear then - __lua_table_clear(table) + if not no_clear_array_part or not no_clear_hash_part then + __lua_table_clear(table, no_clear_array_part, no_clear_hash_part) end table_pool_size = table_pool_size + 1 @@ -757,6 +787,7 @@ end local __list_new local __list_dup +local __list_lwr ---@param reserve? integer ---@return any[] @@ -785,6 +816,42 @@ function __list_dup(list) return dup_list end +---@generic V +---@param list V[] +---@param item V +---@param comp? fun(a: V, b: V): boolean +---@return integer lower_bound_index +---@nodiscard +function __list_lwr(list, item, comp) + local lower, upper = 1, #list + + if comp then + while lower <= upper do + local middle = lower + (upper - lower) / 2 + middle = middle - middle % 1 -- fast math.floor + + if comp(item, list[middle]) then + upper = middle - 1 + else + lower = middle + 1 + end + end + else + while lower <= upper do + local middle = lower + (upper - lower) / 2 + middle = middle - middle % 1 -- fast math.floor + + if item < list[middle] then + upper = middle - 1 + else + lower = middle + 1 + end + end + end + + return lower +end + --- --- --- @@ -1064,7 +1131,9 @@ local __evolved_get local __evolved_set local __evolved_remove local __evolved_clear +local __evolved_clear_one local __evolved_destroy +local __evolved_destroy_one local __evolved_batch_set local __evolved_batch_remove @@ -1098,6 +1167,11 @@ local __new_chunk local __default_realloc local __default_compmove +local __add_root_chunk +local __remove_root_chunk +local __add_child_chunk +local __remove_child_chunk + local __update_chunk_caches local __update_chunk_queries local __update_chunk_storages @@ -1145,8 +1219,13 @@ local __purge_chunk local __expand_chunk local __shrink_chunk local __clear_chunk_list +local __clear_entity_one +local __clear_entity_list +local __destroy_entity_one local __destroy_entity_list +local __destroy_fragment_one local __destroy_fragment_list +local __destroy_fragment_stack local __chunk_set local __chunk_remove @@ -1254,23 +1333,10 @@ function __new_chunk(chunk_parent, chunk_fragment) __has_required_fragments = false, }, __chunk_mt) - if chunk_parent then - chunk.__parent = chunk_parent - - chunk_parent.__child_count = __assoc_list_insert_ex( - chunk_parent.__child_set, chunk_parent.__child_list, chunk_parent.__child_count, - chunk) - - chunk_parent.__with_fragment_edges[chunk_fragment] = chunk - chunk.__without_fragment_edges[chunk_fragment] = chunk_parent - end - if not chunk_parent then - if __root_chunks[chunk_fragment] ~= nil then - __error_fmt('unexpected root chunk %s', - __lua_tostring(__root_chunks[chunk_fragment])) - end - __root_chunks[chunk_fragment] = chunk + __add_root_chunk(chunk) + else + __add_child_chunk(chunk, chunk_parent) end do @@ -1337,6 +1403,107 @@ function __default_compmove(src, f, e, t, dst) __lua_table_move(src, f, e, t, dst) end +---@param root evolved.chunk +function __add_root_chunk(root) + local root_index = __list_lwr(__root_list, root, function(a, b) + return a.__fragment < b.__fragment + end) + + for sib_root_index = __root_count, root_index, -1 do + local sib_root = __root_list[sib_root_index] + __root_set[sib_root.__fragment] = sib_root_index + 1 + __root_list[sib_root_index + 1] = sib_root + end + + __root_set[root.__fragment] = root_index + __root_list[root_index] = root + __root_count = __root_count + 1 +end + +---@param root evolved.chunk +function __remove_root_chunk(root) + if root.__parent then + __error_fmt('unexpected root chunk: (%s)', + __lua_tostring(root)) + return + end + + local root_index = __root_set[root.__fragment] + + if not root_index or __root_list[root_index] ~= root then + __error_fmt('unexpected root chunk: (%s)', + __lua_tostring(root)) + return + end + + for sib_root_index = root_index, __root_count - 1 do + local sib_root = __root_list[sib_root_index + 1] + __root_set[sib_root.__fragment] = sib_root_index + __root_list[sib_root_index] = sib_root + end + + __root_set[root.__fragment] = nil + __root_list[__root_count] = nil + __root_count = __root_count - 1 +end + +---@param child evolved.chunk +---@param parent evolved.chunk +function __add_child_chunk(child, parent) + local child_index = __list_lwr(parent.__child_list, child, function(a, b) + return a.__fragment < b.__fragment + end) + + for sib_child_index = parent.__child_count, child_index, -1 do + local sib_child = parent.__child_list[sib_child_index] + parent.__child_set[sib_child] = sib_child_index + 1 + parent.__child_list[sib_child_index + 1] = sib_child + end + + parent.__child_set[child] = child_index + parent.__child_list[child_index] = child + parent.__child_count = parent.__child_count + 1 + + parent.__with_fragment_edges[child.__fragment] = child + child.__without_fragment_edges[child.__fragment] = parent + + child.__parent = parent +end + +---@param child evolved.chunk +function __remove_child_chunk(child) + local parent = child.__parent + + if not parent then + __error_fmt('unexpected child chunk: (%s)', + __lua_tostring(child)) + return + end + + local child_index = parent.__child_set[child] + + if not child_index or parent.__child_list[child_index] ~= child then + __error_fmt('unexpected child chunk: (%s)', + __lua_tostring(child)) + return + end + + for sib_child_index = child_index, parent.__child_count - 1 do + local next_sib_child = parent.__child_list[sib_child_index + 1] + parent.__child_set[next_sib_child] = sib_child_index + parent.__child_list[sib_child_index] = next_sib_child + end + + parent.__child_set[child] = nil + parent.__child_list[parent.__child_count] = nil + parent.__child_count = parent.__child_count - 1 + + parent.__with_fragment_edges[child.__fragment] = nil + child.__without_fragment_edges[child.__fragment] = nil + + child.__parent = nil +end + ---@param chunk evolved.chunk function __update_chunk_caches(chunk) local chunk_parent = chunk.__parent @@ -1613,9 +1780,8 @@ end ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_major_chunks(major, trace, ...) - ---@type evolved.chunk[] - local chunk_stack = __acquire_table(__table_pool_tag.chunk_list) - local chunk_stack_size = 0 + local chunk_stack ---@type evolved.chunk[]? + local chunk_stack_size = 0 ---@type integer do local major_chunks = __major_chunks[major] @@ -1623,6 +1789,9 @@ function __trace_major_chunks(major, trace, ...) local major_chunk_count = major_chunks and major_chunks.__item_count or 0 if major_chunk_count > 0 then + ---@type evolved.chunk[] + chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + __lua_table_move( major_chunk_list, 1, major_chunk_count, chunk_stack_size + 1, chunk_stack) @@ -1632,6 +1801,7 @@ function __trace_major_chunks(major, trace, ...) end while chunk_stack_size > 0 do + ---@cast chunk_stack -? local chunk = chunk_stack[chunk_stack_size] trace(chunk, ...) @@ -1651,15 +1821,17 @@ function __trace_major_chunks(major, trace, ...) end end - __release_table(__table_pool_tag.chunk_list, chunk_stack, true) + if chunk_stack then + __release_table(__table_pool_tag.chunk_list, chunk_stack, + chunk_stack_size == 0, true) + end end ---@param minor evolved.fragment ---@param trace fun(chunk: evolved.chunk, ...: any) ---@param ... any additional trace arguments function __trace_minor_chunks(minor, trace, ...) - ---@type evolved.chunk[] - local chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + local chunk_stack ---@type evolved.chunk[]? local chunk_stack_size = 0 do @@ -1668,6 +1840,9 @@ function __trace_minor_chunks(minor, trace, ...) local minor_chunk_count = minor_chunks and minor_chunks.__item_count or 0 if minor_chunk_count > 0 then + ---@type evolved.chunk[] + chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + __lua_table_move( minor_chunk_list, 1, minor_chunk_count, chunk_stack_size + 1, chunk_stack) @@ -1677,6 +1852,7 @@ function __trace_minor_chunks(minor, trace, ...) end while chunk_stack_size > 0 do + ---@cast chunk_stack -? local chunk = chunk_stack[chunk_stack_size] trace(chunk, ...) @@ -1685,7 +1861,10 @@ function __trace_minor_chunks(minor, trace, ...) chunk_stack_size = chunk_stack_size - 1 end - __release_table(__table_pool_tag.chunk_list, chunk_stack, true) + if chunk_stack then + __release_table(__table_pool_tag.chunk_list, chunk_stack, + chunk_stack_size == 0, true) + end end ---@param query evolved.query @@ -1850,7 +2029,7 @@ end ---@nodiscard function __chunk_with_fragment(chunk, fragment) if not chunk then - local root_chunk = __root_chunks[fragment] + local root_chunk = __root_list[__root_set[fragment]] return root_chunk or __new_chunk(nil, fragment) end @@ -2036,7 +2215,7 @@ end ---@return evolved.chunk ---@nodiscard function __chunk_fragments(head_fragment, ...) - local chunk = __root_chunks[head_fragment] + local chunk = __root_list[__root_set[head_fragment]] or __new_chunk(nil, head_fragment) for tail_fragment_index = 1, __lua_select('#', ...) do @@ -2059,7 +2238,7 @@ function __chunk_components(components) return end - local chunk = __root_chunks[head_fragment] + local chunk = __root_list[__root_set[head_fragment]] or __new_chunk(nil, head_fragment) for tail_fragment in __lua_next, components, head_fragment do @@ -2909,25 +3088,14 @@ function __purge_chunk(chunk) __shrink_chunk(chunk, 0) end - local chunk_parent = chunk.__parent - local chunk_fragment = chunk.__fragment - - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count - - local with_fragment_edges = chunk.__with_fragment_edges - local without_fragment_edges = chunk.__without_fragment_edges - - if not chunk_parent then - if __root_chunks[chunk_fragment] ~= chunk then - __error_fmt('unexpected root chunk %s', - __lua_tostring(__root_chunks[chunk_fragment])) - end - __root_chunks[chunk_fragment] = nil + if not chunk.__parent then + __remove_root_chunk(chunk) + else + __remove_child_chunk(chunk) end do - local major = chunk_fragment + local major = chunk.__fragment local major_chunks = __major_chunks[major] if major_chunks and __assoc_list_remove(major_chunks, chunk) == 0 then @@ -2935,8 +3103,8 @@ function __purge_chunk(chunk) end end - for chunk_fragment_index = 1, chunk_fragment_count do - local minor = chunk_fragment_list[chunk_fragment_index] + for chunk_fragment_index = 1, chunk.__fragment_count do + local minor = chunk.__fragment_list[chunk_fragment_index] local minor_chunks = __minor_chunks[minor] if minor_chunks and __assoc_list_remove(minor_chunks, chunk) == 0 then @@ -2944,19 +3112,13 @@ function __purge_chunk(chunk) end end - if chunk_parent then - chunk.__parent, chunk_parent.__child_count = nil, __assoc_list_remove_ex( - chunk_parent.__child_set, chunk_parent.__child_list, chunk_parent.__child_count, - chunk) - end - - for with_fragment, with_fragment_edge in __lua_next, with_fragment_edges do - with_fragment_edges[with_fragment] = nil + for with_fragment, with_fragment_edge in __lua_next, chunk.__with_fragment_edges do + chunk.__with_fragment_edges[with_fragment] = nil with_fragment_edge.__without_fragment_edges[with_fragment] = nil end - for without_fragment, without_fragment_edge in __lua_next, without_fragment_edges do - without_fragment_edges[without_fragment] = nil + for without_fragment, without_fragment_edge in __lua_next, chunk.__without_fragment_edges do + chunk.__without_fragment_edges[without_fragment] = nil without_fragment_edge.__with_fragment_edges[without_fragment] = nil end @@ -3117,83 +3279,171 @@ function __clear_chunk_list(chunk_list, chunk_count) end end +---@param entity evolved.entity +function __clear_entity_one(entity) + if __defer_depth <= 0 then + __error_fmt('this operation should be deferred') + end + + local entity_chunks = __entity_chunks + local entity_places = __entity_places + + local entity_primary = entity % 2 ^ 20 + + if __freelist_ids[entity_primary] ~= entity then + -- nothing to clear from non-alive entities + else + local chunk = entity_chunks[entity_primary] + local place = entity_places[entity_primary] + + if chunk and chunk.__has_remove_hooks then + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages + + for chunk_fragment_index = 1, chunk_fragment_count do + local fragment = chunk_fragment_list[chunk_fragment_index] + + ---@type evolved.remove_hook? + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + + if fragment_on_remove then + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + local old_component = component_storage[place] + fragment_on_remove(entity, fragment, old_component) + else + fragment_on_remove(entity, fragment) + end + end + end + end + + if chunk then + __detach_entity(chunk, place) + + entity_chunks[entity_primary] = nil + entity_places[entity_primary] = nil + + __structural_changes = __structural_changes + 1 + end + end +end + ---@param entity_list evolved.entity[] ---@param entity_count integer -function __destroy_entity_list(entity_list, entity_count) +function __clear_entity_list(entity_list, entity_count) if __defer_depth <= 0 then __error_fmt('this operation should be deferred') end - if entity_count == 0 then - return + for entity_index = 1, entity_count do + local entity = entity_list[entity_index] + __clear_entity_one(entity) + end +end + +---@param entity evolved.entity +function __destroy_entity_one(entity) + if __defer_depth <= 0 then + __error_fmt('this operation should be deferred') end local entity_chunks = __entity_chunks local entity_places = __entity_places - for entity_index = 1, entity_count do - local entity = entity_list[entity_index] - local entity_primary = entity % 2 ^ 20 + local entity_primary = entity % 2 ^ 20 - if __freelist_ids[entity_primary] ~= entity then - -- this entity is not alive, nothing to purge - else - local chunk = entity_chunks[entity_primary] - local place = entity_places[entity_primary] + if __freelist_ids[entity_primary] ~= entity then + -- this entity is not alive, nothing to purge + else + local chunk = entity_chunks[entity_primary] + local place = entity_places[entity_primary] - if chunk and chunk.__has_remove_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count - local chunk_component_indices = chunk.__component_indices - local chunk_component_storages = chunk.__component_storages + if chunk and chunk.__has_remove_hooks then + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + for chunk_fragment_index = 1, chunk_fragment_count do + local fragment = chunk_fragment_list[chunk_fragment_index] - ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + ---@type evolved.remove_hook? + local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) - if fragment_on_remove then - local component_index = chunk_component_indices[fragment] + if fragment_on_remove then + local component_index = chunk_component_indices[fragment] - if component_index then - local component_storage = chunk_component_storages[component_index] - local old_component = component_storage[place] - fragment_on_remove(entity, fragment, old_component) - else - fragment_on_remove(entity, fragment) - end + if component_index then + local component_storage = chunk_component_storages[component_index] + local old_component = component_storage[place] + fragment_on_remove(entity, fragment, old_component) + else + fragment_on_remove(entity, fragment) end end end + end - if chunk then - __detach_entity(chunk, place) - - entity_chunks[entity_primary] = nil - entity_places[entity_primary] = nil + if chunk then + __detach_entity(chunk, place) - __structural_changes = __structural_changes + 1 - end + entity_chunks[entity_primary] = nil + entity_places[entity_primary] = nil - __release_id(entity) + __structural_changes = __structural_changes + 1 end + + __release_id(entity) end end ----@param fragment_list evolved.fragment[] ----@param fragment_count integer -function __destroy_fragment_list(fragment_list, fragment_count) +---@param entity_list evolved.entity[] +---@param entity_count integer +function __destroy_entity_list(entity_list, entity_count) if __defer_depth <= 0 then __error_fmt('this operation should be deferred') end - if fragment_count == 0 then - return + for entity_index = 1, entity_count do + local entity = entity_list[entity_index] + __destroy_entity_one(entity) end +end - ---@type table - local processed_fragment_set = __acquire_table(__table_pool_tag.fragment_set) +---@param fragment evolved.fragment +function __destroy_fragment_one(fragment) + if __defer_depth <= 0 then + __error_fmt('this operation should be deferred') + end + + ---@type evolved.fragment[] + local processing_fragment_stack = __acquire_table(__table_pool_tag.fragment_list) + local processing_fragment_stack_size = 0 + + do + processing_fragment_stack_size = processing_fragment_stack_size + 1 + processing_fragment_stack[processing_fragment_stack_size] = fragment + end + + __destroy_fragment_stack( + processing_fragment_stack, + processing_fragment_stack_size) + + __release_table(__table_pool_tag.fragment_list, processing_fragment_stack, + true, true) +end + +---@param fragment_list evolved.fragment[] +---@param fragment_count integer +function __destroy_fragment_list(fragment_list, fragment_count) + if __defer_depth <= 0 then + __error_fmt('this operation should be deferred') + end ---@type evolved.fragment[] local processing_fragment_stack = __acquire_table(__table_pool_tag.fragment_list) @@ -3207,17 +3457,33 @@ function __destroy_fragment_list(fragment_list, fragment_count) processing_fragment_stack_size = processing_fragment_stack_size + fragment_count end + __destroy_fragment_stack( + processing_fragment_stack, + processing_fragment_stack_size) + + __release_table(__table_pool_tag.fragment_list, processing_fragment_stack, + true, true) +end + +---@param processing_fragment_stack evolved.fragment[] +---@param processing_fragment_stack_size integer +function __destroy_fragment_stack(processing_fragment_stack, processing_fragment_stack_size) + if __defer_depth <= 0 then + __error_fmt('this operation should be deferred') + end + + ---@type table + local processed_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + ---@type evolved.fragment[] local releasing_fragment_list = __acquire_table(__table_pool_tag.fragment_list) - local releasing_fragment_count = 0 + local releasing_fragment_count = 0 ---@type integer - ---@type evolved.fragment[] - local destroy_entity_policy_fragment_list = __acquire_table(__table_pool_tag.fragment_list) - local destroy_entity_policy_fragment_count = 0 + local destroy_entity_policy_fragment_list ---@type evolved.fragment[]? + local destroy_entity_policy_fragment_count = 0 ---@type integer - ---@type evolved.fragment[] - local remove_fragment_policy_fragment_list = __acquire_table(__table_pool_tag.fragment_list) - local remove_fragment_policy_fragment_count = 0 + local remove_fragment_policy_fragment_list ---@type evolved.fragment[]? + local remove_fragment_policy_fragment_count = 0 ---@type integer while processing_fragment_stack_size > 0 do local processing_fragment = processing_fragment_stack[processing_fragment_stack_size] @@ -3230,13 +3496,20 @@ function __destroy_fragment_list(fragment_list, fragment_count) else processed_fragment_set[processing_fragment] = true - releasing_fragment_count = releasing_fragment_count + 1 - releasing_fragment_list[releasing_fragment_count] = processing_fragment + do + releasing_fragment_count = releasing_fragment_count + 1 + releasing_fragment_list[releasing_fragment_count] = processing_fragment + end local processing_fragment_destruction_policy = __evolved_get(processing_fragment, __DESTRUCTION_POLICY) or __DESTRUCTION_POLICY_REMOVE_FRAGMENT if processing_fragment_destruction_policy == __DESTRUCTION_POLICY_DESTROY_ENTITY then + if not destroy_entity_policy_fragment_list then + ---@type evolved.fragment[] + destroy_entity_policy_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + end + destroy_entity_policy_fragment_count = destroy_entity_policy_fragment_count + 1 destroy_entity_policy_fragment_list[destroy_entity_policy_fragment_count] = processing_fragment @@ -3251,6 +3524,11 @@ function __destroy_fragment_list(fragment_list, fragment_count) processing_fragment_stack_size = processing_fragment_stack_size + chunk_entity_count end) elseif processing_fragment_destruction_policy == __DESTRUCTION_POLICY_REMOVE_FRAGMENT then + if not remove_fragment_policy_fragment_list then + ---@type evolved.fragment[] + remove_fragment_policy_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + end + remove_fragment_policy_fragment_count = remove_fragment_policy_fragment_count + 1 remove_fragment_policy_fragment_list[remove_fragment_policy_fragment_count] = processing_fragment else @@ -3260,39 +3538,35 @@ function __destroy_fragment_list(fragment_list, fragment_count) end end - __release_table(__table_pool_tag.fragment_set, processed_fragment_set) - __release_table(__table_pool_tag.fragment_list, processing_fragment_stack, true) - - if destroy_entity_policy_fragment_count > 0 then + if destroy_entity_policy_fragment_list then for i = 1, destroy_entity_policy_fragment_count do - local fragment = destroy_entity_policy_fragment_list[i] - - __trace_minor_chunks(fragment, __chunk_clear) + local minor = destroy_entity_policy_fragment_list[i] + __trace_minor_chunks(minor, __chunk_clear) end - __release_table(__table_pool_tag.fragment_list, destroy_entity_policy_fragment_list) - else - __release_table(__table_pool_tag.fragment_list, destroy_entity_policy_fragment_list, true) + __release_table(__table_pool_tag.fragment_list, destroy_entity_policy_fragment_list, + destroy_entity_policy_fragment_count == 0, true) end - if remove_fragment_policy_fragment_count > 0 then + if remove_fragment_policy_fragment_list then for i = 1, remove_fragment_policy_fragment_count do - local fragment = remove_fragment_policy_fragment_list[i] - - __trace_minor_chunks(fragment, __chunk_remove, fragment) + local minor = remove_fragment_policy_fragment_list[i] + __trace_minor_chunks(minor, __chunk_remove, minor) end - __release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list) - else - __release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list, true) + __release_table(__table_pool_tag.fragment_list, remove_fragment_policy_fragment_list, + remove_fragment_policy_fragment_count == 0, true) end if releasing_fragment_count > 0 then __destroy_entity_list(releasing_fragment_list, releasing_fragment_count) - __release_table(__table_pool_tag.fragment_list, releasing_fragment_list) - else - __release_table(__table_pool_tag.fragment_list, releasing_fragment_list, true) end + + __release_table(__table_pool_tag.fragment_list, releasing_fragment_list, + releasing_fragment_count == 0, true) + + __release_table(__table_pool_tag.fragment_set, processed_fragment_set, + true, false) end ---@param old_chunk evolved.chunk @@ -4038,7 +4312,7 @@ __defer_ops[__defer_op.spawn_entity] = function(bytes, index) __spawn_entity(chunk, entity, component_table2, component_mapper) if component_table2 then - __release_table(__table_pool_tag.component_table, component_table2) + __release_table(__table_pool_tag.component_table, component_table2, true, false) end end __evolved_commit() @@ -4095,11 +4369,11 @@ __defer_ops[__defer_op.multi_spawn_entity] = function(bytes, index) __multi_spawn_entity(chunk, entity_list2, entity_count, component_table2, component_mapper) if entity_list2 then - __release_table(__table_pool_tag.entity_list, entity_list2) + __release_table(__table_pool_tag.entity_list, entity_list2, false, true) end if component_table2 then - __release_table(__table_pool_tag.component_table, component_table2) + __release_table(__table_pool_tag.component_table, component_table2, true, false) end end __evolved_commit() @@ -4146,7 +4420,7 @@ __defer_ops[__defer_op.clone_entity] = function(bytes, index) __clone_entity(prefab, entity, component_table2, component_mapper) if component_table2 then - __release_table(__table_pool_tag.component_table, component_table2) + __release_table(__table_pool_tag.component_table, component_table2, true, false) end end __evolved_commit() @@ -4203,11 +4477,11 @@ __defer_ops[__defer_op.multi_clone_entity] = function(bytes, index) __multi_clone_entity(prefab, entity_list2, entity_count, component_table2, component_mapper) if entity_list2 then - __release_table(__table_pool_tag.entity_list, entity_list2) + __release_table(__table_pool_tag.entity_list, entity_list2, false, true) end if component_table2 then - __release_table(__table_pool_tag.component_table, component_table2) + __release_table(__table_pool_tag.component_table, component_table2, true, false) end end __evolved_commit() @@ -4249,7 +4523,7 @@ function __iterator_fns.__each_iterator(each_state) return fragment, component_storage and component_storage[entity_place] end - __release_table(__table_pool_tag.each_state, each_state, true) + __release_table(__table_pool_tag.each_state, each_state, true, true) end ---@type evolved.execute_iterator @@ -4301,8 +4575,8 @@ function __iterator_fns.__execute_iterator(execute_state) end end - __release_table(__table_pool_tag.chunk_list, chunk_stack, true) - __release_table(__table_pool_tag.execute_state, execute_state, true) + __release_table(__table_pool_tag.chunk_list, chunk_stack, true, true) + __release_table(__table_pool_tag.execute_state, execute_state, true, true) end ---@param query evolved.query @@ -4362,7 +4636,8 @@ local function __system_process(system, ...) end end - __release_table(__table_pool_tag.system_list, subsystem_list) + __release_table(__table_pool_tag.system_list, subsystem_list, + group_subsystem_count == 0, true) end end @@ -4516,7 +4791,7 @@ function __evolved_commit() bytecode_index = bytecode_index + op(bytecode, bytecode_index + 1) + 1 end - __release_table(__table_pool_tag.bytecode, bytecode, true) + __release_table(__table_pool_tag.bytecode, bytecode, true, true) return true end @@ -5254,6 +5529,10 @@ function __evolved_clear(...) return end + if argument_count == 1 then + return __evolved_clear_one(...) + end + if __defer_depth > 0 then __defer_call_hook(__evolved_clear, ...) return @@ -5262,8 +5541,8 @@ function __evolved_clear(...) __evolved_defer() do - local entity_chunks = __entity_chunks - local entity_places = __entity_places + local purging_entity_list ---@type evolved.entity[]? + local purging_entity_count = 0 ---@type integer for argument_index = 1, argument_count do ---@type evolved.entity @@ -5273,44 +5552,42 @@ function __evolved_clear(...) if __freelist_ids[entity_primary] ~= entity then -- nothing to clear from non-alive entities else - local chunk = entity_chunks[entity_primary] - local place = entity_places[entity_primary] - - if chunk and chunk.__has_remove_hooks then - local chunk_fragment_list = chunk.__fragment_list - local chunk_fragment_count = chunk.__fragment_count - local chunk_component_indices = chunk.__component_indices - local chunk_component_storages = chunk.__component_storages + if not purging_entity_list then + ---@type evolved.entity[] + purging_entity_list = __acquire_table(__table_pool_tag.entity_list) + end - for chunk_fragment_index = 1, chunk_fragment_count do - local fragment = chunk_fragment_list[chunk_fragment_index] + purging_entity_count = purging_entity_count + 1 + purging_entity_list[purging_entity_count] = entity + end + end - ---@type evolved.remove_hook? - local fragment_on_remove = __evolved_get(fragment, __ON_REMOVE) + if purging_entity_list then + __clear_entity_list(purging_entity_list, purging_entity_count) + __release_table(__table_pool_tag.entity_list, purging_entity_list, + purging_entity_count == 0, true) + end + end - if fragment_on_remove then - local component_index = chunk_component_indices[fragment] + __evolved_commit() +end - if component_index then - local component_storage = chunk_component_storages[component_index] - local old_component = component_storage[place] - fragment_on_remove(entity, fragment, old_component) - else - fragment_on_remove(entity, fragment) - end - end - end - end +---@param entity evolved.entity +function __evolved_clear_one(entity) + if __defer_depth > 0 then + __defer_call_hook(__evolved_clear_one, entity) + return + end - if chunk then - __detach_entity(chunk, place) + __evolved_defer() - entity_chunks[entity_primary] = nil - entity_places[entity_primary] = nil + do + local entity_primary = entity % 2 ^ 20 - __structural_changes = __structural_changes + 1 - end - end + if __freelist_ids[entity_primary] ~= entity then + -- nothing to clear from non-alive entities + else + __clear_entity_one(entity) end end @@ -5325,6 +5602,10 @@ function __evolved_destroy(...) return end + if argument_count == 1 then + return __evolved_destroy_one(...) + end + if __defer_depth > 0 then __defer_call_hook(__evolved_destroy, ...) return @@ -5335,13 +5616,11 @@ function __evolved_destroy(...) do local minor_chunks = __minor_chunks - ---@type evolved.entity[] - local purging_entity_list = __acquire_table(__table_pool_tag.entity_list) - local purging_entity_count = 0 + local purging_entity_list ---@type evolved.entity[]? + local purging_entity_count = 0 ---@type integer - ---@type evolved.fragment[] - local purging_fragment_list = __acquire_table(__table_pool_tag.fragment_list) - local purging_fragment_count = 0 + local purging_fragment_list ---@type evolved.fragment[]? + local purging_fragment_count = 0 ---@type integer for argument_index = 1, argument_count do ---@type evolved.entity @@ -5354,27 +5633,63 @@ function __evolved_destroy(...) local is_fragment = minor_chunks[entity] if not is_fragment then + if not purging_entity_list then + ---@type evolved.entity[] + purging_entity_list = __acquire_table(__table_pool_tag.entity_list) + end + purging_entity_count = purging_entity_count + 1 purging_entity_list[purging_entity_count] = entity else + if not purging_fragment_list then + ---@type evolved.fragment[] + purging_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + end + purging_fragment_count = purging_fragment_count + 1 purging_fragment_list[purging_fragment_count] = entity end end end - if purging_fragment_count > 0 then + if purging_fragment_list then __destroy_fragment_list(purging_fragment_list, purging_fragment_count) - __release_table(__table_pool_tag.fragment_list, purging_fragment_list) - else - __release_table(__table_pool_tag.fragment_list, purging_fragment_list, true) + __release_table(__table_pool_tag.fragment_list, purging_fragment_list, + purging_fragment_count == 0, true) end - if purging_entity_count > 0 then + if purging_entity_list then __destroy_entity_list(purging_entity_list, purging_entity_count) - __release_table(__table_pool_tag.entity_list, purging_entity_list) + __release_table(__table_pool_tag.entity_list, purging_entity_list, + purging_entity_count == 0, true) + end + end + + __evolved_commit() +end + +---@param entity evolved.entity +function __evolved_destroy_one(entity) + if __defer_depth > 0 then + __defer_call_hook(__evolved_destroy_one, entity) + return + end + + __evolved_defer() + + do + local entity_primary = entity % 2 ^ 20 + + if __freelist_ids[entity_primary] ~= entity then + -- nothing to destroy from non-alive entities else - __release_table(__table_pool_tag.entity_list, purging_entity_list, true) + local is_fragment = __minor_chunks[entity] + + if not is_fragment then + __destroy_entity_one(entity) + else + __destroy_fragment_one(entity) + end end end @@ -5409,21 +5724,29 @@ function __evolved_batch_set(query, fragment, component) __evolved_defer() do - ---@type evolved.chunk[] - local chunk_list = __acquire_table(__table_pool_tag.chunk_list) - local chunk_count = 0 + local chunk_list ---@type evolved.chunk[]? + local chunk_count = 0 ---@type integer for chunk in __evolved_execute(query) do + if not chunk_list then + ---@type evolved.chunk[] + chunk_list = __acquire_table(__table_pool_tag.chunk_list) + end + chunk_count = chunk_count + 1 chunk_list[chunk_count] = chunk end for chunk_index = 1, chunk_count do + ---@cast chunk_list -? local chunk = chunk_list[chunk_index] __chunk_set(chunk, fragment, component) end - __release_table(__table_pool_tag.chunk_list, chunk_list) + if chunk_list then + __release_table(__table_pool_tag.chunk_list, chunk_list, + chunk_count == 0, true) + end end __evolved_commit() @@ -5453,21 +5776,29 @@ function __evolved_batch_remove(query, ...) __evolved_defer() do - ---@type evolved.chunk[] - local chunk_list = __acquire_table(__table_pool_tag.chunk_list) - local chunk_count = 0 + local chunk_list ---@type evolved.chunk[]? + local chunk_count = 0 ---@type integer for chunk in __evolved_execute(query) do + if not chunk_list then + ---@type evolved.chunk[] + chunk_list = __acquire_table(__table_pool_tag.chunk_list) + end + chunk_count = chunk_count + 1 chunk_list[chunk_count] = chunk end for chunk_index = 1, chunk_count do + ---@cast chunk_list -? local chunk = chunk_list[chunk_index] __chunk_remove(chunk, ...) end - __release_table(__table_pool_tag.chunk_list, chunk_list) + if chunk_list then + __release_table(__table_pool_tag.chunk_list, chunk_list, + chunk_count == 0, true) + end end __evolved_commit() @@ -5489,9 +5820,8 @@ function __evolved_batch_clear(...) __evolved_defer() do - ---@type evolved.chunk[] - local chunk_list = __acquire_table(__table_pool_tag.chunk_list) - local chunk_count = 0 + local chunk_list ---@type evolved.chunk[]? + local chunk_count = 0 ---@type integer for argument_index = 1, argument_count do ---@type evolved.query @@ -5503,18 +5833,22 @@ function __evolved_batch_clear(...) __id_name(query)) else for chunk in __evolved_execute(query) do + if not chunk_list then + ---@type evolved.chunk[] + chunk_list = __acquire_table(__table_pool_tag.chunk_list) + end + chunk_count = chunk_count + 1 chunk_list[chunk_count] = chunk end end end - for chunk_index = 1, chunk_count do - local chunk = chunk_list[chunk_index] - __chunk_clear(chunk) + if chunk_list then + __clear_chunk_list(chunk_list, chunk_count) + __release_table(__table_pool_tag.chunk_list, chunk_list, + chunk_count == 0, true) end - - __release_table(__table_pool_tag.chunk_list, chunk_list) end __evolved_commit() @@ -5538,17 +5872,14 @@ function __evolved_batch_destroy(...) do local minor_chunks = __minor_chunks - ---@type evolved.chunk[] - local clearing_chunk_list = __acquire_table(__table_pool_tag.chunk_list) - local clearing_chunk_count = 0 + local clearing_chunk_list ---@type evolved.chunk[]? + local clearing_chunk_count = 0 ---@type integer - ---@type evolved.entity[] - local purging_entity_list = __acquire_table(__table_pool_tag.entity_list) - local purging_entity_count = 0 + local purging_entity_list ---@type evolved.entity[]? + local purging_entity_count = 0 ---@type integer - ---@type evolved.fragment[] - local purging_fragment_list = __acquire_table(__table_pool_tag.fragment_list) - local purging_fragment_count = 0 + local purging_fragment_list ---@type evolved.fragment[]? + local purging_fragment_count = 0 ---@type integer for argument_index = 1, argument_count do ---@type evolved.query @@ -5560,8 +5891,15 @@ function __evolved_batch_destroy(...) __id_name(query)) else for chunk, entity_list, entity_count in __evolved_execute(query) do - clearing_chunk_count = clearing_chunk_count + 1 - clearing_chunk_list[clearing_chunk_count] = chunk + do + if not clearing_chunk_list then + ---@type evolved.chunk[] + clearing_chunk_list = __acquire_table(__table_pool_tag.chunk_list) + end + + clearing_chunk_count = clearing_chunk_count + 1 + clearing_chunk_list[clearing_chunk_count] = chunk + end for i = 1, entity_count do local entity = entity_list[i] @@ -5569,9 +5907,19 @@ function __evolved_batch_destroy(...) local is_fragment = minor_chunks[entity] if not is_fragment then + if not purging_entity_list then + ---@type evolved.entity[] + purging_entity_list = __acquire_table(__table_pool_tag.entity_list) + end + purging_entity_count = purging_entity_count + 1 purging_entity_list[purging_entity_count] = entity else + if not purging_fragment_list then + ---@type evolved.fragment[] + purging_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + end + purging_fragment_count = purging_fragment_count + 1 purging_fragment_list[purging_fragment_count] = entity end @@ -5580,25 +5928,22 @@ function __evolved_batch_destroy(...) end end - if purging_fragment_count > 0 then + if purging_fragment_list then __destroy_fragment_list(purging_fragment_list, purging_fragment_count) - __release_table(__table_pool_tag.fragment_list, purging_fragment_list) - else - __release_table(__table_pool_tag.fragment_list, purging_fragment_list, true) + __release_table(__table_pool_tag.fragment_list, purging_fragment_list, + purging_fragment_count == 0, true) end - if clearing_chunk_count > 0 then + if clearing_chunk_list then __clear_chunk_list(clearing_chunk_list, clearing_chunk_count) - __release_table(__table_pool_tag.chunk_list, clearing_chunk_list) - else - __release_table(__table_pool_tag.chunk_list, clearing_chunk_list, true) + __release_table(__table_pool_tag.chunk_list, clearing_chunk_list, + clearing_chunk_count == 0, true) end - if purging_entity_count > 0 then + if purging_entity_list then __destroy_entity_list(purging_entity_list, purging_entity_count) - __release_table(__table_pool_tag.entity_list, purging_entity_list) - else - __release_table(__table_pool_tag.entity_list, purging_entity_list, true) + __release_table(__table_pool_tag.entity_list, purging_entity_list, + purging_entity_count == 0, true) end end @@ -5675,7 +6020,9 @@ function __evolved_execute(query) chunk_stack_size = chunk_stack_size + query_chunk_count end elseif query_exclude_count > 0 then - for _, root_chunk in __lua_next, __root_chunks do + for root_index = 1, __root_count do + local root_chunk = __root_list[root_index] + local is_root_chunk_matched = not root_chunk.__has_explicit_fragments and not query_exclude_set[root_chunk.__fragment] @@ -5686,7 +6033,9 @@ function __evolved_execute(query) end end else - for _, root_chunk in __lua_next, __root_chunks do + for root_index = 1, __root_count do + local root_chunk = __root_list[root_index] + local is_root_chunk_matched = not root_chunk.__has_explicit_fragments @@ -5781,15 +6130,20 @@ function __evolved_collect_garbage() __evolved_defer() do - ---@type evolved.chunk[] - local working_chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + local working_chunk_stack ---@type evolved.chunk[]? local working_chunk_stack_size = 0 - ---@type evolved.chunk[] - local postorder_chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + local postorder_chunk_stack ---@type evolved.chunk[]? local postorder_chunk_stack_size = 0 - for _, root_chunk in __lua_next, __root_chunks do + for root_index = 1, __root_count do + local root_chunk = __root_list[root_index] + + if not working_chunk_stack then + ---@type evolved.chunk[] + working_chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + end + working_chunk_stack_size = working_chunk_stack_size + 1 working_chunk_stack[working_chunk_stack_size] = root_chunk @@ -5810,13 +6164,22 @@ function __evolved_collect_garbage() working_chunk_stack_size = working_chunk_stack_size + working_chunk_child_count end + if not postorder_chunk_stack then + ---@type evolved.chunk[] + postorder_chunk_stack = __acquire_table(__table_pool_tag.chunk_list) + end + postorder_chunk_stack_size = postorder_chunk_stack_size + 1 postorder_chunk_stack[postorder_chunk_stack_size] = working_chunk end end - for postorder_chunk_index = postorder_chunk_stack_size, 1, -1 do - local postorder_chunk = postorder_chunk_stack[postorder_chunk_index] + while postorder_chunk_stack_size > 0 do + ---@cast postorder_chunk_stack -? + local postorder_chunk = postorder_chunk_stack[postorder_chunk_stack_size] + + postorder_chunk_stack[postorder_chunk_stack_size] = nil + postorder_chunk_stack_size = postorder_chunk_stack_size - 1 local postorder_chunk_child_count = postorder_chunk.__child_count local postorder_chunk_entity_count = postorder_chunk.__entity_count @@ -5836,15 +6199,30 @@ function __evolved_collect_garbage() end end - __release_table(__table_pool_tag.chunk_list, working_chunk_stack) - __release_table(__table_pool_tag.chunk_list, postorder_chunk_stack) + if working_chunk_stack then + __release_table(__table_pool_tag.chunk_list, working_chunk_stack, + working_chunk_stack_size == 0, true) + end + + if postorder_chunk_stack then + __release_table(__table_pool_tag.chunk_list, postorder_chunk_stack, + postorder_chunk_stack_size == 0, true) + end end - for table_tag = 1, __table_pool_tag.__count do - local table_pool = __tagged_table_pools[table_tag] - for pool_index = 1, table_pool.__size do - table_pool[pool_index] = {} + for table_pool_tag = 1, __table_pool_tag.__count do + local table_pool_reserve = __table_pool_reserve[table_pool_tag] + + ---@type evolved.table_pool + local new_table_pool = __lua_table_new(table_pool_reserve) + + for table_pool_index = 1, table_pool_reserve do + new_table_pool[table_pool_index] = {} end + + new_table_pool.__size = table_pool_reserve + + __tagged_table_pools[table_pool_tag] = new_table_pool end do @@ -5891,7 +6269,6 @@ function __evolved_collect_garbage() __defer_bytecode = new_defer_bytecode end - __evolved_commit() end diff --git a/rockspecs/evolved.lua-1.9.0-0.rockspec b/rockspecs/evolved.lua-1.9.0-0.rockspec new file mode 100644 index 0000000..099e3a8 --- /dev/null +++ b/rockspecs/evolved.lua-1.9.0-0.rockspec @@ -0,0 +1,34 @@ +rockspec_format = "3.0" +package = "evolved.lua" +version = "1.9.0-0" +source = { + url = "git://github.com/BlackMATov/evolved.lua", + tag = "v1.9.0", +} +description = { + homepage = "https://github.com/BlackMATov/evolved.lua", + summary = "Evolved ECS (Entity-Component-System) for Lua", + detailed = [[ + `evolved.lua` is a fast and flexible ECS (Entity-Component-System) library for Lua. + It is designed to be simple and easy to use, while providing all the features needed to create complex systems with blazing performance. + ]], + license = "MIT", + labels = { + "ecs", + "entity", + "entities", + "component", + "components", + "entity-component", + "entity-component-system", + }, +} +dependencies = { + "lua >= 5.1", +} +build = { + type = "builtin", + modules = { + evolved = "evolved.lua", + } +}