From b1b627b6772aff3897a4b9106a04506fca545cf8 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 23 Sep 2025 07:53:11 +0700 Subject: [PATCH] improve perf of cloning prefabs with many unique fragments --- README.md | 1 + ROADMAP.md | 1 - develop/untests.lua | 45 +++++++++++++++++++++++++++++++++++++++++++++ evolved.lua | 26 ++++++++++++++++++-------- 4 files changed, 64 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b77e55a..b4701fa 100644 --- a/README.md +++ b/README.md @@ -1303,6 +1303,7 @@ builder_mt:destruction_policy :: id -> builder - The internal garbage collector now collects more garbage - Improved system processing debugging experience with stack traces on errors - [`SET/ASSIGN hooks`](#fragment-hooks) are not invoked for tags on override operations anymore +- Improved performance of cloning prefabs with many [`Unique Fragments`](#unique-fragments) ### v1.2.0 diff --git a/ROADMAP.md b/ROADMAP.md index 9d89f3b..18ce054 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -14,4 +14,3 @@ - We can return deferred status from modifying operations and spawn/clone methods. - Should we make one builder:build method instead of :spawn and :clone? -- Should we cache the result of without_unique_fragments to clone faster? diff --git a/develop/untests.lua b/develop/untests.lua index abc01f7..d5dca98 100644 --- a/develop/untests.lua +++ b/develop/untests.lua @@ -6373,3 +6373,48 @@ do assert(evo.get(e, f) ~= v2_default) end end + +do + local f1, f2 = evo.id(2) + + local prefab = evo.builder():prefab():set(f1, 11):set(f2, 22):spawn() + + do + local entity = evo.clone(prefab) + assert(evo.has(entity, f1) and evo.get(entity, f1) == 11) + assert(evo.has(entity, f2) and evo.get(entity, f2) == 22) + end + + evo.set(f2, evo.UNIQUE) + + do + local entity = evo.clone(prefab) + assert(evo.has(entity, f1) and evo.get(entity, f1) == 11) + assert(not evo.has(entity, f2) and evo.get(entity, f2) == nil) + end + + evo.remove(f2, evo.UNIQUE) + + do + local entity = evo.clone(prefab) + assert(evo.has(entity, f1) and evo.get(entity, f1) == 11) + assert(evo.has(entity, f2) and evo.get(entity, f2) == 22) + end + + evo.set(f1, evo.UNIQUE) + evo.set(f2, evo.UNIQUE) + + do + local entity = evo.clone(prefab) + assert(evo.empty(entity)) + end + + evo.remove(f1, evo.UNIQUE) + evo.remove(f2, evo.UNIQUE) + + do + local entity = evo.clone(prefab) + assert(evo.has(entity, f1) and evo.get(entity, f1) == 11) + assert(evo.has(entity, f2) and evo.get(entity, f2) == 22) + end +end diff --git a/evolved.lua b/evolved.lua index d325cb8..294effc 100644 --- a/evolved.lua +++ b/evolved.lua @@ -160,6 +160,7 @@ local __group_subsystems = {} ---@type table ---@field package __without_fragment_edges table +---@field package __without_unique_fragments? evolved.chunk ---@field package __unreachable_or_collected boolean ---@field package __has_setup_hooks boolean ---@field package __has_assign_hooks boolean @@ -998,6 +999,7 @@ function __new_chunk(chunk_parent, chunk_fragment) __component_fragments = {}, __with_fragment_edges = {}, __without_fragment_edges = {}, + __without_unique_fragments = nil, __unreachable_or_collected = false, __has_setup_hooks = false, __has_assign_hooks = false, @@ -1116,6 +1118,12 @@ function __update_chunk_caches(chunk) chunk.__has_internal_fragments = has_internal_fragments chunk.__has_required_fragments = has_required_fragments + + if has_unique_fragments then + chunk.__without_unique_fragments = nil + else + chunk.__without_unique_fragments = chunk + end end ---@param chunk evolved.chunk @@ -2240,13 +2248,14 @@ function __clone_entity(entity, prefab, components) __error_fmt('clone entity operations should be deferred') end - local prefab_primary = prefab % 2 ^ 20 + local prefab_chunk, prefab_place = __evolved_locate(prefab) - local prefab_chunk = __entity_chunks[prefab_primary] - local prefab_place = __entity_places[prefab_primary] + if prefab_chunk and prefab_chunk.__has_unique_fragments and not prefab_chunk.__without_unique_fragments then + prefab_chunk.__without_unique_fragments = __chunk_without_unique_fragments(prefab_chunk) + end local chunk = __chunk_with_components( - __chunk_without_unique_fragments(prefab_chunk), + prefab_chunk and prefab_chunk.__without_unique_fragments, components) if not chunk then @@ -2491,13 +2500,14 @@ function __multi_clone_entity(entity_list, entity_count, prefab, components) __error_fmt('clone entity operations should be deferred') end - local prefab_primary = prefab % 2 ^ 20 + local prefab_chunk, prefab_place = __evolved_locate(prefab) - local prefab_chunk = __entity_chunks[prefab_primary] - local prefab_place = __entity_places[prefab_primary] + if prefab_chunk and prefab_chunk.__has_unique_fragments and not prefab_chunk.__without_unique_fragments then + prefab_chunk.__without_unique_fragments = __chunk_without_unique_fragments(prefab_chunk) + end local chunk = __chunk_with_components( - __chunk_without_unique_fragments(prefab_chunk), + prefab_chunk and prefab_chunk.__without_unique_fragments, components) if not chunk then