diff --git a/benches/visual/query.bench.luau b/benches/visual/query.bench.luau index 97e27c9..90426f1 100644 --- a/benches/visual/query.bench.luau +++ b/benches/visual/query.bench.luau @@ -170,10 +170,14 @@ return { end, Functions = { - Mirror = function() - for entityId, firstComponent in mcs:query(E1, E2, E3, E4) do + Matter = function() + for entityId, firstComponent in newWorld:query(A1, A2, A3, A4) do end + end, + ECR = function() + for entityId, firstComponent in registry2:view(B1, B2, B3, B3) do + end end, Jecs = function() diff --git a/image-3.png b/image-3.png index 1039142..0324903 100644 Binary files a/image-3.png and b/image-3.png differ diff --git a/image.png b/image.png deleted file mode 100644 index d6497ff..0000000 Binary files a/image.png and /dev/null differ diff --git a/src/init.luau b/src/init.luau index b1cbb1f..3118e53 100644 --- a/src/init.luau +++ b/src/init.luau @@ -750,7 +750,15 @@ do local A, B, C, D, E, F, G, H local a, b, c, d, e, f, g, h + local init + local drain + local function query_init(query) + if init and drain then + return + end + + init = true lastArchetype = 1 archetype = compatible_archetypes[lastArchetype] @@ -767,191 +775,193 @@ do local records = archetype.records if not B then - a = records[A] + a = columns[records[A]] elseif not C then - a = records[A] - b = records[B] + a = columns[records[A]] + b = columns[records[B]] elseif not D then - a = records[A] - b = records[B] - c = records[C] + a = columns[records[A]] + b = columns[records[B]] + c = columns[records[C]] elseif not E then - a = records[A] - b = records[B] - c = records[C] - d = records[D] + a = columns[records[A]] + b = columns[records[B]] + c = columns[records[C]] + d = columns[records[D]] elseif not F then - a = records[A] - b = records[B] - c = records[C] - d = records[D] - e = records[E] + 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 = records[A] - b = records[B] - c = records[C] - d = records[D] - e = records[E] - f = records[F] + 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 = records[A] - b = records[B] - c = records[C] - d = records[D] - e = records[E] - f = records[F] - g = records[G] + 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 = records[A] - b = records[B] - c = records[C] - d = records[D] - e = records[E] - f = records[F] - g = records[G] - h = records[H] + 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 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 + 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 B then + a = columns[records[A]] + 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 + i-=1 + + if queryLength == 1 then + return entityId, a[row] + 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 + for j, id in ids do + queryOutput[j] = columns[field[id]][row] end - 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 - entities = archetype.entities - i = #entities - entityId = entities[i] - end - local row = i - i-=1 - - if queryLength == 1 then - return entityId, columns[a][row] - elseif queryLength == 2 then - return entityId, columns[a][row], columns[b][row] - elseif queryLength == 3 then - return entityId, columns[a][row], columns[b][row], columns[c][row] - elseif queryLength == 4 then - return entityId, columns[a][row], columns[b][row], columns[c][row], columns[d][row] - elseif queryLength == 5 then - return entityId, - columns[a][row], - columns[b][row], - columns[c][row], - columns[d][row], - columns[e][row] - elseif queryLength == 6 then - return entityId, - columns[a][row], - columns[b][row], - columns[c][row], - columns[d][row], - columns[e][row], - columns[f][row] - elseif queryLength == 7 then - return entityId, - columns[a][row], - columns[b][row], - columns[c][row], - columns[d][row], - columns[e][row], - columns[f][row], - columns[g][row] - elseif queryLength == 8 then - return entityId, - columns[a][row], - columns[b][row], - columns[c][row], - columns[d][row], - columns[e][row], - columns[f][row], - columns[g][row], - columns[h][row] - end - - local field = archetype.records - for j, id in ids do - queryOutput[j] = columns[field[id]][row] - end - - return entityId, unpack(queryOutput, 1, queryLength) + return entityId, unpack(queryOutput, 1, queryLength) end local function world_query_without(self, ...) - local withoutComponents = { ... } - for i = #compatible_archetypes, 1, -1 do - local archetype = compatible_archetypes[i] - local records = archetype.records - local shouldRemove = false + local withoutComponents = { ... } + for i = #compatible_archetypes, 1, -1 do + local archetype = compatible_archetypes[i] + local records = archetype.records + local shouldRemove = false - for _, componentId in withoutComponents do - if records[componentId] then - shouldRemove = true - break + for _, componentId in withoutComponents do + if records[componentId] then + shouldRemove = true + break + end + end + + if shouldRemove then + local last = #compatible_archetypes + if last ~= i then + compatible_archetypes[i] = compatible_archetypes[last] + end + compatible_archetypes[last] = nil end - end + end - if shouldRemove then - local last = #compatible_archetypes - if last ~= i then - compatible_archetypes[i] = compatible_archetypes[last] - end - compatible_archetypes[last] = nil - end - end - - return self + return self end local function world_query_replace_values(row, columns, ...) @@ -961,72 +971,74 @@ do end local function world_query_replace(query, fn: (...any) -> (...any)) - query_init(query) + query_init(query) - for i, archetype in compatible_archetypes do - local columns = archetype.columns - local tr = archetype.records - for row in archetype.entities do - if queryLength == 1 then - local va = columns[tr[a]] - local pa = fn(va[row]) + for i, archetype in compatible_archetypes do + local columns = archetype.columns + local tr = archetype.records + for row in archetype.entities do + if queryLength == 1 then + local va = columns[tr[A]] + local pa = fn(va[row]) - va[row] = pa - elseif queryLength == 2 then - local va = columns[tr[a]] - local vb = columns[tr[b]] + va[row] = pa + elseif queryLength == 2 then + local va = columns[tr[A]] + local vb = columns[tr[B]] - va[row], vb[row] = fn(va[row], vb[row]) - elseif queryLength == 3 then - local va = columns[tr[a]] - local vb = columns[tr[b]] - local vc = columns[tr[c]] + va[row], vb[row] = fn(va[row], vb[row]) + elseif queryLength == 3 then + local va = columns[tr[A]] + local vb = columns[tr[B]] + local vc = columns[tr[C]] - va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row]) - elseif queryLength == 4 then - local va = columns[tr[a]] - local vb = columns[tr[b]] - local vc = columns[tr[c]] - local vd = columns[tr[d]] + va[row], vb[row], vc[row] = fn(va[row], vb[row], vc[row]) + elseif queryLength == 4 then + local va = columns[tr[A]] + local vb = columns[tr[B]] + local vc = columns[tr[C]] + local vd = columns[tr[D]] - va[row], vb[row], vc[row], vd[row] = fn( - va[row], vb[row], vc[row], vd[row]) - else - local field = archetype.records - for j, id in ids do - queryOutput[j] = columns[field[id]][row] - end - world_query_replace_values(row, columns, - fn(unpack(queryOutput))) - end + va[row], vb[row], vc[row], vd[row] = fn( + va[row], vb[row], vc[row], vd[row]) + else + local field = archetype.records + for j, id in ids do + queryOutput[j] = columns[field[id]][row] + end + world_query_replace_values(row, columns, + fn(unpack(queryOutput))) + end + end end - end end local function world_query_with(query, ...) - local ids = { ... } - for i = #compatible_archetypes, 1, -1 do - local archetype = compatible_archetypes[i] - local records = archetype.records - local shouldRemove = false + local ids = { ... } + for i = #compatible_archetypes, 1, -1 do + local archetype = compatible_archetypes[i] + local records = archetype.records + local shouldRemove = false - for _, id in ids do - if not records[id] then - shouldRemove = true - break - end - end + for _, id in ids do + if not records[id] then + shouldRemove = true + break + end + end - if shouldRemove then - local last = #compatible_archetypes - if last ~= i then - compatible_archetypes[i] = compatible_archetypes[last] - end - compatible_archetypes[last] = nil - end - end + if shouldRemove then + local last = #compatible_archetypes + if last ~= i then + compatible_archetypes[i] = compatible_archetypes[last] + end + compatible_archetypes[last] = nil + end + end - return query + query_init(query) + + return query end -- Meant for directly iterating over archetypes to minimize @@ -1036,8 +1048,6 @@ do return compatible_archetypes end - local drain - local function world_query_drain(query) drain = true query_init(query) @@ -1045,9 +1055,7 @@ do end local function world_query_iter(query) - if not drain then - query_init(query) - end + query_init(query) return world_query_iter_next end @@ -1058,7 +1066,6 @@ do return world_query_iter_next() end - local it = { __iter = world_query_iter, drain = world_query_drain, @@ -1121,6 +1128,7 @@ do end drain = false + init = false ids = components return it diff --git a/test/tests.luau b/test/tests.luau index 7cb5dbe..a75b472 100644 --- a/test/tests.luau +++ b/test/tests.luau @@ -282,6 +282,32 @@ TEST("world:query()", function() CHECK(counter == 0) end + do CASE "query more than 8 components" + local world = jecs.World.new() + local components = {} + + for i = 1, 9 do + local id = world:component() + components[i] = id + end + local e = world:entity() + for i, id in components do + world:set(e, id, 13^i) + end + + for entity, a, b, c, d, e, f, g, h, i in world:query(unpack(components)) do + CHECK(a == 13^1) + CHECK(b == 13^2) + CHECK(c == 13^3) + CHECK(d == 13^4) + CHECK(e == 13^5) + CHECK(f == 13^6) + CHECK(g == 13^7) + CHECK(h == 13^8) + CHECK(i == 13^9) + end + end + do CASE "should be able to get next results" local world = jecs.World.new() :: World world:component()