From 9d83c3bc13374a98a8fa8ea447c140f15a832ffc Mon Sep 17 00:00:00 2001 From: Ukendio Date: Mon, 24 Mar 2025 17:36:13 +0100 Subject: [PATCH] Fix bits arrangement in IDs --- benches/visual/insertion.bench.luau | 86 ++++++++++++----------------- jecs.luau | 80 ++++++++++----------------- test/tests.luau | 49 ++++++++++------ 3 files changed, 95 insertions(+), 120 deletions(-) diff --git a/benches/visual/insertion.bench.luau b/benches/visual/insertion.bench.luau index 035f9e1..f33821d 100644 --- a/benches/visual/insertion.bench.luau +++ b/benches/visual/insertion.bench.luau @@ -28,22 +28,22 @@ local B6 = ecr.component() local B7 = ecr.component() local B8 = ecr.component() -local C1 = ecs:entity() -local C2 = ecs:entity() -local C3 = ecs:entity() -local C4 = ecs:entity() -local C5 = ecs:entity() -local C6 = ecs:entity() -local C7 = ecs:entity() -local C8 = ecs:entity() -local E1 = mcs:entity() -local E2 = mcs:entity() -local E3 = mcs:entity() -local E4 = mcs:entity() -local E5 = mcs:entity() -local E6 = mcs:entity() -local E7 = mcs:entity() -local E8 = mcs:entity() +local C1 = ecs:component() +local C2 = ecs:component() +local C3 = ecs:component() +local C4 = ecs:component() +local C5 = ecs:component() +local C6 = ecs:component() +local C7 = ecs:component() +local C8 = ecs:component() +local E1 = mcs:component() +local E2 = mcs:component() +local E3 = mcs:component() +local E4 = mcs:component() +local E5 = mcs:component() +local E6 = mcs:component() +local E7 = mcs:component() +local E8 = mcs:component() local registry2 = ecr.registry() return { @@ -52,48 +52,30 @@ return { end, Functions = { - Matter = function() - local e = newWorld:spawn() + Mirror = function() + local e = mcs:entity() for i = 1, 5000 do - newWorld:insert( - e, - A1({ value = true }), - A2({ value = true }), - A3({ value = true }), - A4({ value = true }), - A5({ value = true }), - A6({ value = true }), - A7({ value = true }), - A8({ value = true }) - ) + mcs:set(e, E1, false) + mcs:set(e, E2, false) + mcs:set(e, E3, false) + mcs:set(e, E4, false) + mcs:set(e, E5, false) + mcs:set(e, E6, false) + mcs:set(e, E7, false) + mcs:set(e, E8, false) end end, - - ECR = function() - local e = registry2.create() - for i = 1, 5000 do - registry2:set(e, B1, { value = false }) - registry2:set(e, B2, { value = false }) - registry2:set(e, B3, { value = false }) - registry2:set(e, B4, { value = false }) - registry2:set(e, B5, { value = false }) - registry2:set(e, B6, { value = false }) - registry2:set(e, B7, { value = false }) - registry2:set(e, B8, { value = false }) - end - end, - Jecs = function() local e = ecs:entity() for i = 1, 5000 do - ecs:set(e, C1, { value = false }) - ecs:set(e, C2, { value = false }) - ecs:set(e, C3, { value = false }) - ecs:set(e, C4, { value = false }) - ecs:set(e, C5, { value = false }) - ecs:set(e, C6, { value = false }) - ecs:set(e, C7, { value = false }) - ecs:set(e, C8, { value = false }) + ecs:set(e, C1, false) + ecs:set(e, C2, false) + ecs:set(e, C3, false) + ecs:set(e, C4, false) + ecs:set(e, C5, false) + ecs:set(e, C6, false) + ecs:set(e, C7, false) + ecs:set(e, C8, false) end end, }, diff --git a/jecs.luau b/jecs.luau index 1458e5c..ace51b2 100644 --- a/jecs.luau +++ b/jecs.luau @@ -90,60 +90,35 @@ local EcsOnArchetypeCreate = HI_COMPONENT_ID + 12 local EcsOnArchetypeDelete = HI_COMPONENT_ID + 13 local EcsRest = HI_COMPONENT_ID + 14 -local ECS_PAIR_FLAG = 0x8 -local ECS_ID_FLAGS_MASK = 0x10 -local ECS_ENTITY_MASK = bit32.lshift(1, 24) -local ECS_GENERATION_MASK = bit32.lshift(1, 16) - local ECS_ID_DELETE = 0b0000_0001 local ECS_ID_IS_TAG = 0b0000_0010 local ECS_ID_HAS_ON_ADD = 0b0000_0100 local ECS_ID_HAS_ON_SET = 0b0000_1000 local ECS_ID_HAS_ON_REMOVE = 0b0001_0000 local ECS_ID_MASK = 0b0000_0000 --- stylua: ignore end -local NULL_ARRAY = table.freeze({}) :: Column -local function FLAGS_ADD(is_pair: boolean): number - local flags = 0x0 +local ECS_ENTITY_MASK = bit32.lshift(1, 24) +local ECS_GENERATION_MASK = bit32.lshift(1, 16) - if is_pair then - flags = bit32.bor(flags, ECS_PAIR_FLAG) -- HIGHEST bit in the ID. - end - if false then - flags = bit32.bor(flags, 0x4) -- Set the second flag to true - end - if false then - flags = bit32.bor(flags, 0x2) -- Set the third flag to true - end - if false then - flags = bit32.bor(flags, 0x1) -- LAST BIT in the ID. - end +local NULL_ARRAY = table.freeze({}) - return flags -end - -local function ECS_COMBINE(source: number, target: number): i53 - return (source * 268435456) + (target * ECS_ID_FLAGS_MASK) + +local function ECS_COMBINE(id: number, generation: number): i53 + return id + (generation * ECS_ENTITY_MASK) end +local ECS_PAIR_OFFSET = 2^48 local function ECS_IS_PAIR(e: number): boolean - return if e > ECS_ENTITY_MASK then (e % ECS_ID_FLAGS_MASK) // ECS_PAIR_FLAG ~= 0 else false -end - --- HIGH 24 bits LOW 24 bits -local function ECS_GENERATION(e: i53): i24 - return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_GENERATION_MASK else 0 + return e > ECS_PAIR_OFFSET end local function ECS_GENERATION_INC(e: i53) if e > ECS_ENTITY_MASK then - local flags = e // ECS_ID_FLAGS_MASK - local id = flags // ECS_ENTITY_MASK - local generation = flags % ECS_GENERATION_MASK + local id = e % ECS_ENTITY_MASK + local generation = e // ECS_ENTITY_MASK local next_gen = generation + 1 - if next_gen > ECS_GENERATION_MASK then + if next_gen >= ECS_GENERATION_MASK then return id end @@ -152,22 +127,23 @@ local function ECS_GENERATION_INC(e: i53) return ECS_COMBINE(e, 1) end --- FIRST gets the high ID -local function ECS_ENTITY_T_HI(e: i53): i24 - return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) % ECS_ENTITY_MASK else e -end - --- SECOND local function ECS_ENTITY_T_LO(e: i53): i24 - return if e > ECS_ENTITY_MASK then (e // ECS_ID_FLAGS_MASK) // ECS_ENTITY_MASK else e + return e % ECS_ENTITY_MASK end -local function _STRIP_GENERATION(e: i53): i24 - return ECS_ENTITY_T_LO(e) +local function ECS_GENERATION(e: i53) + return e // ECS_ENTITY_MASK +end + +local function ECS_ENTITY_T_HI(e: i53): i24 + return e // ECS_ENTITY_MASK end local function ECS_PAIR(pred: i53, obj: i53): i53 - return ECS_COMBINE(ECS_ENTITY_T_LO(pred), ECS_ENTITY_T_LO(obj)) + FLAGS_ADD(--[[isPair]] true) :: i53 + pred %= ECS_ENTITY_MASK + obj %= ECS_ENTITY_MASK + + return obj + (pred * 2^24) + ECS_PAIR_OFFSET end local function entity_index_try_get_any(entity_index: EntityIndex, entity: number): Record? @@ -236,14 +212,14 @@ local function entity_index_new_id(entity_index: EntityIndex): i53 return id end --- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits local function ecs_pair_first(world, e) - return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_LO(e)) + local pred = (e - ECS_PAIR_OFFSET) // ECS_ENTITY_MASK + return entity_index_get_alive(world.entity_index, pred) end --- ECS_PAIR_SECOND gets the relationship / pred / LOW bits local function ecs_pair_second(world, e) - return entity_index_get_alive(world.entity_index, ECS_ENTITY_T_HI(e)) + local obj = (e - ECS_PAIR_OFFSET) % ECS_ENTITY_MASK + return entity_index_get_alive(world.entity_index, obj) end local function query_match(query, archetype: Archetype) @@ -476,8 +452,8 @@ local function world_target(world: World, entity: i53, relation: i24, index: num return nil end - if nth >= count then - nth = nth + count + 1 + if nth > count then + nth = nth + count end local tr = idr.cache[archetype_id] diff --git a/test/tests.luau b/test/tests.luau index eb85da1..4da0df6 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -241,19 +241,6 @@ local pe = require("@tools/entity_visualiser").prettify local lifetime_tracker_add = require("@tools/lifetime_tracker") TEST("world:entity()", function() - do CASE "ensure id" - local w = world_new() - w = lifetime_tracker_add(w, {padding_enabled=false}) - local id = w:entity() - CHECK(id == w:entity(id)) - CHECK(w:entity(999 :: any) == 999 :: any) - w:print_snapshot() - local id1 = w:entity(999) - CHECK(w:contains(999)) - CHECK(not w:contains(998)) - print(w:entity(), "newest id after 999") - end - do CASE("unique IDs") local world = jecs.World.new() @@ -1388,10 +1375,15 @@ TEST("world:target", function() world:add(e, pair(C, D)) CHECK(pair(A, B) < pair(A, C)) - CHECK(pair(A, E) < pair(B, C)) + CHECK(pair(A, C) < pair(A, D)) + CHECK(pair(C, A) < pair(C, D)) local records = debug_world_inspect(world).records(e) CHECK(jecs.pair_first(world, pair(B, C)) == B) + local r = jecs.entity_index_try_get(world.entity_index, e) + local archetype = r.archetype + local counts = archetype.counts + CHECK(counts[pair(A, __)] == 4) CHECK(records[pair(B, C)] > records[pair(A, E)]) CHECK(world:target(e, A, 0) == B) CHECK(world:target(e, A, 1) == C) @@ -1401,6 +1393,28 @@ TEST("world:target", function() CHECK(world:target(e, B, 1) == D) CHECK(world:target(e, C, 0) == D) CHECK(world:target(e, C, 1) == nil) + + -- for id in archetype.records do + -- local f = world:get(ecs_pair_first(world, id), jecs.Name) + -- local s = world:get(ecs_pair_second(world, id), jecs.Name) + -- print(`({f}, {s})`) + -- end + -- + + CHECK(archetype.records[pair(A, B)] == 1) + CHECK(archetype.records[pair(A, C)] == 2) + CHECK(archetype.records[pair(A, D)] == 3) + CHECK(archetype.records[pair(A, E)] == 4) + -- print("(A, B)", archetype.records[pair(A, B)]) + -- print("(A, C)", archetype.records[pair(A, C)]) + -- print("(A, D)", archetype.records[pair(A, D)]) + -- print("(A, E)", archetype.records[pair(A, E)]) + + -- print(pair(A, D), pair(B, C)) + -- print("(B, C)", archetype.records[pair(B, C)]) + + CHECK(world:target(e, C, 0) == D) + CHECK(world:target(e, C, 1) == nil) end do @@ -1516,7 +1530,7 @@ TEST("Hooks", function() world:set(e, A, true) world:remove(e, A) CHECK(not world:get(e, A)) - CHECK(not world:get(e, B)) + CHECK(world:get(e, B)) end end end) @@ -1596,7 +1610,6 @@ TEST("repro", function() if cooldown <= 0 then table.insert(toRemove, id) - print("removing") -- world:remove(id, components.Cooldown) else world:set(id, components.Cooldown, cooldown) @@ -1645,6 +1658,10 @@ TEST("wildcard query", function() local entity = world:entity() + local p = pair(Relation, A) + CHECK(jecs.pair_first(world, p) == Relation) + CHECK(jecs.pair_second(world, p) == A) + local w = dwi(world) world:add(entity, pair(Relation, A)) local counter = 0