From 5184b39f4e6808a712cb985ba761d2c6bec7f736 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 12 Sep 2025 01:52:33 +0700 Subject: [PATCH] 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