From 632232b9b1e872de1c73ff5996002353c7577ed0 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 11 Sep 2025 01:27:54 +0700 Subject: [PATCH 1/4] dummy multi spawn api --- README.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- evolved.lua | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 568c5e9..e506525 100644 --- a/README.md +++ b/README.md @@ -1103,7 +1103,10 @@ defer :: boolean commit :: boolean spawn :: ? -> entity +multi_spawn :: integer, ? -> entity[] + clone :: entity -> ? -> entity +multi_clone :: integer, entity -> ? -> entity alive :: entity -> boolean alive_all :: entity... -> boolean @@ -1163,7 +1166,10 @@ chunk_mt:components :: fragment... -> storage... builder :: builder builder_mt:spawn :: entity +builder_mt:multi_spawn :: integer -> entity[] + builder_mt:clone :: entity -> entity +builder_mt:multi_clone :: integer, entity -> entity[] builder_mt:has :: fragment -> boolean builder_mt:has_all :: fragment... -> boolean @@ -1342,15 +1348,34 @@ function evolved.commit() end function evolved.spawn(components) end ``` +### `evolved.multi_spawn` + +```lua +---@param count integer +---@param components? table +---@return evolved.entity[] entity_list +function evolved.multi_spawn(count, components) end +``` + ### `evolved.clone` ```lua ---@param prefab evolved.entity ---@param components? table ----@return evolved.entity +---@return evolved.entity entity function evolved.clone(prefab, components) end ``` +### `evolved.multi_clone` + +```lua +---@param count integer +---@param prefab evolved.entity +---@param components? table +---@return evolved.entity[] entity_list +function evolved.multi_clone(count, prefab, components) end +``` + ### `evolved.alive` ```lua @@ -1646,18 +1671,35 @@ function evolved.builder() end #### `evolved.builder_mt:spawn` ```lua ----@return evolved.entity +---@return evolved.entity entity function evolved.builder_mt:spawn() end ``` +#### `evolved.builder_mt:multi_spawn` + +```lua +---@param count integer +---@return evolved.entity[] entity_list +function evolved.builder_mt:multi_spawn(count) end +``` + #### `evolved.builder_mt:clone` ```lua ---@param prefab evolved.entity ----@return evolved.entity +---@return evolved.entity entity function evolved.builder_mt:clone(prefab) end ``` +#### `evolved.builder_mt:multi_clone` + +```lua +---@param count integer +---@param prefab evolved.entity +---@return evolved.entity[] entity_list +function evolved.builder_mt:multi_clone(count, prefab) end +``` + #### `evolved.builder_mt:has` ```lua diff --git a/evolved.lua b/evolved.lua index 8bacfa5..ec92b07 100644 --- a/evolved.lua +++ b/evolved.lua @@ -794,7 +794,10 @@ local __evolved_defer local __evolved_commit local __evolved_spawn +local __evolved_multi_spawn + local __evolved_clone +local __evolved_multi_clone local __evolved_alive local __evolved_alive_all @@ -4020,7 +4023,7 @@ function __evolved_commit() end ---@param components? table ----@return evolved.entity +---@return evolved.entity entity function __evolved_spawn(components) if not components then components = __safe_tbls.__EMPTY_COMPONENT_MAP @@ -4050,9 +4053,16 @@ function __evolved_spawn(components) return entity end +---@param count integer +---@param components? table +---@return evolved.entity[] entity_list +function __evolved_multi_spawn(count, components) + __error_fmt('not implemented yet') +end + ---@param prefab evolved.entity ---@param components? table ----@return evolved.entity +---@return evolved.entity entity function __evolved_clone(prefab, components) if not components then components = __safe_tbls.__EMPTY_COMPONENT_MAP @@ -4087,6 +4097,14 @@ function __evolved_clone(prefab, components) return entity end +---@param count integer +---@param prefab evolved.entity +---@param components? table +---@return evolved.entity[] entity_list +function __evolved_multi_clone(count, prefab, components) + __error_fmt('not implemented yet') +end + ---@param entity evolved.entity ---@return boolean ---@nodiscard @@ -5382,17 +5400,30 @@ function __builder_mt:__tostring() return __lua_string_format('<%s>', __lua_table_concat(fragment_names, ', ')) end ----@return evolved.entity +---@return evolved.entity entity function __builder_mt:spawn() return __evolved_spawn(self.__components) end +---@param count integer +---@return evolved.entity[] entity_list +function __builder_mt:multi_spawn(count) + __error_fmt('not implemented yet') +end + ---@param prefab evolved.entity ----@return evolved.entity +---@return evolved.entity entity function __builder_mt:clone(prefab) return __evolved_clone(prefab, self.__components) end +---@param count integer +---@param prefab evolved.entity +---@return evolved.entity[] entity_list +function __builder_mt:multi_clone(count, prefab) + __error_fmt('not implemented yet') +end + ---@param fragment evolved.fragment ---@return boolean ---@nodiscard @@ -6130,7 +6161,10 @@ evolved.defer = __evolved_defer evolved.commit = __evolved_commit evolved.spawn = __evolved_spawn +evolved.multi_spawn = __evolved_multi_spawn + evolved.clone = __evolved_clone +evolved.multi_clone = __evolved_multi_clone evolved.alive = __evolved_alive evolved.alive_all = __evolved_alive_all From de7d1a66747b43ac82a434fec98c8b4215ac6a4f Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 11 Sep 2025 06:57:13 +0700 Subject: [PATCH 2/4] dirty proof of concept multi_spawn impl (without any optimization) --- README.md | 16 +-- develop/all.lua | 1 + develop/testing/multi_spawn_tests.lua | 194 ++++++++++++++++++++++++++ evolved.lua | 111 +++++++++++++-- 4 files changed, 302 insertions(+), 20 deletions(-) create mode 100644 develop/testing/multi_spawn_tests.lua diff --git a/README.md b/README.md index e506525..adad32e 100644 --- a/README.md +++ b/README.md @@ -1351,10 +1351,10 @@ function evolved.spawn(components) end ### `evolved.multi_spawn` ```lua ----@param count integer +---@param entity_count integer ---@param components? table ---@return evolved.entity[] entity_list -function evolved.multi_spawn(count, components) end +function evolved.multi_spawn(entity_count, components) end ``` ### `evolved.clone` @@ -1369,11 +1369,11 @@ function evolved.clone(prefab, components) end ### `evolved.multi_clone` ```lua ----@param count integer +---@param entity_count integer ---@param prefab evolved.entity ---@param components? table ---@return evolved.entity[] entity_list -function evolved.multi_clone(count, prefab, components) end +function evolved.multi_clone(entity_count, prefab, components) end ``` ### `evolved.alive` @@ -1678,9 +1678,9 @@ function evolved.builder_mt:spawn() end #### `evolved.builder_mt:multi_spawn` ```lua ----@param count integer +---@param entity_count integer ---@return evolved.entity[] entity_list -function evolved.builder_mt:multi_spawn(count) end +function evolved.builder_mt:multi_spawn(entity_count) end ``` #### `evolved.builder_mt:clone` @@ -1694,10 +1694,10 @@ function evolved.builder_mt:clone(prefab) end #### `evolved.builder_mt:multi_clone` ```lua ----@param count integer +---@param entity_count integer ---@param prefab evolved.entity ---@return evolved.entity[] entity_list -function evolved.builder_mt:multi_clone(count, prefab) end +function evolved.builder_mt:multi_clone(entity_count, prefab) end ``` #### `evolved.builder_mt:has` diff --git a/develop/all.lua b/develop/all.lua index bd34bf9..9176dad 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -1,5 +1,6 @@ require 'develop.samples.systems' +require 'develop.testing.multi_spawn_tests' require 'develop.testing.name_tests' require 'develop.testing.requires_fragment_tests' require 'develop.testing.system_as_query_tests' diff --git a/develop/testing/multi_spawn_tests.lua b/develop/testing/multi_spawn_tests.lua new file mode 100644 index 0000000..267090e --- /dev/null +++ b/develop/testing/multi_spawn_tests.lua @@ -0,0 +1,194 @@ +local evo = require 'evolved' + +do + local entity_list + + do + entity_list = evo.multi_spawn(0) + assert(entity_list and #entity_list == 0) + + entity_list = evo.multi_spawn(0, {}) + assert(entity_list and #entity_list == 0) + end + + do + entity_list = evo.multi_spawn(-1) + assert(entity_list and #entity_list == 0) + + entity_list = evo.multi_spawn(-1, {}) + assert(entity_list and #entity_list == 0) + end + + do + entity_list = evo.builder():multi_spawn(0) + assert(entity_list and #entity_list == 0) + end + + do + entity_list = evo.builder():multi_spawn(-1) + assert(entity_list and #entity_list == 0) + end +end + +do + local entity_list + + do + entity_list = evo.multi_spawn(1) + assert(entity_list and #entity_list == 1) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(not entity_list[2]) + + entity_list = evo.multi_spawn(1, {}) + assert(entity_list and #entity_list == 1) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(not entity_list[2]) + end + + do + entity_list = evo.multi_spawn(2) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(entity_list[2] and evo.empty(entity_list[2])) + assert(not entity_list[3]) + + entity_list = evo.multi_spawn(2, {}) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(entity_list[2] and evo.empty(entity_list[2])) + assert(not entity_list[3]) + end + + do + entity_list = evo.builder():multi_spawn(1) + assert(entity_list and #entity_list == 1) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(not entity_list[2]) + end + + do + entity_list = evo.builder():multi_spawn(2) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(entity_list[2] and evo.empty(entity_list[2])) + assert(not entity_list[3]) + end +end + +do + local entity_list + + local prefab = evo.id() + + do + entity_list = evo.multi_clone(0, prefab) + assert(entity_list and #entity_list == 0) + + entity_list = evo.multi_clone(0, prefab, {}) + assert(entity_list and #entity_list == 0) + end + + do + entity_list = evo.multi_clone(-1, prefab) + assert(entity_list and #entity_list == 0) + + entity_list = evo.multi_clone(-1, prefab, {}) + assert(entity_list and #entity_list == 0) + end + + do + entity_list = evo.builder():multi_clone(0, prefab) + assert(entity_list and #entity_list == 0) + end + + do + entity_list = evo.builder():multi_clone(-1, prefab) + assert(entity_list and #entity_list == 0) + end +end + +do + local entity_list + + local prefab = evo.id() + + do + entity_list = evo.multi_clone(1, prefab) + assert(entity_list and #entity_list == 1) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(not entity_list[2]) + + entity_list = evo.multi_clone(1, prefab, {}) + assert(entity_list and #entity_list == 1) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(not entity_list[2]) + end + + do + entity_list = evo.multi_clone(2, prefab) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(entity_list[2] and evo.empty(entity_list[2])) + assert(not entity_list[3]) + + entity_list = evo.multi_clone(2, prefab, {}) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(entity_list[2] and evo.empty(entity_list[2])) + assert(not entity_list[3]) + end + + do + entity_list = evo.builder():multi_clone(1, prefab) + assert(entity_list and #entity_list == 1) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(not entity_list[2]) + end + + do + entity_list = evo.builder():multi_clone(2, prefab) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.empty(entity_list[1])) + assert(entity_list[2] and evo.empty(entity_list[2])) + assert(not entity_list[3]) + end +end + +do + local f1, f2 = evo.id(2) + + do + local entity_list + + entity_list = evo.multi_spawn(2, { [f1] = true, [f2] = 123 }) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.get(entity_list[1], f1) == true and evo.get(entity_list[1], f2) == 123) + assert(entity_list[2] and evo.get(entity_list[2], f1) == true and evo.get(entity_list[2], f2) == 123) + + entity_list = evo.multi_spawn(2, { [f1] = false, [f2] = 456 }) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.get(entity_list[1], f1) == false and evo.get(entity_list[1], f2) == 456) + assert(entity_list[2] and evo.get(entity_list[2], f1) == false and evo.get(entity_list[2], f2) == 456) + end + + do + local prefab = evo.builder():set(f1, true):set(f2, 123):spawn() + + local entity_list + + entity_list = evo.multi_clone(2, prefab) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.get(entity_list[1], f1) == true and evo.get(entity_list[1], f2) == 123) + assert(entity_list[2] and evo.get(entity_list[2], f1) == true and evo.get(entity_list[2], f2) == 123) + + entity_list = evo.multi_clone(2, prefab, {}) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.get(entity_list[1], f1) == true and evo.get(entity_list[1], f2) == 123) + assert(entity_list[2] and evo.get(entity_list[2], f1) == true and evo.get(entity_list[2], f2) == 123) + + entity_list = evo.multi_clone(2, prefab, { [f1] = false, [f2] = 456 }) + assert(entity_list and #entity_list == 2) + assert(entity_list[1] and evo.get(entity_list[1], f1) == false and evo.get(entity_list[1], f2) == 456) + assert(entity_list[2] and evo.get(entity_list[2], f1) == false and evo.get(entity_list[2], f2) == 456) + end +end diff --git a/evolved.lua b/evolved.lua index ec92b07..ce34349 100644 --- a/evolved.lua +++ b/evolved.lua @@ -4053,11 +4053,52 @@ function __evolved_spawn(components) return entity end ----@param count integer +---@param entity_count integer ---@param components? table ---@return evolved.entity[] entity_list -function __evolved_multi_spawn(count, components) - __error_fmt('not implemented yet') +function __evolved_multi_spawn(entity_count, components) + entity_count = entity_count or 1 + + if entity_count <= 0 then + return {} + end + + if not components then + components = __safe_tbls.__EMPTY_COMPONENT_MAP + end + + if __debug_mode then + for fragment in __lua_next, components do + if not __evolved_alive(fragment) then + __error_fmt('the fragment (%s) is not alive and cannot be used', + __id_name(fragment)) + end + end + end + + local entity_list = __lua_table_new(entity_count, 0) + + for entity_index = 1, entity_count do + entity_list[entity_index] = __acquire_id() + end + + if __defer_depth > 0 then + for entity_index = 1, entity_count do + local entity = entity_list[entity_index] + __defer_spawn_entity(entity, components) + end + else + __evolved_defer() + do + for entity_index = 1, entity_count do + local entity = entity_list[entity_index] + __spawn_entity(entity, components) + end + end + __evolved_commit() + end + + return entity_list end ---@param prefab evolved.entity @@ -4097,12 +4138,58 @@ function __evolved_clone(prefab, components) return entity end ----@param count integer +---@param entity_count integer ---@param prefab evolved.entity ---@param components? table ---@return evolved.entity[] entity_list -function __evolved_multi_clone(count, prefab, components) - __error_fmt('not implemented yet') +function __evolved_multi_clone(entity_count, prefab, components) + entity_count = entity_count or 1 + + if entity_count <= 0 then + return {} + end + + if not components then + components = __safe_tbls.__EMPTY_COMPONENT_MAP + end + + if __debug_mode then + if not __evolved_alive(prefab) then + __error_fmt('the prefab (%s) is not alive and cannot be used', + __id_name(prefab)) + end + + for fragment in __lua_next, components do + if not __evolved_alive(fragment) then + __error_fmt('the fragment (%s) is not alive and cannot be used', + __id_name(fragment)) + end + end + end + + local entity_list = __lua_table_new(entity_count, 0) + + for entity_index = 1, entity_count do + entity_list[entity_index] = __acquire_id() + end + + if __defer_depth > 0 then + for entity_index = 1, entity_count do + local entity = entity_list[entity_index] + __defer_clone_entity(entity, prefab, components) + end + else + __evolved_defer() + do + for entity_index = 1, entity_count do + local entity = entity_list[entity_index] + __clone_entity(entity, prefab, components) + end + end + __evolved_commit() + end + + return entity_list end ---@param entity evolved.entity @@ -5405,10 +5492,10 @@ function __builder_mt:spawn() return __evolved_spawn(self.__components) end ----@param count integer +---@param entity_count integer ---@return evolved.entity[] entity_list -function __builder_mt:multi_spawn(count) - __error_fmt('not implemented yet') +function __builder_mt:multi_spawn(entity_count) + return __evolved_multi_spawn(entity_count, self.__components) end ---@param prefab evolved.entity @@ -5417,11 +5504,11 @@ function __builder_mt:clone(prefab) return __evolved_clone(prefab, self.__components) end ----@param count integer +---@param entity_count integer ---@param prefab evolved.entity ---@return evolved.entity[] entity_list -function __builder_mt:multi_clone(count, prefab) - __error_fmt('not implemented yet') +function __builder_mt:multi_clone(entity_count, prefab) + return __evolved_multi_clone(entity_count, prefab, self.__components) end ---@param fragment evolved.fragment From 5184b39f4e6808a712cb985ba761d2c6bec7f736 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 12 Sep 2025 01:52:33 +0700 Subject: [PATCH 3/4] proof of concept multi_clone/spawn optimizations --- develop/all.lua | 3 + develop/benchmarks/multi_clone_bmarks.lua | 57 ++ develop/benchmarks/multi_spawn_bmarks.lua | 57 ++ develop/testing/multi_spawn_tests.lua | 114 ++++ evolved.lua | 654 ++++++++++++++++++++-- 5 files changed, 847 insertions(+), 38 deletions(-) create mode 100644 develop/benchmarks/multi_clone_bmarks.lua create mode 100644 develop/benchmarks/multi_spawn_bmarks.lua diff --git a/develop/all.lua b/develop/all.lua index 9176dad..8b7966e 100644 --- a/develop/all.lua +++ b/develop/all.lua @@ -5,6 +5,9 @@ require 'develop.testing.name_tests' require 'develop.testing.requires_fragment_tests' require 'develop.testing.system_as_query_tests' +require 'develop.benchmarks.multi_clone_bmarks' +require 'develop.benchmarks.multi_spawn_bmarks' + require 'develop.untests' require 'develop.unbench' diff --git a/develop/benchmarks/multi_clone_bmarks.lua b/develop/benchmarks/multi_clone_bmarks.lua new file mode 100644 index 0000000..7db8251 --- /dev/null +++ b/develop/benchmarks/multi_clone_bmarks.lua @@ -0,0 +1,57 @@ +local evo = require 'evolved' +local basics = require 'develop.basics' + +local N = 1000 + +local F1, F2, F3, F4, F5 = evo.id(5) +local Q1 = evo.builder():include(F1):spawn() + +print '----------------------------------------' + +basics.describe_bench(string.format('Multi Clone Benchmarks: Simple Clone | %d entities with 1 component', N), + function() + local clone = evo.clone + + local prefab = evo.spawn { [F1] = true } + + for _ = 1, N do + clone(prefab) + end + + evo.batch_destroy(Q1) + end) + +basics.describe_bench(string.format('Multi Clone Benchmarks: Simple Clone | %d entities with 5 components', N), + function() + local clone = evo.clone + + local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } + + for _ = 1, N do + clone(prefab) + end + + evo.batch_destroy(Q1) + end) + +basics.describe_bench(string.format('Multi Clone Benchmarks: Multi Clone | %d entities with 1 component', N), + function() + local multi_clone = evo.multi_clone + + local prefab = evo.spawn { [F1] = true } + + multi_clone(N, prefab) + + evo.batch_destroy(Q1) + end) + +basics.describe_bench(string.format('Multi Clone Benchmarks: Multi Clone | %d entities with 5 components', N), + function() + local multi_clone = evo.multi_clone + + local prefab = evo.spawn { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } + + multi_clone(N, prefab) + + evo.batch_destroy(Q1) + end) diff --git a/develop/benchmarks/multi_spawn_bmarks.lua b/develop/benchmarks/multi_spawn_bmarks.lua new file mode 100644 index 0000000..a85b706 --- /dev/null +++ b/develop/benchmarks/multi_spawn_bmarks.lua @@ -0,0 +1,57 @@ +local evo = require 'evolved' +local basics = require 'develop.basics' + +local N = 1000 + +local F1, F2, F3, F4, F5 = evo.id(5) +local Q1 = evo.builder():include(F1):spawn() + +print '----------------------------------------' + +basics.describe_bench(string.format('Multi Spawn Benchmarks: Simple Spawn | %d entities with 1 component', N), + function() + local spawn = evo.spawn + + local components = { [F1] = true } + + for _ = 1, N do + spawn(components) + end + + evo.batch_destroy(Q1) + end) + +basics.describe_bench(string.format('Multi Spawn Benchmarks: Simple Spawn | %d entities with 5 components', N), + function() + local spawn = evo.spawn + + local components = { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } + + for _ = 1, N do + spawn(components) + end + + evo.batch_destroy(Q1) + end) + +basics.describe_bench(string.format('Multi Spawn Benchmarks: Multi Spawn | %d entities with 1 component', N), + function() + local multi_spawn = evo.multi_spawn + + local components = { [F1] = true } + + multi_spawn(N, components) + + evo.batch_destroy(Q1) + end) + +basics.describe_bench(string.format('Multi Spawn Benchmarks: Multi Spawn | %d entities with 5 components', N), + function() + local multi_spawn = evo.multi_spawn + + local components = { [F1] = true, [F2] = true, [F3] = true, [F4] = true, [F5] = true } + + multi_spawn(N, components) + + evo.batch_destroy(Q1) + end) diff --git a/develop/testing/multi_spawn_tests.lua b/develop/testing/multi_spawn_tests.lua index 267090e..d0eaf96 100644 --- a/develop/testing/multi_spawn_tests.lua +++ b/develop/testing/multi_spawn_tests.lua @@ -192,3 +192,117 @@ do assert(entity_list[2] and evo.get(entity_list[2], f1) == false and evo.get(entity_list[2], f2) == 456) end end + +do + local f1, f2, f3 = evo.id(3) + + do + local entity_list1, entity_list2 + + evo.defer() + do + entity_list1 = evo.multi_spawn(2, { [f1] = 42, [f2] = "hello", [f3] = false }) + assert(entity_list1 and #entity_list1 == 2) + assert(entity_list1[1] and evo.empty(entity_list1[1])) + assert(entity_list1[2] and evo.empty(entity_list1[2])) + assert(not entity_list1[3]) + + entity_list2 = evo.multi_spawn(3, { [f2] = "world", [f3] = true }) + assert(entity_list2 and #entity_list2 == 3) + assert(entity_list2[1] and evo.empty(entity_list2[1])) + assert(entity_list2[2] and evo.empty(entity_list2[2])) + assert(entity_list2[3] and evo.empty(entity_list2[3])) + end + evo.commit() + do + assert(entity_list1 and #entity_list1 == 2) + assert(entity_list1[1] and not evo.empty(entity_list1[1])) + assert(entity_list1[2] and not evo.empty(entity_list1[2])) + assert(not entity_list1[3]) + assert( + evo.get(entity_list1[1], f1) == 42 and + evo.get(entity_list1[1], f2) == "hello" and + evo.get(entity_list1[1], f3) == false) + assert( + evo.get(entity_list1[2], f1) == 42 and + evo.get(entity_list1[2], f2) == "hello" and + evo.get(entity_list1[2], f3) == false) + + assert(entity_list2 and #entity_list2 == 3) + assert(entity_list2[1] and not evo.empty(entity_list2[1])) + assert(entity_list2[2] and not evo.empty(entity_list2[2])) + assert(entity_list2[3] and not evo.empty(entity_list2[3])) + assert(not entity_list2[4]) + assert( + evo.get(entity_list2[1], f1) == nil and + evo.get(entity_list2[1], f2) == "world" and + evo.get(entity_list2[1], f3) == true) + assert( + evo.get(entity_list2[2], f1) == nil and + evo.get(entity_list2[2], f2) == "world" and + evo.get(entity_list2[2], f3) == true) + assert( + evo.get(entity_list2[3], f1) == nil and + evo.get(entity_list2[3], f2) == "world" and + evo.get(entity_list2[3], f3) == true) + end + end +end + +do + local f1, f2, f3 = evo.id(3) + + do + local prefab = evo.builder():set(f1, false):set(f2, 123):spawn() + + local entity_list1, entity_list2 + + evo.defer() + do + entity_list1 = evo.multi_clone(2, prefab) + assert(entity_list1 and #entity_list1 == 2) + assert(entity_list1[1] and evo.empty(entity_list1[1])) + assert(entity_list1[2] and evo.empty(entity_list1[2])) + assert(not entity_list1[3]) + + entity_list2 = evo.multi_clone(3, prefab, { [f2] = 456, [f3] = "world" }) + assert(entity_list2 and #entity_list2 == 3) + assert(entity_list2[1] and evo.empty(entity_list2[1])) + assert(entity_list2[2] and evo.empty(entity_list2[2])) + assert(entity_list2[3] and evo.empty(entity_list2[3])) + end + evo.commit() + do + assert(entity_list1 and #entity_list1 == 2) + assert(entity_list1[1] and not evo.empty(entity_list1[1])) + assert(entity_list1[2] and not evo.empty(entity_list1[2])) + assert(not entity_list1[3]) + assert( + evo.get(entity_list1[1], f1) == false and + evo.get(entity_list1[1], f2) == 123 and + evo.get(entity_list1[1], f3) == nil) + assert( + evo.get(entity_list1[2], f1) == false and + evo.get(entity_list1[2], f2) == 123 and + evo.get(entity_list1[2], f3) == nil) + + assert(entity_list2 and #entity_list2 == 3) + assert(entity_list2[1] and not evo.empty(entity_list2[1])) + assert(entity_list2[2] and not evo.empty(entity_list2[2])) + assert(entity_list2[3] and not evo.empty(entity_list2[3])) + assert(not entity_list2[4]) + assert( + evo.get(entity_list2[1], f1) == false and + evo.get(entity_list2[1], f2) == 456 and + evo.get(entity_list2[1], f3) == "world") + assert( + evo.get(entity_list2[2], f1) == false and + evo.get(entity_list2[2], f2) == 456 and + evo.get(entity_list2[2], f3) == "world") + assert( + evo.get(entity_list2[3], f1) == false and + evo.get(entity_list2[3], f2) == 456 and + evo.get(entity_list2[3], f3) == "world") + end + end +end diff --git a/evolved.lua b/evolved.lua index ce34349..b68cefc 100644 --- a/evolved.lua +++ b/evolved.lua @@ -877,7 +877,10 @@ local __detach_entity local __detach_all_entities local __spawn_entity +local __multi_spawn_entity + local __clone_entity +local __multi_clone_entity local __purge_chunk local __clear_chunk_list @@ -899,7 +902,10 @@ local __defer_batch_clear local __defer_batch_destroy local __defer_spawn_entity +local __defer_multi_spawn_entity + local __defer_clone_entity +local __defer_multi_clone_entity local __defer_call_hook @@ -1986,6 +1992,216 @@ function __spawn_entity(entity, components) end end +---@param entity_list evolved.entity[] +---@param entity_count integer +---@param components table +function __multi_spawn_entity(entity_list, entity_count, components) + if __defer_depth <= 0 then + __error_fmt('spawn entity operations should be deferred') + end + + local chunk = __chunk_components(components) + + if not chunk then + return + end + + local req_fragment_set + local req_fragment_list + local req_fragment_count = 0 + + local ini_chunk = chunk + local ini_fragment_set = ini_chunk.__fragment_set + + if chunk.__has_required_fragments then + ---@type table + req_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + + ---@type evolved.fragment[] + req_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + + req_fragment_count = __chunk_required_fragments(ini_chunk, + req_fragment_set, req_fragment_list, req_fragment_count) + + for req_fragment_index = 1, req_fragment_count do + local req_fragment = req_fragment_list[req_fragment_index] + chunk = __chunk_with_fragment(chunk, req_fragment) + end + end + + local chunk_entity_list = chunk.__entity_list + local chunk_entity_count = chunk.__entity_count + + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages + + local b_place = chunk_entity_count + 1 + local e_place = chunk_entity_count + entity_count + + chunk.__entity_count = e_place + + for place = b_place, e_place do + local entity = entity_list[place - b_place + 1] + local entity_primary = entity % 2 ^ 20 + + chunk_entity_list[place] = entity + + __entity_chunks[entity_primary] = chunk + __entity_places[entity_primary] = place + + __structural_changes = __structural_changes + 1 + end + + if chunk.__has_setup_hooks then + for fragment, component in __lua_next, components do + local component_index = chunk_component_indices[fragment] + + if component_index then + ---@type evolved.duplicate? + local fragment_duplicate = + __evolved_get(fragment, __DUPLICATE) + + for place = b_place, e_place do + local new_component = component + + if new_component ~= nil and fragment_duplicate then + new_component = fragment_duplicate(new_component) + end + + if new_component == nil then + new_component = true + end + + local component_storage = chunk_component_storages[component_index] + + component_storage[place] = new_component + end + end + end + + for req_fragment_index = 1, req_fragment_count do + local req_fragment = req_fragment_list[req_fragment_index] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + ---@type evolved.default?, evolved.duplicate? + local req_fragment_default, req_fragment_duplicate = + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) + + for place = b_place, e_place do + local req_component = req_fragment_default + + if req_component ~= nil and req_fragment_duplicate then + req_component = req_fragment_duplicate(req_component) + end + + if req_component == nil then + req_component = true + end + + local req_component_storage = chunk_component_storages[req_component_index] + + req_component_storage[place] = req_component + end + end + end + end + else + for fragment, component in __lua_next, components do + local component_index = chunk_component_indices[fragment] + + if component_index then + local new_component = component + + local component_storage = chunk_component_storages[component_index] + + for place = b_place, e_place do + component_storage[place] = new_component + end + end + end + + for req_fragment_index = 1, req_fragment_count do + local req_fragment = req_fragment_list[req_fragment_index] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + local req_component = true + + local req_component_storage = chunk_component_storages[req_component_index] + + for place = b_place, e_place do + req_component_storage[place] = req_component + end + end + end + end + end + + if chunk.__has_insert_hooks then + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + + for chunk_fragment_index = 1, chunk_fragment_count do + local fragment = chunk_fragment_list[chunk_fragment_index] + + ---@type evolved.set_hook?, evolved.insert_hook? + local fragment_on_set, fragment_on_insert = + __evolved_get(fragment, __ON_SET, __ON_INSERT) + + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + for place = b_place, e_place do + local entity = chunk_entity_list[place] + + local new_component = component_storage[place] + + if fragment_on_set then + __defer_call_hook(fragment_on_set, entity, fragment, new_component) + end + + if fragment_on_insert then + __defer_call_hook(fragment_on_insert, entity, fragment, new_component) + end + end + else + if fragment_on_set then + for place = b_place, e_place do + local entity = chunk_entity_list[place] + __defer_call_hook(fragment_on_set, entity, fragment) + end + end + + if fragment_on_insert then + for place = b_place, e_place do + local entity = chunk_entity_list[place] + __defer_call_hook(fragment_on_insert, entity, fragment) + end + end + end + end + end + + if req_fragment_set then + __release_table(__table_pool_tag.fragment_set, req_fragment_set) + end + + if req_fragment_list then + __release_table(__table_pool_tag.fragment_list, req_fragment_list) + end +end + ---@param entity evolved.entity ---@param prefab evolved.entity ---@param components table @@ -2238,6 +2454,284 @@ function __clone_entity(entity, prefab, components) end end +---@param entity_list evolved.entity[] +---@param entity_count integer +---@param prefab evolved.entity +---@param components table +function __multi_clone_entity(entity_list, entity_count, prefab, components) + if __defer_depth <= 0 then + __error_fmt('clone entity operations should be deferred') + end + + local prefab_primary = prefab % 2 ^ 20 + + local prefab_chunk = __entity_chunks[prefab_primary] + local prefab_place = __entity_places[prefab_primary] + + local chunk = __chunk_with_components( + __chunk_without_unique_fragments(prefab_chunk), + components) + + if not chunk then + return + end + + local req_fragment_set + local req_fragment_list + local req_fragment_count = 0 + + local ini_chunk = chunk + local ini_fragment_set = ini_chunk.__fragment_set + + if chunk.__has_required_fragments then + ---@type table + req_fragment_set = __acquire_table(__table_pool_tag.fragment_set) + + ---@type evolved.fragment[] + req_fragment_list = __acquire_table(__table_pool_tag.fragment_list) + + req_fragment_count = __chunk_required_fragments(ini_chunk, + req_fragment_set, req_fragment_list, req_fragment_count) + + for req_fragment_index = 1, req_fragment_count do + local req_fragment = req_fragment_list[req_fragment_index] + chunk = __chunk_with_fragment(chunk, req_fragment) + end + end + + local chunk_entity_list = chunk.__entity_list + local chunk_entity_count = chunk.__entity_count + + local chunk_component_indices = chunk.__component_indices + local chunk_component_storages = chunk.__component_storages + + local b_place = chunk_entity_count + 1 + local e_place = chunk_entity_count + entity_count + + chunk.__entity_count = e_place + + for place = b_place, e_place do + local entity = entity_list[place - b_place + 1] + local entity_primary = entity % 2 ^ 20 + + chunk_entity_list[place] = entity + + __entity_chunks[entity_primary] = chunk + __entity_places[entity_primary] = place + + __structural_changes = __structural_changes + 1 + end + + if prefab_chunk then + local prefab_component_count = prefab_chunk.__component_count + local prefab_component_storages = prefab_chunk.__component_storages + local prefab_component_fragments = prefab_chunk.__component_fragments + + if prefab_chunk.__has_setup_hooks then + for prefab_component_index = 1, prefab_component_count do + local fragment = prefab_component_fragments[prefab_component_index] + local component_index = chunk_component_indices[fragment] + + if component_index then + ---@type evolved.duplicate? + local fragment_duplicate = + __evolved_get(fragment, __DUPLICATE) + + local prefab_component_storage = prefab_component_storages[prefab_component_index] + local prefab_component = prefab_component_storage[prefab_place] + + for place = b_place, e_place do + local new_component = prefab_component + + if new_component ~= nil and fragment_duplicate then + new_component = fragment_duplicate(new_component) + end + + if new_component == nil then + new_component = true + end + + local component_storage = chunk_component_storages[component_index] + + component_storage[place] = new_component + end + end + end + else + for prefab_component_index = 1, prefab_component_count do + local fragment = prefab_component_fragments[prefab_component_index] + local component_index = chunk_component_indices[fragment] + + if component_index then + local prefab_component_storage = prefab_component_storages[prefab_component_index] + local prefab_component = prefab_component_storage[prefab_place] + + local new_component = prefab_component + + if new_component == nil then + new_component = true + end + + local component_storage = chunk_component_storages[component_index] + + for place = b_place, e_place do + component_storage[place] = new_component + end + end + end + end + end + + if chunk.__has_setup_hooks then + for fragment, component in __lua_next, components do + local component_index = chunk_component_indices[fragment] + + if component_index then + ---@type evolved.duplicate? + local fragment_duplicate = + __evolved_get(fragment, __DUPLICATE) + + for place = b_place, e_place do + local new_component = component + + if new_component ~= nil and fragment_duplicate then + new_component = fragment_duplicate(new_component) + end + + if new_component == nil then + new_component = true + end + + local component_storage = chunk_component_storages[component_index] + + component_storage[place] = new_component + end + end + end + + for req_fragment_index = 1, req_fragment_count do + local req_fragment = req_fragment_list[req_fragment_index] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + ---@type evolved.default?, evolved.duplicate? + local req_fragment_default, req_fragment_duplicate = + __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) + + for place = b_place, e_place do + local req_component = req_fragment_default + + if req_component ~= nil and req_fragment_duplicate then + req_component = req_fragment_duplicate(req_component) + end + + if req_component == nil then + req_component = true + end + + local req_component_storage = chunk_component_storages[req_component_index] + + req_component_storage[place] = req_component + end + end + end + end + else + for fragment, component in __lua_next, components do + local component_index = chunk_component_indices[fragment] + + if component_index then + local new_component = component + + local component_storage = chunk_component_storages[component_index] + + for place = b_place, e_place do + component_storage[place] = new_component + end + end + end + + for req_fragment_index = 1, req_fragment_count do + local req_fragment = req_fragment_list[req_fragment_index] + + if ini_fragment_set[req_fragment] then + -- this fragment has already been initialized + else + local req_component_index = chunk_component_indices[req_fragment] + + if req_component_index then + local req_component = true + + local req_component_storage = chunk_component_storages[req_component_index] + + for place = b_place, e_place do + req_component_storage[place] = req_component + end + end + end + end + end + + if chunk.__has_insert_hooks then + local chunk_fragment_list = chunk.__fragment_list + local chunk_fragment_count = chunk.__fragment_count + + for chunk_fragment_index = 1, chunk_fragment_count do + local fragment = chunk_fragment_list[chunk_fragment_index] + + ---@type evolved.set_hook?, evolved.insert_hook? + local fragment_on_set, fragment_on_insert = + __evolved_get(fragment, __ON_SET, __ON_INSERT) + + local component_index = chunk_component_indices[fragment] + + if component_index then + local component_storage = chunk_component_storages[component_index] + + for place = b_place, e_place do + local entity = chunk_entity_list[place] + + local new_component = component_storage[place] + + if fragment_on_set then + __defer_call_hook(fragment_on_set, entity, fragment, new_component) + end + + if fragment_on_insert then + __defer_call_hook(fragment_on_insert, entity, fragment, new_component) + end + end + else + if fragment_on_set then + for place = b_place, e_place do + local entity = chunk_entity_list[place] + __defer_call_hook(fragment_on_set, entity, fragment) + end + end + + if fragment_on_insert then + for place = b_place, e_place do + local entity = chunk_entity_list[place] + __defer_call_hook(fragment_on_insert, entity, fragment) + end + end + end + end + end + + if req_fragment_set then + __release_table(__table_pool_tag.fragment_set, req_fragment_set) + end + + if req_fragment_list then + __release_table(__table_pool_tag.fragment_list, req_fragment_list) + end +end + ---@param chunk evolved.chunk function __purge_chunk(chunk) if __defer_depth <= 0 then @@ -3104,11 +3598,14 @@ local __defer_op = { batch_destroy = 8, spawn_entity = 9, - clone_entity = 10, + multi_spawn_entity = 10, - call_hook = 11, + clone_entity = 11, + multi_clone_entity = 12, - __count = 11, + call_hook = 13, + + __count = 13, } ---@type table @@ -3596,13 +4093,13 @@ __defer_ops[__defer_op.batch_destroy] = function(bytes, index) end ---@param entity evolved.entity ----@param components table -function __defer_spawn_entity(entity, components) +---@param component_map table +function __defer_spawn_entity(entity, component_map) ---@type table - local component_map = __acquire_table(__table_pool_tag.component_map) + local component_map_dup = __acquire_table(__table_pool_tag.component_map) - for fragment, component in __lua_next, components do - component_map[fragment] = component + for fragment, component in __lua_next, component_map do + component_map_dup[fragment] = component end local length = __defer_length @@ -3610,62 +4107,155 @@ function __defer_spawn_entity(entity, components) bytecode[length + 1] = __defer_op.spawn_entity bytecode[length + 2] = entity - bytecode[length + 3] = component_map + bytecode[length + 3] = component_map_dup __defer_length = length + 3 end __defer_ops[__defer_op.spawn_entity] = function(bytes, index) local entity = bytes[index + 0] - local component_map = bytes[index + 1] + local component_map_dup = bytes[index + 1] __evolved_defer() do - __spawn_entity(entity, component_map) - __release_table(__table_pool_tag.component_map, component_map) + __spawn_entity(entity, component_map_dup) + __release_table(__table_pool_tag.component_map, component_map_dup) end __evolved_commit() return 2 end +---@param entity_list evolved.entity[] +---@param entity_count integer +---@param component_map table +function __defer_multi_spawn_entity(entity_list, entity_count, component_map) + ---@type evolved.entity[] + local entity_list_dup = __acquire_table(__table_pool_tag.entity_list) + + __lua_table_move( + entity_list, 1, entity_count, + 1, entity_list_dup) + + ---@type table + local component_map_dup = __acquire_table(__table_pool_tag.component_map) + + for fragment, component in __lua_next, component_map do + component_map_dup[fragment] = component + end + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.multi_spawn_entity + bytecode[length + 2] = entity_count + bytecode[length + 3] = entity_list_dup + bytecode[length + 4] = component_map_dup + + __defer_length = length + 4 +end + +__defer_ops[__defer_op.multi_spawn_entity] = function(bytes, index) + local entity_count = bytes[index + 0] + local entity_list_dup = bytes[index + 1] + local component_map_dup = bytes[index + 2] + + __evolved_defer() + do + __multi_spawn_entity(entity_list_dup, entity_count, component_map_dup) + __release_table(__table_pool_tag.entity_list, entity_list_dup) + __release_table(__table_pool_tag.component_map, component_map_dup) + end + __evolved_commit() + + return 3 +end + ---@param entity evolved.entity ---@param prefab evolved.entity ---@param components table function __defer_clone_entity(entity, prefab, components) ---@type table - local component_map = __acquire_table(__table_pool_tag.component_map) + local component_map_dup = __acquire_table(__table_pool_tag.component_map) for fragment, component in __lua_next, components do - component_map[fragment] = component + component_map_dup[fragment] = component end local length = __defer_length local bytecode = __defer_bytecode bytecode[length + 1] = __defer_op.clone_entity - bytecode[length + 2] = entity - bytecode[length + 3] = prefab - bytecode[length + 4] = component_map + bytecode[length + 2] = prefab + bytecode[length + 3] = entity + bytecode[length + 4] = component_map_dup __defer_length = length + 4 end __defer_ops[__defer_op.clone_entity] = function(bytes, index) - local entity = bytes[index + 0] - local prefab = bytes[index + 1] - local component_map = bytes[index + 2] + local prefab = bytes[index + 0] + local entity = bytes[index + 1] + local component_map_dup = bytes[index + 2] __evolved_defer() do - __clone_entity(entity, prefab, component_map) - __release_table(__table_pool_tag.component_map, component_map) + __clone_entity(entity, prefab, component_map_dup) + __release_table(__table_pool_tag.component_map, component_map_dup) end __evolved_commit() return 3 end +---@param entity_list evolved.entity[] +---@param entity_count integer +---@param prefab evolved.entity +---@param components table +function __defer_multi_clone_entity(entity_list, entity_count, prefab, components) + ---@type evolved.entity[] + local entity_list_dup = __acquire_table(__table_pool_tag.entity_list) + + __lua_table_move( + entity_list, 1, entity_count, + 1, entity_list_dup) + + ---@type table + local component_map_dup = __acquire_table(__table_pool_tag.component_map) + + for fragment, component in __lua_next, components do + component_map_dup[fragment] = component + end + + local length = __defer_length + local bytecode = __defer_bytecode + + bytecode[length + 1] = __defer_op.multi_clone_entity + bytecode[length + 2] = prefab + bytecode[length + 3] = entity_count + bytecode[length + 4] = entity_list_dup + bytecode[length + 5] = component_map_dup + + __defer_length = length + 5 +end + +__defer_ops[__defer_op.multi_clone_entity] = function(bytes, index) + local prefab = bytes[index + 0] + local entity_count = bytes[index + 1] + local entity_list_dup = bytes[index + 2] + local component_map_dup = bytes[index + 3] + + __evolved_defer() + do + __multi_clone_entity(entity_list_dup, entity_count, prefab, component_map_dup) + __release_table(__table_pool_tag.entity_list, entity_list_dup) + __release_table(__table_pool_tag.component_map, component_map_dup) + end + __evolved_commit() + + return 4 +end + ---@param hook fun(...) ---@param ... any hook arguments function __defer_call_hook(hook, ...) @@ -4083,17 +4673,11 @@ function __evolved_multi_spawn(entity_count, components) end if __defer_depth > 0 then - for entity_index = 1, entity_count do - local entity = entity_list[entity_index] - __defer_spawn_entity(entity, components) - end + __defer_multi_spawn_entity(entity_list, entity_count, components) else __evolved_defer() do - for entity_index = 1, entity_count do - local entity = entity_list[entity_index] - __spawn_entity(entity, components) - end + __multi_spawn_entity(entity_list, entity_count, components) end __evolved_commit() end @@ -4174,17 +4758,11 @@ function __evolved_multi_clone(entity_count, prefab, components) end if __defer_depth > 0 then - for entity_index = 1, entity_count do - local entity = entity_list[entity_index] - __defer_clone_entity(entity, prefab, components) - end + __defer_multi_clone_entity(entity_list, entity_count, prefab, components) else __evolved_defer() do - for entity_index = 1, entity_count do - local entity = entity_list[entity_index] - __clone_entity(entity, prefab, components) - end + __multi_clone_entity(entity_list, entity_count, prefab, components) end __evolved_commit() end From 327a6bcbce833bf0f2194076b0a439d49b317593 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 12 Sep 2025 04:54:56 +0700 Subject: [PATCH 4/4] final tweaks of the multi_spawn/clone functions --- README.md | 5 +- develop/benchmarks/multi_clone_bmarks.lua | 2 + develop/benchmarks/multi_spawn_bmarks.lua | 2 + evolved.lua | 353 +++++++++++----------- 4 files changed, 190 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index adad32e..f56910c 100644 --- a/README.md +++ b/README.md @@ -1105,8 +1105,8 @@ commit :: boolean spawn :: ? -> entity multi_spawn :: integer, ? -> entity[] -clone :: entity -> ? -> entity -multi_clone :: integer, entity -> ? -> entity +clone :: entity, ? -> entity +multi_clone :: integer, entity, ? -> entity[] alive :: entity -> boolean alive_all :: entity... -> boolean @@ -1223,6 +1223,7 @@ builder_mt:destruction_policy :: id -> builder ## vX.X.X - Added the new [`evolved.name`](#evolvedname-1) function +- Added the new [`evolved.multi_spawn`](#evolvedmulti_spawn) and [`evolved.multi_clone`](#evolvedmulti_clone) functions - Added the new [`evolved.INTERNAL`](#evolvedinternal) fragment trait ## v1.1.0 diff --git a/develop/benchmarks/multi_clone_bmarks.lua b/develop/benchmarks/multi_clone_bmarks.lua index 7db8251..a9ce38b 100644 --- a/develop/benchmarks/multi_clone_bmarks.lua +++ b/develop/benchmarks/multi_clone_bmarks.lua @@ -1,6 +1,8 @@ local evo = require 'evolved' local basics = require 'develop.basics' +evo.debug_mode(false) + local N = 1000 local F1, F2, F3, F4, F5 = evo.id(5) diff --git a/develop/benchmarks/multi_spawn_bmarks.lua b/develop/benchmarks/multi_spawn_bmarks.lua index a85b706..f6c38e1 100644 --- a/develop/benchmarks/multi_spawn_bmarks.lua +++ b/develop/benchmarks/multi_spawn_bmarks.lua @@ -1,6 +1,8 @@ local evo = require 'evolved' local basics = require 'develop.basics' +evo.debug_mode(false) + local N = 1000 local F1, F2, F3, F4, F5 = evo.id(5) diff --git a/evolved.lua b/evolved.lua index b68cefc..92ea0c3 100644 --- a/evolved.lua +++ b/evolved.lua @@ -519,7 +519,7 @@ function __list_dup(list) return {} end - local dup_list = __lua_table_new(list_size, 0) + local dup_list = __list_new(list_size) __lua_table_move( list, 1, list_size, @@ -1847,17 +1847,16 @@ function __spawn_entity(entity, components) local chunk_component_storages = chunk.__component_storages local place = chunk_entity_count + 1 - chunk.__entity_count = place - - chunk_entity_list[place] = entity do - local entity_primary = entity % 2 ^ 20 + chunk.__entity_count = place + __structural_changes = __structural_changes + 1 + chunk_entity_list[place] = entity + + local entity_primary = entity % 2 ^ 20 __entity_chunks[entity_primary] = chunk __entity_places[entity_primary] = place - - __structural_changes = __structural_changes + 1 end if chunk.__has_setup_hooks then @@ -1865,6 +1864,8 @@ function __spawn_entity(entity, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + ---@type evolved.duplicate? local fragment_duplicate = __evolved_get(fragment, __DUPLICATE) @@ -1879,8 +1880,6 @@ function __spawn_entity(entity, components) new_component = true end - local component_storage = chunk_component_storages[component_index] - component_storage[place] = new_component end end @@ -1894,6 +1893,8 @@ function __spawn_entity(entity, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then + local req_component_storage = chunk_component_storages[req_component_index] + ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) @@ -1908,8 +1909,6 @@ function __spawn_entity(entity, components) req_component = true end - local req_component_storage = chunk_component_storages[req_component_index] - req_component_storage[place] = req_component end end @@ -1919,10 +1918,10 @@ function __spawn_entity(entity, components) local component_index = chunk_component_indices[fragment] if component_index then - local new_component = component - local component_storage = chunk_component_storages[component_index] + local new_component = component + if new_component == nil then new_component = true end component_storage[place] = new_component end end @@ -1936,10 +1935,9 @@ function __spawn_entity(entity, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then - local req_component = true - local req_component_storage = chunk_component_storages[req_component_index] + local req_component = true req_component_storage[place] = req_component end end @@ -2038,18 +2036,21 @@ function __multi_spawn_entity(entity_list, entity_count, components) local b_place = chunk_entity_count + 1 local e_place = chunk_entity_count + entity_count - chunk.__entity_count = e_place - - for place = b_place, e_place do - local entity = entity_list[place - b_place + 1] - local entity_primary = entity % 2 ^ 20 - - chunk_entity_list[place] = entity - - __entity_chunks[entity_primary] = chunk - __entity_places[entity_primary] = place - + do + chunk.__entity_count = e_place __structural_changes = __structural_changes + 1 + + local entity_chunks = __entity_chunks + local entity_places = __entity_places + + for place = b_place, e_place do + local entity = entity_list[place - b_place + 1] + chunk_entity_list[place] = entity + + local entity_primary = entity % 2 ^ 20 + entity_chunks[entity_primary] = chunk + entity_places[entity_primary] = place + end end if chunk.__has_setup_hooks then @@ -2057,24 +2058,25 @@ function __multi_spawn_entity(entity_list, entity_count, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + ---@type evolved.duplicate? local fragment_duplicate = __evolved_get(fragment, __DUPLICATE) - for place = b_place, e_place do + if fragment_duplicate then + for place = b_place, e_place do + local new_component = component + if new_component ~= nil then new_component = fragment_duplicate(new_component) end + if new_component == nil then new_component = true end + component_storage[place] = new_component + end + else local new_component = component - - if new_component ~= nil and fragment_duplicate then - new_component = fragment_duplicate(new_component) + if new_component == nil then new_component = true end + for place = b_place, e_place do + component_storage[place] = new_component end - - if new_component == nil then - new_component = true - end - - local component_storage = chunk_component_storages[component_index] - - component_storage[place] = new_component end end end @@ -2088,24 +2090,25 @@ function __multi_spawn_entity(entity_list, entity_count, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then + local req_component_storage = chunk_component_storages[req_component_index] + ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) - for place = b_place, e_place do + if req_fragment_duplicate then + for place = b_place, e_place do + local req_component = req_fragment_default + if req_component ~= nil then req_component = req_fragment_duplicate(req_component) end + if req_component == nil then req_component = true end + req_component_storage[place] = req_component + end + else local req_component = req_fragment_default - - if req_component ~= nil and req_fragment_duplicate then - req_component = req_fragment_duplicate(req_component) + if req_component == nil then req_component = true end + for place = b_place, e_place do + req_component_storage[place] = req_component end - - if req_component == nil then - req_component = true - end - - local req_component_storage = chunk_component_storages[req_component_index] - - req_component_storage[place] = req_component end end end @@ -2115,10 +2118,10 @@ function __multi_spawn_entity(entity_list, entity_count, components) local component_index = chunk_component_indices[fragment] if component_index then - local new_component = component - local component_storage = chunk_component_storages[component_index] + local new_component = component + if new_component == nil then new_component = true end for place = b_place, e_place do component_storage[place] = new_component end @@ -2134,10 +2137,9 @@ function __multi_spawn_entity(entity_list, entity_count, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then - local req_component = true - local req_component_storage = chunk_component_storages[req_component_index] + local req_component = true for place = b_place, e_place do req_component_storage[place] = req_component end @@ -2162,32 +2164,38 @@ function __multi_spawn_entity(entity_list, entity_count, components) if component_index then local component_storage = chunk_component_storages[component_index] - for place = b_place, e_place do - local entity = chunk_entity_list[place] + if fragment_on_set or fragment_on_insert then + for place = b_place, e_place do + local entity = chunk_entity_list[place] - local new_component = component_storage[place] + local new_component = component_storage[place] - if fragment_on_set then - __defer_call_hook(fragment_on_set, entity, fragment, new_component) - end - - if fragment_on_insert then - __defer_call_hook(fragment_on_insert, entity, fragment, new_component) + if fragment_on_set then + __defer_call_hook(fragment_on_set, entity, fragment, new_component) + end + + if fragment_on_insert then + __defer_call_hook(fragment_on_insert, entity, fragment, new_component) + end end + else + -- nothing end else - if fragment_on_set then + if fragment_on_set or fragment_on_insert then for place = b_place, e_place do local entity = chunk_entity_list[place] - __defer_call_hook(fragment_on_set, entity, fragment) - end - end - if fragment_on_insert then - for place = b_place, e_place do - local entity = chunk_entity_list[place] - __defer_call_hook(fragment_on_insert, entity, fragment) + if fragment_on_set then + __defer_call_hook(fragment_on_set, entity, fragment) + end + + if fragment_on_insert then + __defer_call_hook(fragment_on_insert, entity, fragment) + end end + else + -- nothing end end end @@ -2253,17 +2261,16 @@ function __clone_entity(entity, prefab, components) local chunk_component_storages = chunk.__component_storages local place = chunk_entity_count + 1 - chunk.__entity_count = place - - chunk_entity_list[place] = entity do - local entity_primary = entity % 2 ^ 20 + chunk.__entity_count = place + __structural_changes = __structural_changes + 1 + chunk_entity_list[place] = entity + + local entity_primary = entity % 2 ^ 20 __entity_chunks[entity_primary] = chunk __entity_places[entity_primary] = place - - __structural_changes = __structural_changes + 1 end if prefab_chunk then @@ -2277,6 +2284,8 @@ function __clone_entity(entity, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + ---@type evolved.duplicate? local fragment_duplicate = __evolved_get(fragment, __DUPLICATE) @@ -2294,8 +2303,6 @@ function __clone_entity(entity, prefab, components) new_component = true end - local component_storage = chunk_component_storages[component_index] - component_storage[place] = new_component end end @@ -2305,6 +2312,8 @@ function __clone_entity(entity, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + local prefab_component_storage = prefab_component_storages[prefab_component_index] local prefab_component = prefab_component_storage[prefab_place] @@ -2314,8 +2323,6 @@ function __clone_entity(entity, prefab, components) new_component = true end - local component_storage = chunk_component_storages[component_index] - component_storage[place] = new_component end end @@ -2327,6 +2334,8 @@ function __clone_entity(entity, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + ---@type evolved.duplicate? local fragment_duplicate = __evolved_get(fragment, __DUPLICATE) @@ -2341,8 +2350,6 @@ function __clone_entity(entity, prefab, components) new_component = true end - local component_storage = chunk_component_storages[component_index] - component_storage[place] = new_component end end @@ -2356,6 +2363,8 @@ function __clone_entity(entity, prefab, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then + local req_component_storage = chunk_component_storages[req_component_index] + ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) @@ -2370,8 +2379,6 @@ function __clone_entity(entity, prefab, components) req_component = true end - local req_component_storage = chunk_component_storages[req_component_index] - req_component_storage[place] = req_component end end @@ -2381,10 +2388,10 @@ function __clone_entity(entity, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then - local new_component = component - local component_storage = chunk_component_storages[component_index] + local new_component = component + if new_component == nil then new_component = true end component_storage[place] = new_component end end @@ -2398,10 +2405,9 @@ function __clone_entity(entity, prefab, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then - local req_component = true - local req_component_storage = chunk_component_storages[req_component_index] + local req_component = true req_component_storage[place] = req_component end end @@ -2508,18 +2514,21 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local b_place = chunk_entity_count + 1 local e_place = chunk_entity_count + entity_count - chunk.__entity_count = e_place - - for place = b_place, e_place do - local entity = entity_list[place - b_place + 1] - local entity_primary = entity % 2 ^ 20 - - chunk_entity_list[place] = entity - - __entity_chunks[entity_primary] = chunk - __entity_places[entity_primary] = place - + do + chunk.__entity_count = e_place __structural_changes = __structural_changes + 1 + + local entity_chunks = __entity_chunks + local entity_places = __entity_places + + for place = b_place, e_place do + local entity = entity_list[place - b_place + 1] + chunk_entity_list[place] = entity + + local entity_primary = entity % 2 ^ 20 + entity_chunks[entity_primary] = chunk + entity_places[entity_primary] = place + end end if prefab_chunk then @@ -2533,6 +2542,8 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + ---@type evolved.duplicate? local fragment_duplicate = __evolved_get(fragment, __DUPLICATE) @@ -2540,20 +2551,19 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local prefab_component_storage = prefab_component_storages[prefab_component_index] local prefab_component = prefab_component_storage[prefab_place] - for place = b_place, e_place do + if fragment_duplicate then + for place = b_place, e_place do + local new_component = prefab_component + if new_component ~= nil then new_component = fragment_duplicate(new_component) end + if new_component == nil then new_component = true end + component_storage[place] = new_component + end + else local new_component = prefab_component - - if new_component ~= nil and fragment_duplicate then - new_component = fragment_duplicate(new_component) + if new_component == nil then new_component = true end + for place = b_place, e_place do + component_storage[place] = new_component end - - if new_component == nil then - new_component = true - end - - local component_storage = chunk_component_storages[component_index] - - component_storage[place] = new_component end end end @@ -2563,17 +2573,13 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + local prefab_component_storage = prefab_component_storages[prefab_component_index] local prefab_component = prefab_component_storage[prefab_place] local new_component = prefab_component - - if new_component == nil then - new_component = true - end - - local component_storage = chunk_component_storages[component_index] - + if new_component == nil then new_component = true end for place = b_place, e_place do component_storage[place] = new_component end @@ -2587,24 +2593,25 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then + local component_storage = chunk_component_storages[component_index] + ---@type evolved.duplicate? local fragment_duplicate = __evolved_get(fragment, __DUPLICATE) - for place = b_place, e_place do + if fragment_duplicate then + for place = b_place, e_place do + local new_component = component + if new_component ~= nil then new_component = fragment_duplicate(new_component) end + if new_component == nil then new_component = true end + component_storage[place] = new_component + end + else local new_component = component - - if new_component ~= nil and fragment_duplicate then - new_component = fragment_duplicate(new_component) + if new_component == nil then new_component = true end + for place = b_place, e_place do + component_storage[place] = new_component end - - if new_component == nil then - new_component = true - end - - local component_storage = chunk_component_storages[component_index] - - component_storage[place] = new_component end end end @@ -2618,24 +2625,25 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then + local req_component_storage = chunk_component_storages[req_component_index] + ---@type evolved.default?, evolved.duplicate? local req_fragment_default, req_fragment_duplicate = __evolved_get(req_fragment, __DEFAULT, __DUPLICATE) - for place = b_place, e_place do + if req_fragment_duplicate then + for place = b_place, e_place do + local req_component = req_fragment_default + if req_component ~= nil then req_component = req_fragment_duplicate(req_component) end + if req_component == nil then req_component = true end + req_component_storage[place] = req_component + end + else local req_component = req_fragment_default - - if req_component ~= nil and req_fragment_duplicate then - req_component = req_fragment_duplicate(req_component) + if req_component == nil then req_component = true end + for place = b_place, e_place do + req_component_storage[place] = req_component end - - if req_component == nil then - req_component = true - end - - local req_component_storage = chunk_component_storages[req_component_index] - - req_component_storage[place] = req_component end end end @@ -2645,10 +2653,10 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local component_index = chunk_component_indices[fragment] if component_index then - local new_component = component - local component_storage = chunk_component_storages[component_index] + local new_component = component + if new_component == nil then new_component = true end for place = b_place, e_place do component_storage[place] = new_component end @@ -2664,10 +2672,9 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) local req_component_index = chunk_component_indices[req_fragment] if req_component_index then - local req_component = true - local req_component_storage = chunk_component_storages[req_component_index] + local req_component = true for place = b_place, e_place do req_component_storage[place] = req_component end @@ -2692,32 +2699,38 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) if component_index then local component_storage = chunk_component_storages[component_index] - for place = b_place, e_place do - local entity = chunk_entity_list[place] + if fragment_on_set or fragment_on_insert then + for place = b_place, e_place do + local entity = chunk_entity_list[place] - local new_component = component_storage[place] + local new_component = component_storage[place] - if fragment_on_set then - __defer_call_hook(fragment_on_set, entity, fragment, new_component) - end - - if fragment_on_insert then - __defer_call_hook(fragment_on_insert, entity, fragment, new_component) + if fragment_on_set then + __defer_call_hook(fragment_on_set, entity, fragment, new_component) + end + + if fragment_on_insert then + __defer_call_hook(fragment_on_insert, entity, fragment, new_component) + end end + else + -- nothing end else - if fragment_on_set then + if fragment_on_set or fragment_on_insert then for place = b_place, e_place do local entity = chunk_entity_list[place] - __defer_call_hook(fragment_on_set, entity, fragment) - end - end - if fragment_on_insert then - for place = b_place, e_place do - local entity = chunk_entity_list[place] - __defer_call_hook(fragment_on_insert, entity, fragment) + if fragment_on_set then + __defer_call_hook(fragment_on_set, entity, fragment) + end + + if fragment_on_insert then + __defer_call_hook(fragment_on_insert, entity, fragment) + end end + else + -- nothing end end end @@ -3609,7 +3622,7 @@ local __defer_op = { } ---@type table -local __defer_ops = __lua_table_new(__defer_op.__count, 0) +local __defer_ops = __list_new(__defer_op.__count) ---@param entity evolved.entity ---@param fragment evolved.fragment @@ -4666,7 +4679,7 @@ function __evolved_multi_spawn(entity_count, components) end end - local entity_list = __lua_table_new(entity_count, 0) + local entity_list = __list_new(entity_count) for entity_index = 1, entity_count do entity_list[entity_index] = __acquire_id() @@ -4751,7 +4764,7 @@ function __evolved_multi_clone(entity_count, prefab, components) end end - local entity_list = __lua_table_new(entity_count, 0) + local entity_list = __list_new(entity_count) for entity_index = 1, entity_count do entity_list[entity_index] = __acquire_id() @@ -6355,7 +6368,7 @@ function __builder_mt:include(...) local include_count = include_list and #include_list or 0 if include_count == 0 then - include_list = __lua_table_new(argument_count, 0) + include_list = __list_new(argument_count) end for argument_index = 1, argument_count do @@ -6380,7 +6393,7 @@ function __builder_mt:exclude(...) local exclude_count = exclude_list and #exclude_list or 0 if exclude_count == 0 then - exclude_list = __lua_table_new(argument_count, 0) + exclude_list = __list_new(argument_count) end for argument_index = 1, argument_count do @@ -6405,7 +6418,7 @@ function __builder_mt:require(...) local require_count = require_list and #require_list or 0 if require_count == 0 then - require_list = __lua_table_new(argument_count, 0) + require_list = __list_new(argument_count) end for argument_index = 1, argument_count do