Change casing

This commit is contained in:
Ukendio 2024-07-15 20:29:06 +02:00
parent 1b6cdd4791
commit 459e670ce9
5 changed files with 319 additions and 158 deletions

View file

@ -2,6 +2,5 @@
"aliases": {
"jecs": "src",
"testkit": "testkit",
"mirror": "mirror"
}
}

161
benches/cached.luau Normal file
View 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

View file

@ -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

View file

@ -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,43 +624,50 @@ local function world_clear(world: World, entityId: i53)
entity_move(world.entityIndex, entityId, record, ROOT_ARCHETYPE)
end
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(record: Record, componentId: i24): any
local archetype = record.archetype
if not archetype then
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)
export type Query = typeof({
@ -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,
}

View file

@ -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