Specialize iterator

This commit is contained in:
Ukendio 2024-07-31 18:48:34 +02:00
parent c486e85d43
commit 7476952f89
2 changed files with 212 additions and 121 deletions

View file

@ -739,7 +739,6 @@ do
local lastArchetype: number local lastArchetype: number
local archetype: Archetype local archetype: Archetype
local queryOutput: { any } local queryOutput: { any }
local queryLength: number
local entities: { number } local entities: { number }
local i: number local i: number
@ -747,7 +746,7 @@ do
local ids: { number } local ids: { number }
local columns local columns
local A, B, C, D, E, F, G, H local A, B, C, D, E, F, G, H, I
local a, b, c, d, e, f, g, h local a, b, c, d, e, f, g, h
local init local init
@ -820,123 +819,193 @@ do
end end
end end
local function world_query_iter_next(): any local world_query_iter_next
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return nil
end
entities = archetype.entities local function world_query_iter_create()
i = #entities if not B then
if i == 0 then function world_query_iter_next(): any
continue local entityId = entities[i]
end while entityId == nil do
entityId = entities[i] lastArchetype += 1
columns = archetype.columns archetype = compatible_archetypes[lastArchetype]
local records = archetype.records if not archetype then
if not B then return nil
a = columns[records[A]] end
elseif not C then
a = columns[records[A]]
b = columns[records[B]]
elseif not D then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
elseif not E then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
elseif not F then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
e = columns[records[E]]
elseif not G then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
e = columns[records[E]]
f = columns[records[F]]
elseif not H then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
e = columns[records[E]]
f = columns[records[F]]
g = columns[records[G]]
elseif H then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[D]]
e = columns[records[E]]
f = columns[records[F]]
g = columns[records[G]]
h = columns[records[H]]
end
end
local row = i entities = archetype.entities
i-=1 i = #entities
if i == 0 then
continue
end
entityId = entities[i]
columns = archetype.columns
local records = archetype.records
a = columns[records[A]]
end
if queryLength == 1 then local row = i
return entityId, a[row] i-=1
elseif queryLength == 2 then
return entityId, a[row], b[row]
elseif queryLength == 3 then
return entityId, a[row], b[row], c[row]
elseif queryLength == 4 then
return entityId, a[row], b[row], c[row], d[row]
elseif queryLength == 5 then
return entityId,
a[row],
b[row],
c[row],
d[row],
e[row]
elseif queryLength == 6 then
return entityId,
a[row],
b[row],
c[row],
d[row],
e[row],
f[row]
elseif queryLength == 7 then
return entityId,
a[row],
b[row],
c[row],
d[row],
e[row],
f[row],
g[row]
elseif queryLength == 8 then
return entityId,
a[row],
b[row],
c[row],
d[row],
e[row],
f[row],
g[row],
h[row]
end
local field = archetype.records return entityId, a[row]
for j, id in ids do end
queryOutput[j] = columns[field[id]][row] elseif not C then
end function world_query_iter_next(): any
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return nil
end
return entityId, unpack(queryOutput, 1, queryLength) entities = archetype.entities
end i = #entities
if i == 0 then
continue
end
entityId = entities[i]
columns = archetype.columns
local records = archetype.records
a = columns[records[A]]
b = columns[records[B]]
end
local row = i
i-=1
return entityId, a[row], b[row]
end
elseif not D then
function world_query_iter_next(): any
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return nil
end
entities = archetype.entities
i = #entities
if i == 0 then
continue
end
entityId = entities[i]
columns = archetype.columns
local records = archetype.records
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
end
local row = i
i-=1
return entityId, a[row], b[row], c[row]
end
elseif not E then
function world_query_iter_next(): any
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return nil
end
entities = archetype.entities
i = #entities
if i == 0 then
continue
end
entityId = entities[i]
columns = archetype.columns
local records = archetype.records
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
end
local row = i
i-=1
return entityId, a[row], b[row], c[row], d[row]
end
else
function world_query_iter_next(): any
local entityId = entities[i]
while entityId == nil do
lastArchetype += 1
archetype = compatible_archetypes[lastArchetype]
if not archetype then
return nil
end
entities = archetype.entities
i = #entities
if i == 0 then
continue
end
entityId = entities[i]
columns = archetype.columns
local records = archetype.records
if not F then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
e = columns[records[E]]
elseif not G then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
e = columns[records[E]]
f = columns[records[F]]
elseif not H then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[C]]
d = columns[records[D]]
e = columns[records[E]]
f = columns[records[F]]
g = columns[records[G]]
elseif H then
a = columns[records[A]]
b = columns[records[B]]
c = columns[records[D]]
e = columns[records[E]]
f = columns[records[F]]
g = columns[records[G]]
h = columns[records[H]]
end
end
local row = i
i-=1
if not F then
return entityId, a[row], b[row], c[row], d[row], e[row]
elseif not G then
return entityId, a[row], b[row], c[row], d[row], e[row], f[row]
elseif not H then
return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row]
elseif not I then
return entityId, a[row], b[row], c[row], d[row], e[row], f[row], g[row], h[row]
end
local field = archetype.records
for j, id in ids do
queryOutput[j] = columns[field[id]][row]
end
return entityId, unpack(queryOutput)
end
end
end
local function world_query_without(self, ...) local function world_query_without(self, ...)
local withoutComponents = { ... } local withoutComponents = { ... }
@ -977,27 +1046,27 @@ do
local columns = archetype.columns local columns = archetype.columns
local tr = archetype.records local tr = archetype.records
for row in archetype.entities do for row in archetype.entities do
if queryLength == 1 then if not B then
local va = columns[tr[A]] local va = columns[tr[A]]
local pa = fn(va[row]) local pa = fn(va[row])
va[row] = pa va[row] = pa
elseif queryLength == 2 then elseif not C then
local va = columns[tr[A]] local va = columns[tr[A]]
local vb = columns[tr[B]] local vb = columns[tr[B]]
va[row], vb[row] = fn(va[row], vb[row]) va[row], vb[row] = fn(va[row], vb[row])
elseif queryLength == 3 then elseif not D then
local va = columns[tr[A]] local va = columns[tr[A]]
local vb = columns[tr[B]] local vb = columns[tr[B]]
local vc = columns[tr[C]] local vc = columns[tr[C]]
va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row]) va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row])
elseif queryLength == 4 then elseif not E then
local va = columns[tr[A]] local va = columns[tr[A]]
local vb = columns[tr[B]] local vb = columns[tr[B]]
local vc = columns[tr[C]] local vc = columns[tr[C]]
local vd = columns[tr[D]] local vd = columns[tr[D]]
va[row], vb[row], vc[row], vd[row] = fn( va[row], vb[row], vc[row], vd[row] = fn(
va[row], vb[row], vc[row], vd[row]) va[row], vb[row], vc[row], vd[row])
@ -1088,7 +1157,8 @@ do
local length = 0 local length = 0
local components = { ... } :: any local components = { ... } :: any
A, B, C, D, E, F, G, H = ... A, B, C, D, E, F, G, H, I = ...
local archetypes = world.archetypes local archetypes = world.archetypes
local firstArchetypeMap: ArchetypeMap local firstArchetypeMap: ArchetypeMap
@ -1131,6 +1201,8 @@ do
init = false init = false
ids = components ids = components
world_query_iter_create()
return it return it
end end
end end

View file

@ -516,8 +516,27 @@ TEST("world:query()", function()
CHECK(count == 2) CHECK(count == 2)
end end
do CASE "despawning while iterating"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local e1 = world:entity()
local e2 = world:entity()
world:add(e1, A)
world:add(e2, A)
world:add(e2, B)
local count = 0
for id in world:query(A) do
world:clear(id)
count += 1
end
CHECK(count == 2)
end
do CASE "iterator invalidation" do CASE "iterator invalidation"
do CASE "adding" do CASE "adding"
SKIP() SKIP()
local world = jecs.World.new() local world = jecs.World.new()
local A = world:component() local A = world:component()