Query Rework

This commit is contained in:
Ukendio 2024-07-30 17:17:18 +02:00
parent 4e21367caf
commit e9d2154988
3 changed files with 202 additions and 121 deletions

View file

@ -57,7 +57,7 @@ local function flip()
end end
local common = 0 local common = 0
local N = 500 local N = 2^16-2
local archetypes = {} local archetypes = {}
local hm = 0 local hm = 0
@ -171,18 +171,14 @@ return {
Functions = { Functions = {
Mirror = function() Mirror = function()
for i = 1, 1000 do for entityId, firstComponent in mcs:query(E1, E2, E3, E4) do
for entityId, firstComponent in mcs:query(E1, E4) do
end
end end
end, end,
Jecs = function() Jecs = function()
for i = 1, 1000 do for entityId, firstComponent in ecs:query(D1, D2, D3, D4) do
for entityId, firstComponent in ecs:query(D1, D4) do end
end
end
end, end,
}, },
} }

View file

@ -720,15 +720,18 @@ do
return nil :: any return nil :: any
end end
local Arm = function(self: Query, ...)
return self
end
local EmptyQuery: Query = { local EmptyQuery: Query = {
__iter = function(): Item __iter = function(): Item
return noop return noop
end, end,
drain = Arm,
next = noop :: Item, next = noop :: Item,
replace = noop :: (Query, ...any) -> (), replace = noop :: (Query, ...any) -> (),
without = function(self: Query, ...) with = Arm,
return self without = Arm,
end
} }
setmetatable(EmptyQuery, EmptyQuery) setmetatable(EmptyQuery, EmptyQuery)
@ -741,11 +744,75 @@ do
local i: number local i: number
local compatible_archetypes: { Archetype } local compatible_archetypes: { Archetype }
local column_indices: { { number} }
local ids: { number } local ids: { number }
local tr
local columns local columns
local A, B, C, D, E, F, G, H
local a, b, c, d, e, f, g, h
local function query_init(query)
lastArchetype = 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return
end
queryOutput = {}
queryLength = #ids
entities = archetype.entities
i = #entities
columns = archetype.columns
local records = archetype.records
if not B then
a = records[A]
elseif not C then
a = records[A]
b = records[B]
elseif not D then
a = records[A]
b = records[B]
c = records[C]
elseif not E then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
elseif not F then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
elseif not G then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
f = records[F]
elseif not H then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
f = records[F]
g = records[G]
elseif H then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
f = records[F]
g = records[G]
h = records[H]
end
end
local function world_query_next(): any local function world_query_next(): any
local entityId = entities[i] local entityId = entities[i]
while entityId == nil do while entityId == nil do
@ -754,7 +821,52 @@ do
if not archetype then if not archetype then
return nil return nil
end end
tr = column_indices[lastArchetype] local records = archetype.records
if not B then
a = records[A]
elseif not C then
a = records[A]
b = records[B]
elseif not D then
a = records[A]
b = records[B]
c = records[C]
elseif not E then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
elseif not F then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
elseif not G then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
f = records[F]
elseif not H then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
f = records[F]
g = records[G]
elseif H then
a = records[A]
b = records[B]
c = records[C]
d = records[D]
e = records[E]
f = records[F]
g = records[G]
h = records[H]
end
columns = archetype.columns columns = archetype.columns
entities = archetype.entities entities = archetype.entities
i = #entities i = #entities
@ -765,60 +877,57 @@ do
i-=1 i-=1
if queryLength == 1 then if queryLength == 1 then
return entityId, columns[tr[1]][row] return entityId, columns[a][row]
elseif queryLength == 2 then elseif queryLength == 2 then
return entityId, columns[tr[1]][row], columns[tr[2]][row] return entityId, columns[a][row], columns[b][row]
elseif queryLength == 3 then elseif queryLength == 3 then
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row] return entityId, columns[a][row], columns[b][row], columns[c][row]
elseif queryLength == 4 then elseif queryLength == 4 then
return entityId, columns[tr[1]][row], columns[tr[2]][row], columns[tr[3]][row], columns[tr[4]][row] return entityId, columns[a][row], columns[b][row], columns[c][row], columns[d][row]
elseif queryLength == 5 then elseif queryLength == 5 then
return entityId, return entityId,
columns[tr[1]][row], columns[a][row],
columns[tr[2]][row], columns[b][row],
columns[tr[3]][row], columns[c][row],
columns[tr[4]][row], columns[d][row],
columns[tr[5]][row] columns[e][row]
elseif queryLength == 6 then elseif queryLength == 6 then
return entityId, return entityId,
columns[tr[1]][row], columns[a][row],
columns[tr[2]][row], columns[b][row],
columns[tr[3]][row], columns[c][row],
columns[tr[4]][row], columns[d][row],
columns[tr[5]][row], columns[e][row],
columns[tr[6]][row] columns[f][row]
elseif queryLength == 7 then elseif queryLength == 7 then
return entityId, return entityId,
columns[tr[1]][row], columns[a][row],
columns[tr[2]][row], columns[b][row],
columns[tr[3]][row], columns[c][row],
columns[tr[4]][row], columns[d][row],
columns[tr[5]][row], columns[e][row],
columns[tr[6]][row], columns[f][row],
columns[tr[7]][row] columns[g][row]
elseif queryLength == 8 then elseif queryLength == 8 then
return entityId, return entityId,
columns[tr[1]][row], columns[a][row],
columns[tr[2]][row], columns[b][row],
columns[tr[3]][row], columns[c][row],
columns[tr[4]][row], columns[d][row],
columns[tr[5]][row], columns[e][row],
columns[tr[6]][row], columns[f][row],
columns[tr[7]][row], columns[g][row],
columns[tr[8]][row] columns[h][row]
end end
for j in ids do local field = archetype.records
queryOutput[j] = columns[tr[j]][row] for j, id in ids do
end queryOutput[j] = columns[field[id]][row]
end
return entityId, unpack(queryOutput, 1, queryLength) return entityId, unpack(queryOutput, 1, queryLength)
end end
local function world_query_iter()
return world_query_next
end
local function world_query_without(self, ...) local function world_query_without(self, ...)
local withoutComponents = { ... } local withoutComponents = { ... }
for i = #compatible_archetypes, 1, -1 do for i = #compatible_archetypes, 1, -1 do
@ -837,23 +946,11 @@ do
local last = #compatible_archetypes local last = #compatible_archetypes
if last ~= i then if last ~= i then
compatible_archetypes[i] = compatible_archetypes[last] compatible_archetypes[i] = compatible_archetypes[last]
column_indices[i] = column_indices[last]
end end
compatible_archetypes[last] = nil compatible_archetypes[last] = nil
column_indices[last] = nil
end end
end end
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return EmptyQuery
end
entities = archetype.entities
columns = archetype.columns
tr = column_indices[lastArchetype]
i = #entities
return self return self
end end
@ -863,40 +960,42 @@ do
end end
end end
local function world_query_replace(_, fn: (...any) -> (...any)) local function world_query_replace(query, fn: (...any) -> (...any))
for i, archetype in compatible_archetypes do query_init(query)
local tr = column_indices[i]
local columns = archetype.columns
for i, archetype in compatible_archetypes do
local columns = archetype.columns
local tr = archetype.records
for row in archetype.entities do for row in archetype.entities do
if queryLength == 1 then if queryLength == 1 then
local a = columns[tr[1]] local va = columns[tr[a]]
local pa = fn(a[row]) local pa = fn(va[row])
a[row] = pa va[row] = pa
elseif queryLength == 2 then elseif queryLength == 2 then
local a = columns[tr[1]] local va = columns[tr[a]]
local b = columns[tr[2]] local vb = columns[tr[b]]
a[row], b[row] = fn(a[row], b[row]) va[row], vb[row] = fn(va[row], vb[row])
elseif queryLength == 3 then elseif queryLength == 3 then
local a = columns[tr[1]] local va = columns[tr[a]]
local b = columns[tr[2]] local vb = columns[tr[b]]
local c = columns[tr[3]] local vc = columns[tr[c]]
a[row], b[row], c[row] = fn(a[row], b[row], c[row]) va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
elseif queryLength == 4 then elseif queryLength == 4 then
local a = columns[tr[1]] local a = columns[tr[a]]
local b = columns[tr[2]] local b = columns[tr[b]]
local c = columns[tr[3]] local c = columns[tr[c]]
local d = columns[tr[4]] local d = columns[tr[d]]
a[row], b[row], c[row], d[row] = fn( a[row], b[row], c[row], d[row] = fn(
a[row], b[row], c[row], d[row]) a[row], b[row], c[row], d[row])
else else
for i = 1, queryLength do local field = archetype.records
queryOutput[i] = columns[tr[i]][row] for j, id in ids do
end queryOutput[j] = columns[field[id]][row]
end
world_query_replace_values(row, columns, world_query_replace_values(row, columns,
fn(unpack(queryOutput))) fn(unpack(queryOutput)))
end end
@ -922,23 +1021,11 @@ do
local last = #compatible_archetypes local last = #compatible_archetypes
if last ~= i then if last ~= i then
compatible_archetypes[i] = compatible_archetypes[last] compatible_archetypes[i] = compatible_archetypes[last]
column_indices[i] = column_indices[last]
end end
compatible_archetypes[last] = nil compatible_archetypes[last] = nil
column_indices[last] = nil
end end
end end
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return EmptyQuery
end
entities = archetype.entities
columns = archetype.columns
tr = column_indices[lastArchetype]
i = #entities
return query return query
end end
@ -949,8 +1036,24 @@ do
return compatible_archetypes return compatible_archetypes
end end
local drain
local function world_query_drain(query)
drain = true
query_init(query)
return query
end
local function world_query_iter(query)
if not drain then
query_init(query)
end
return world_query_next
end
local it = { local it = {
__iter = world_query_iter, __iter = world_query_iter,
drain = world_query_drain,
next = world_query_next, next = world_query_next,
with = world_query_with, with = world_query_with,
without = world_query_without, without = world_query_without,
@ -966,11 +1069,11 @@ do
error("Missing components") error("Missing components")
end end
local indices = {}
compatible_archetypes = {} compatible_archetypes = {}
local length = 0 local length = 0
local components = { ... } :: any local components = { ... } :: any
A, B, C, D, E, F, G, H = ...
local archetypes = world.archetypes local archetypes = world.archetypes
local firstArchetypeMap: ArchetypeMap local firstArchetypeMap: ArchetypeMap
@ -991,7 +1094,6 @@ do
local compatibleArchetype = archetypes[id] local compatibleArchetype = archetypes[id]
local archetypeRecords = compatibleArchetype.records local archetypeRecords = compatibleArchetype.records
local records = {}
local skip = false local skip = false
for i, componentId in components do for i, componentId in components do
@ -1000,8 +1102,6 @@ do
skip = true skip = true
break break
end end
-- index should be index.offset
records[i] = index
end end
if skip then if skip then
@ -1010,27 +1110,11 @@ do
length += 1 length += 1
compatible_archetypes[length] = compatibleArchetype compatible_archetypes[length] = compatibleArchetype
indices[length] = records
end end
column_indices = indices drain = false
ids = components ids = components
lastArchetype = 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return EmptyQuery
end
queryOutput = {}
queryLength = #ids
entities = archetype.entities
i = #entities
tr = column_indices[lastArchetype]
columns = archetype.columns
return it return it
end end
end end

View file

@ -246,7 +246,8 @@ TEST("world:query()", function()
world:set(eAB, A, true) world:set(eAB, A, true)
world:set(eAB, B, true) world:set(eAB, B, true)
local q = world:query(A) -- Should drain the iterator
local q = world:query(A):drain()
local i = 0 local i = 0
local j = 0 local j = 0
@ -740,7 +741,7 @@ do
local function changes_added() local function changes_added()
added = true added = true
local q = world:query(T):without(PreviousT) local q = world:query(T):without(PreviousT):drain()
return function() return function()
local id, data = q:next() local id, data = q:next()
if not id then if not id then
@ -758,7 +759,7 @@ do
end end
local function changes_changed() local function changes_changed()
local q = world:query(T, PreviousT) local q = world:query(T, PreviousT):drain()
return function() return function()
local id, new, old = q:next() local id, new, old = q:next()
@ -785,7 +786,7 @@ do
local function changes_removed() local function changes_removed()
removed = true removed = true
local q = world:query(PreviousT):without(T) local q = world:query(PreviousT):without(T):drain()
return function() return function()
local id = q:next() local id = q:next()
if id then if id then