mirror of
https://github.com/Ukendio/jecs.git
synced 2025-04-24 17:10:03 +00:00
Change casing
This commit is contained in:
parent
1b6cdd4791
commit
459e670ce9
5 changed files with 319 additions and 158 deletions
1
.luaurc
1
.luaurc
|
@ -2,6 +2,5 @@
|
|||
"aliases": {
|
||||
"jecs": "src",
|
||||
"testkit": "testkit",
|
||||
"mirror": "mirror"
|
||||
}
|
||||
}
|
||||
|
|
161
benches/cached.luau
Normal file
161
benches/cached.luau
Normal file
|
@ -0,0 +1,161 @@
|
|||
|
||||
local jecs = require("@jecs")
|
||||
local mirror = require("../mirror/init")
|
||||
|
||||
type i53 = number
|
||||
|
||||
do
|
||||
TITLE(testkit.color.white_underline("Jecs query"))
|
||||
local ecs = jecs.World.new()
|
||||
do
|
||||
TITLE("one component in common")
|
||||
|
||||
local function view_bench(world: jecs.World, A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53)
|
||||
BENCH("4 component", function()
|
||||
for _ in world:query(D, C, B, A) do
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local D1 = ecs:component()
|
||||
local D2 = ecs:component()
|
||||
local D3 = ecs:component()
|
||||
local D4 = ecs:component()
|
||||
local D5 = ecs:component()
|
||||
local D6 = ecs:component()
|
||||
local D7 = ecs:component()
|
||||
local D8 = ecs:component()
|
||||
|
||||
local function flip()
|
||||
return math.random() >= 0.15
|
||||
end
|
||||
|
||||
local added = 0
|
||||
local archetypes = {}
|
||||
for i = 1, 2 ^ 16 - 2 do
|
||||
local entity = ecs:entity()
|
||||
|
||||
local combination = ""
|
||||
|
||||
if flip() then
|
||||
combination ..= "B"
|
||||
ecs:set(entity, D2, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "C"
|
||||
ecs:set(entity, D3, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "D"
|
||||
ecs:set(entity, D4, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "E"
|
||||
ecs:set(entity, D5, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "F"
|
||||
ecs:set(entity, D6, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "G"
|
||||
ecs:set(entity, D7, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "H"
|
||||
ecs:set(entity, D8, {value = true})
|
||||
end
|
||||
|
||||
if #combination == 7 then
|
||||
added += 1
|
||||
ecs:set(entity, D1, {value = true})
|
||||
end
|
||||
archetypes[combination] = true
|
||||
end
|
||||
|
||||
local a = 0
|
||||
for _ in archetypes do
|
||||
a += 1
|
||||
end
|
||||
|
||||
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
TITLE(testkit.color.white_underline("Mirror query"))
|
||||
local ecs = mirror.World.new()
|
||||
do
|
||||
TITLE("one component in common")
|
||||
|
||||
local function view_bench(world: jecs.World, A: i53, B: i53, C: i53, D: i53, E: i53, F: i53, G: i53, H: i53)
|
||||
BENCH("4 component", function()
|
||||
for _ in world:query(D, C, B, A) do
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local D1 = ecs:component()
|
||||
local D2 = ecs:component()
|
||||
local D3 = ecs:component()
|
||||
local D4 = ecs:component()
|
||||
local D5 = ecs:component()
|
||||
local D6 = ecs:component()
|
||||
local D7 = ecs:component()
|
||||
local D8 = ecs:component()
|
||||
|
||||
local function flip()
|
||||
return math.random() >= 0.15
|
||||
end
|
||||
|
||||
local added = 0
|
||||
local archetypes = {}
|
||||
for i = 1, 2 ^ 16 - 2 do
|
||||
local entity = ecs:entity()
|
||||
|
||||
local combination = ""
|
||||
|
||||
if flip() then
|
||||
combination ..= "B"
|
||||
ecs:set(entity, D2, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "C"
|
||||
ecs:set(entity, D3, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "D"
|
||||
ecs:set(entity, D4, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "E"
|
||||
ecs:set(entity, D5, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "F"
|
||||
ecs:set(entity, D6, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "G"
|
||||
ecs:set(entity, D7, {value = true})
|
||||
end
|
||||
if flip() then
|
||||
combination ..= "H"
|
||||
ecs:set(entity, D8, {value = true})
|
||||
end
|
||||
|
||||
if #combination == 7 then
|
||||
added += 1
|
||||
ecs:set(entity, D1, {value = true})
|
||||
end
|
||||
archetypes[combination] = true
|
||||
end
|
||||
|
||||
local a = 0
|
||||
for _ in archetypes do
|
||||
a += 1
|
||||
end
|
||||
|
||||
view_bench(ecs, D1, D2, D3, D4, D5, D6, D7, D8)
|
||||
end
|
||||
end
|
|
@ -170,30 +170,16 @@ return {
|
|||
end,
|
||||
|
||||
Functions = {
|
||||
Mirror = function()
|
||||
local matched = 0
|
||||
for entityId, firstComponent in mcs:query(E1, E4, E6, E8) do
|
||||
matched += 1
|
||||
end
|
||||
end,
|
||||
|
||||
Matter = function()
|
||||
local matched = 0
|
||||
for entityId, firstComponent in newWorld:query(A1, A4, A6, A8) do
|
||||
matched += 1
|
||||
end
|
||||
end,
|
||||
|
||||
ECR = function()
|
||||
local matched = 0
|
||||
for entityId, firstComponent in registry2:view(B1, B4, B6, B8) do
|
||||
for entityId, firstComponent in registry2:view(B1, B4) do
|
||||
matched += 1
|
||||
end
|
||||
end,
|
||||
|
||||
Jecs = function()
|
||||
local matched = 0
|
||||
for entityId, firstComponent in ecs:query(D1, D4, D6, D8) do
|
||||
for entityId, firstComponent in ecs:query(D1, D4) do
|
||||
matched += 1
|
||||
end
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ end
|
|||
local ERROR_ENTITY_NOT_ALIVE = "Entity is not alive"
|
||||
local ERROR_GENERATION_INVALID = "INVALID GENERATION"
|
||||
|
||||
local function getAlive(index: EntityIndex, e: i24): i53
|
||||
local function entity_index_get_alive(index: EntityIndex, e: i24): i53
|
||||
local denseArray = index.dense
|
||||
local id = denseArray[ECS_ENTITY_T_LO(e)]
|
||||
|
||||
|
@ -159,18 +159,18 @@ local function getAlive(index: EntityIndex, e: i24): i53
|
|||
error(ERROR_ENTITY_NOT_ALIVE)
|
||||
end
|
||||
|
||||
local function sparseGet(entityIndex, id)
|
||||
return entityIndex.sparse[getAlive(entityIndex, id)]
|
||||
local function entity_index_sparse_get(entityIndex, id)
|
||||
return entityIndex.sparse[entity_index_get_alive(entityIndex, id)]
|
||||
end
|
||||
|
||||
-- ECS_PAIR_FIRST, gets the relationship target / obj / HIGH bits
|
||||
local function ECS_PAIR_RELATION(entityIndex, e)
|
||||
return getAlive(entityIndex, ECS_ENTITY_T_HI(e))
|
||||
local function ecs_pair_relation(entityIndex, e)
|
||||
return entity_index_get_alive(entityIndex, ECS_ENTITY_T_HI(e))
|
||||
end
|
||||
|
||||
-- ECS_PAIR_SECOND gets the relationship / pred / LOW bits
|
||||
local function ECS_PAIR_OBJECT(entityIndex, e)
|
||||
return getAlive(entityIndex, ECS_ENTITY_T_LO(e))
|
||||
local function ecs_pair_object(entityIndex, e)
|
||||
return entity_index_get_alive(entityIndex, ECS_ENTITY_T_LO(e))
|
||||
end
|
||||
|
||||
local function entity_index_new_id(entityIndex: EntityIndex, index: i24): i53
|
||||
|
@ -300,8 +300,8 @@ local function archetype_of(world: any, types: { i24 }, prev: Archetype?): Arche
|
|||
idr.size += 1
|
||||
records[componentId] = i
|
||||
if ECS_IS_PAIR(componentId) then
|
||||
local relation = ECS_PAIR_RELATION(world.entityIndex, componentId)
|
||||
local object = ECS_PAIR_OBJECT(world.entityIndex, componentId)
|
||||
local relation = ecs_pair_relation(world.entityIndex, componentId)
|
||||
local object = ecs_pair_object(world.entityIndex, componentId)
|
||||
|
||||
local r = ECS_PAIR(relation, EcsWildcard)
|
||||
local idr_r = id_record_ensure(componentIndex, r)
|
||||
|
@ -375,7 +375,7 @@ local function world_target(world: World, entity: i53, relation: i24--[[, nth: n
|
|||
return nil
|
||||
end
|
||||
|
||||
return ECS_PAIR_OBJECT(entityIndex, archetype.types[archetypeRecord])
|
||||
return ecs_pair_object(entityIndex, archetype.types[archetypeRecord])
|
||||
end
|
||||
|
||||
local function world_parent(world: World, entity: i53)
|
||||
|
@ -408,22 +408,22 @@ local function find_insert(types: { i53 }, toAdd: i53): number
|
|||
return #types + 1
|
||||
end
|
||||
|
||||
local function find_archetype_with(world: World, node: Archetype, componentId: i53): Archetype
|
||||
local function find_archetype_with(world: World, node: Archetype, id: i53): Archetype
|
||||
local types = node.types
|
||||
-- Component IDs are added incrementally, so inserting and sorting
|
||||
-- them each time would be expensive. Instead this insertion sort can find the insertion
|
||||
-- point in the types array.
|
||||
|
||||
local destinationType = table.clone(node.types) :: { i53 }
|
||||
local at = find_insert(types, componentId)
|
||||
local dst_type = table.clone(node.types) :: { i53 }
|
||||
local at = find_insert(types, id)
|
||||
if at == -1 then
|
||||
-- If it finds a duplicate, it just means it is the same archetype so it can return it
|
||||
-- directly instead of needing to hash types for a lookup to the archetype.
|
||||
return node
|
||||
end
|
||||
table.insert(destinationType, at, componentId)
|
||||
table.insert(dst_type, at, id)
|
||||
|
||||
return archetype_ensure(world, destinationType, node)
|
||||
return archetype_ensure(world, dst_type, node)
|
||||
end
|
||||
|
||||
local function edge_ensure(archetype: Archetype, componentId: i53): ArchetypeEdge
|
||||
|
@ -624,42 +624,49 @@ local function world_clear(world: World, entityId: i53)
|
|||
entity_move(world.entityIndex, entityId, record, ROOT_ARCHETYPE)
|
||||
end
|
||||
|
||||
-- Keeping the function as small as possible to enable inlining
|
||||
local function fetch(record: Record, componentId: i24): any
|
||||
local archetype = record.archetype
|
||||
if not archetype then
|
||||
local world_get: (world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?) -> (...any)
|
||||
do
|
||||
-- Keeping the function as small as possible to enable inlining
|
||||
local function fetch(id: i24, records, columns, row): any
|
||||
local tr = records[id]
|
||||
|
||||
if not tr then
|
||||
return nil
|
||||
end
|
||||
|
||||
local archetypeRecord = archetype.records[componentId]
|
||||
|
||||
if not archetypeRecord then
|
||||
return nil
|
||||
return columns[tr][row]
|
||||
end
|
||||
|
||||
return archetype.columns[archetypeRecord][record.row]
|
||||
end
|
||||
|
||||
local function world_get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any
|
||||
function world_get(world: World, entityId: i53, a: i53, b: i53?, c: i53?, d: i53?, e: i53?): ...any
|
||||
local id = entityId
|
||||
local record = world.entityIndex.sparse[id]
|
||||
if not record then
|
||||
return nil
|
||||
end
|
||||
|
||||
local va = fetch(record, a)
|
||||
local archetype = record.archetype
|
||||
if not archetype then
|
||||
return nil
|
||||
end
|
||||
|
||||
local records = archetype.records
|
||||
local columns = archetype.records
|
||||
local row = record.row
|
||||
|
||||
local va = fetch(a, tr, columns, row)
|
||||
|
||||
if b == nil then
|
||||
return va
|
||||
elseif c == nil then
|
||||
return va, fetch(record, b)
|
||||
return va, fetch(b, tr, columns, row)
|
||||
elseif d == nil then
|
||||
return va, fetch(record, b), fetch(record, c)
|
||||
return va, fetch(b, tr, columns, row), fetch(c, tr, columns, row)
|
||||
elseif e == nil then
|
||||
return va, fetch(record, b), fetch(record, c), fetch(record, d)
|
||||
return va, fetch(b, tr, columns, row), fetch(c, tr, columns, row), fetch(d, tr, columns, row)
|
||||
else
|
||||
error("args exceeded")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
type Item = () -> (number, ...any)
|
||||
|
@ -848,17 +855,23 @@ do
|
|||
end
|
||||
end
|
||||
|
||||
local cache
|
||||
|
||||
function world_query(world: World, ...: number): Query
|
||||
-- breaking?
|
||||
if (...) == nil then
|
||||
error("Missing components")
|
||||
end
|
||||
|
||||
|
||||
indices = {}
|
||||
compatibleArchetypes = {}
|
||||
length = 0
|
||||
components = { ... }
|
||||
|
||||
if cache then
|
||||
compatibleArchetypes = cache
|
||||
else
|
||||
compatibleArchetypes = {}
|
||||
local archetypes: { Archetype } = world.archetypes :: any
|
||||
local firstArchetypeMap: ArchetypeMap
|
||||
local componentIndex = world.componentIndex
|
||||
|
@ -900,6 +913,8 @@ do
|
|||
indices[length] = records
|
||||
end
|
||||
|
||||
cache = compatibleArchetypes
|
||||
end
|
||||
lastArchetype = 1
|
||||
archetype = compatibleArchetypes[lastArchetype]
|
||||
|
||||
|
@ -1113,14 +1128,15 @@ return {
|
|||
|
||||
Rest = EcsRest,
|
||||
|
||||
pair = (ECS_PAIR :: any) :: <R, T>(pred: Entity, obj: Entity) -> number,
|
||||
|
||||
-- Inwards facing API for testing
|
||||
IS_PAIR = ECS_IS_PAIR,
|
||||
ECS_ID = ECS_ENTITY_T_LO,
|
||||
ECS_PAIR = ECS_PAIR,
|
||||
ECS_GENERATION_INC = ECS_GENERATION_INC,
|
||||
ECS_GENERATION = ECS_GENERATION,
|
||||
ECS_PAIR_RELATION = ECS_PAIR_RELATION,
|
||||
ECS_PAIR_OBJECT = ECS_PAIR_OBJECT,
|
||||
|
||||
pair = (ECS_PAIR :: any) :: <R, T>(pred: Entity, obj: Entity) -> number,
|
||||
getAlive = getAlive,
|
||||
ecs_pair_relation = ecs_pair_relation,
|
||||
ecs_pair_object = ecs_pair_object,
|
||||
entity_index_get_alive = entity_index_get_alive,
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ local __ = jecs.Wildcard
|
|||
local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
|
||||
local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
|
||||
local IS_PAIR = jecs.IS_PAIR
|
||||
local ECS_PAIR = jecs.ECS_PAIR
|
||||
local getAlive = jecs.getAlive
|
||||
local ECS_PAIR_RELATION = jecs.ECS_PAIR_RELATION
|
||||
local ECS_PAIR_OBJECT = jecs.ECS_PAIR_OBJECT
|
||||
local pair = jecs.pair
|
||||
local getAlive = jecs.entity_index_get_alive
|
||||
local ecs_pair_relation = jecs.ecs_pair_relation
|
||||
local ecs_pair_object = jecs.ecs_pair_object
|
||||
|
||||
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
|
||||
local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
|
||||
|
@ -256,11 +256,11 @@ TEST("world", function()
|
|||
|
||||
CHECK(IS_PAIR(world:entity()) == false)
|
||||
|
||||
local pair = ECS_PAIR(e2, e3)
|
||||
local pair = pair(e2, e3)
|
||||
CHECK(IS_PAIR(pair) == true)
|
||||
|
||||
CHECK(ECS_PAIR_RELATION(world.entityIndex, pair) == e2)
|
||||
CHECK(ECS_PAIR_OBJECT(world.entityIndex, pair) == e3)
|
||||
CHECK(ecs_pair_relation(world.entityIndex, pair) == e2)
|
||||
CHECK(ecs_pair_object(world.entityIndex, pair) == e3)
|
||||
end
|
||||
|
||||
do CASE("should allow querying for relations")
|
||||
|
@ -269,8 +269,8 @@ TEST("world", function()
|
|||
local Apples = world:entity()
|
||||
local bob = world:entity()
|
||||
|
||||
world:set(bob, ECS_PAIR(Eats, Apples), true)
|
||||
for e, bool in world:query(ECS_PAIR(Eats, Apples)) do
|
||||
world:set(bob, pair(Eats, Apples), true)
|
||||
for e, bool in world:query(pair(Eats, Apples)) do
|
||||
CHECK(e == bob)
|
||||
CHECK(bool)
|
||||
end
|
||||
|
@ -282,14 +282,14 @@ TEST("world", function()
|
|||
local Apples = world:entity()
|
||||
local bob = world:entity()
|
||||
|
||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
||||
|
||||
local w = jecs.Wildcard
|
||||
for e, data in world:query(ECS_PAIR(Eats, w)) do
|
||||
for e, data in world:query(pair(Eats, w)) do
|
||||
CHECK(e == bob)
|
||||
CHECK(data == "bob eats apples")
|
||||
end
|
||||
for e, data in world:query(ECS_PAIR(w, Apples)) do
|
||||
for e, data in world:query(pair(w, Apples)) do
|
||||
CHECK(e == bob)
|
||||
CHECK(data == "bob eats apples")
|
||||
end
|
||||
|
@ -303,12 +303,12 @@ TEST("world", function()
|
|||
local bob = world:entity()
|
||||
local alice = world:entity()
|
||||
|
||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
|
||||
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
||||
world:set(alice, pair(Eats, Oranges), "alice eats oranges")
|
||||
|
||||
local w = jecs.Wildcard
|
||||
local count = 0
|
||||
for e, data in world:query(ECS_PAIR(Eats, w)) do
|
||||
for e, data in world:query(pair(Eats, w)) do
|
||||
count += 1
|
||||
if e == bob then
|
||||
CHECK(data == "bob eats apples")
|
||||
|
@ -320,7 +320,7 @@ TEST("world", function()
|
|||
CHECK(count == 2)
|
||||
count = 0
|
||||
|
||||
for e, data in world:query(ECS_PAIR(w, Apples)) do
|
||||
for e, data in world:query(pair(w, Apples)) do
|
||||
count += 1
|
||||
CHECK(data == "bob eats apples")
|
||||
end
|
||||
|
@ -337,21 +337,21 @@ TEST("world", function()
|
|||
local alice = world:entity()
|
||||
|
||||
world:set(bob, Apples, "apples")
|
||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
|
||||
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
||||
world:set(alice, pair(Eats, Oranges), "alice eats oranges")
|
||||
|
||||
world:delete(Apples)
|
||||
local Wildcard = jecs.Wildcard
|
||||
|
||||
local count = 0
|
||||
for _, data in world:query(ECS_PAIR(Wildcard, Apples)) do
|
||||
for _, data in world:query(pair(Wildcard, Apples)) do
|
||||
count += 1
|
||||
end
|
||||
|
||||
world:delete(ECS_PAIR(Eats, Apples))
|
||||
world:delete(pair(Eats, Apples))
|
||||
|
||||
CHECK(count == 0)
|
||||
CHECK(world:get(bob, ECS_PAIR(Eats, Apples)) == nil)
|
||||
CHECK(world:get(bob, pair(Eats, Apples)) == nil)
|
||||
end
|
||||
|
||||
do CASE("should error when setting invalid pair")
|
||||
|
@ -362,13 +362,12 @@ TEST("world", function()
|
|||
|
||||
world:delete(Apples)
|
||||
|
||||
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
|
||||
world:set(bob, pair(Eats, Apples), "bob eats apples")
|
||||
end
|
||||
|
||||
do CASE("should find target for ChildOf")
|
||||
local world = jecs.World.new()
|
||||
local ChildOf = jecs.ChildOf
|
||||
local pair = ECS_PAIR
|
||||
|
||||
local Name = world:component()
|
||||
|
||||
|
@ -383,7 +382,7 @@ TEST("world", function()
|
|||
CHECK(world:parent(bob) == alice) -- O(1)
|
||||
|
||||
local count = 0
|
||||
for _, name in world:query(Name, ECS_PAIR(ChildOf, alice)) do
|
||||
for _, name in world:query(Name, pair(ChildOf, alice)) do
|
||||
count += 1
|
||||
end
|
||||
CHECK(count == 2)
|
||||
|
@ -521,11 +520,11 @@ TEST("world", function()
|
|||
local Bob = world:component()
|
||||
|
||||
local helloBob = world:entity()
|
||||
world:add(helloBob, ECS_PAIR(Hello, Bob))
|
||||
world:add(helloBob, pair(Hello, Bob))
|
||||
world:add(helloBob, Bob)
|
||||
|
||||
local withoutCount = 0
|
||||
for _ in world:query(ECS_PAIR(Hello, Bob)):without(Bob) do
|
||||
for _ in world:query(pair(Hello, Bob)):without(Bob) do
|
||||
withoutCount += 1
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue