mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 10:59:18 +00:00 
			
		
		
		
	Optimize moving archetype
This commit is contained in:
		
							parent
							
								
									7c8358656a
								
							
						
					
					
						commit
						4ff492ceaf
					
				
					 2 changed files with 91 additions and 84 deletions
				
			
		
							
								
								
									
										43
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
					@ -439,6 +439,7 @@ end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function archetype_move(
 | 
					local function archetype_move(
 | 
				
			||||||
	entity_index: EntityIndex,
 | 
						entity_index: EntityIndex,
 | 
				
			||||||
 | 
						entity: Entity,
 | 
				
			||||||
	to: Archetype,
 | 
						to: Archetype,
 | 
				
			||||||
	dst_row: i24,
 | 
						dst_row: i24,
 | 
				
			||||||
	from: Archetype,
 | 
						from: Archetype,
 | 
				
			||||||
| 
						 | 
					@ -452,6 +453,9 @@ local function archetype_move(
 | 
				
			||||||
	local id_types = from.types
 | 
						local id_types = from.types
 | 
				
			||||||
	local columns_map = to.columns_map
 | 
						local columns_map = to.columns_map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if src_row ~= last then
 | 
				
			||||||
 | 
							-- If the entity is the last row in the archetype then swapping it would be meaningless.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for i, column in src_columns do
 | 
							for i, column in src_columns do
 | 
				
			||||||
			if column == NULL_ARRAY then
 | 
								if column == NULL_ARRAY then
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
| 
						 | 
					@ -465,35 +469,42 @@ local function archetype_move(
 | 
				
			||||||
				dst_column[dst_row] = column[src_row]
 | 
									dst_column[dst_row] = column[src_row]
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		-- If the entity is the last row in the archetype then swapping it would be meaningless.
 | 
					 | 
				
			||||||
		if src_row ~= last then
 | 
					 | 
				
			||||||
			-- Swap rempves columns to ensure there are no holes in the archetype.
 | 
								-- Swap rempves columns to ensure there are no holes in the archetype.
 | 
				
			||||||
			column[src_row] = column[last]
 | 
								column[src_row] = column[last]
 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
			column[last] = nil
 | 
								column[last] = nil
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local moved = #src_entities
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		-- Move the entity from the source to the destination archetype.
 | 
							-- Move the entity from the source to the destination archetype.
 | 
				
			||||||
		-- Because we have swapped columns we now have to update the records
 | 
							-- Because we have swapped columns we now have to update the records
 | 
				
			||||||
		-- corresponding to the entities' rows that were swapped.
 | 
							-- corresponding to the entities' rows that were swapped.
 | 
				
			||||||
	local e1 = src_entities[src_row]
 | 
					 | 
				
			||||||
	local e2 = src_entities[moved]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if src_row ~= moved then
 | 
							local e2 = src_entities[last]
 | 
				
			||||||
		src_entities[src_row] = e2
 | 
							src_entities[src_row] = e2
 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	src_entities[moved] = nil :: any
 | 
					 | 
				
			||||||
	dst_entities[dst_row] = e1
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		local sparse_array = entity_index.sparse_array
 | 
							local sparse_array = entity_index.sparse_array
 | 
				
			||||||
 | 
					 | 
				
			||||||
	local record1 = sparse_array[ECS_ENTITY_T_LO(e1 :: number)]
 | 
					 | 
				
			||||||
		local record2 = sparse_array[ECS_ENTITY_T_LO(e2 :: number)]
 | 
							local record2 = sparse_array[ECS_ENTITY_T_LO(e2 :: number)]
 | 
				
			||||||
	record1.row = dst_row
 | 
					 | 
				
			||||||
		record2.row = src_row
 | 
							record2.row = src_row
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							for i, column in src_columns do
 | 
				
			||||||
 | 
								if column == NULL_ARRAY then
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								-- Retrieves the new column index from the source archetype's record from each component
 | 
				
			||||||
 | 
								-- We have to do this because the columns are tightly packed and indexes may not correspond to each other.
 | 
				
			||||||
 | 
								local dst_column = columns_map[id_types[i]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								-- Sometimes target column may not exist, e.g. when you remove a component.
 | 
				
			||||||
 | 
								if dst_column then
 | 
				
			||||||
 | 
									dst_column[dst_row] = column[src_row]
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								column[last] = nil
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						src_entities[last] = nil :: any
 | 
				
			||||||
 | 
						dst_entities[dst_row] = entity
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function archetype_append(
 | 
					local function archetype_append(
 | 
				
			||||||
| 
						 | 
					@ -526,7 +537,7 @@ local function entity_move(
 | 
				
			||||||
	local sourceRow = record.row
 | 
						local sourceRow = record.row
 | 
				
			||||||
	local from = record.archetype
 | 
						local from = record.archetype
 | 
				
			||||||
	local dst_row = archetype_append(entity, to)
 | 
						local dst_row = archetype_append(entity, to)
 | 
				
			||||||
	archetype_move(entity_index, to, dst_row, from, sourceRow)
 | 
						archetype_move(entity_index, entity, to, dst_row, from, sourceRow)
 | 
				
			||||||
	record.archetype = to
 | 
						record.archetype = to
 | 
				
			||||||
	record.row = dst_row
 | 
						record.row = dst_row
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -2459,8 +2470,8 @@ local function world_new()
 | 
				
			||||||
				local idr_archetype = archetypes[archetype_id]
 | 
									local idr_archetype = archetypes[archetype_id]
 | 
				
			||||||
				local entities = idr_archetype.entities
 | 
									local entities = idr_archetype.entities
 | 
				
			||||||
				local n = #entities
 | 
									local n = #entities
 | 
				
			||||||
 | 
									table.move(entities, 1, n, count + 1, queue)
 | 
				
			||||||
				count += n
 | 
									count += n
 | 
				
			||||||
				table.move(entities, 1, n, #queue + 1, queue)
 | 
					 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
			for _, e in queue do
 | 
								for _, e in queue do
 | 
				
			||||||
				inner_world_remove(world, e, entity)
 | 
									inner_world_remove(world, e, entity)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,57 +22,10 @@ type Entity<T=nil> = jecs.Entity<T>
 | 
				
			||||||
type Id<T=unknown> = jecs.Id<T>
 | 
					type Id<T=unknown> = jecs.Id<T>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local entity_visualiser = require("@tools/entity_visualiser")
 | 
					local entity_visualiser = require("@tools/entity_visualiser")
 | 
				
			||||||
local lifetime_tracker_add = require("@tools/lifetime_tracker")
 | 
					 | 
				
			||||||
local dwi = entity_visualiser.stringify
 | 
					local dwi = entity_visualiser.stringify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("repro#", function()
 | 
					TEST("repro", function()
 | 
				
			||||||
	do CASE "pair(OnDelete, Delete)"
 | 
					 | 
				
			||||||
	    local world = jecs.world()
 | 
					 | 
				
			||||||
	    local ct = world:component()
 | 
					 | 
				
			||||||
	    world:add(ct, jecs.pair(jecs.OnDelete, jecs.Delete))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    local e1 = world:entity()
 | 
					 | 
				
			||||||
	    local e2 = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    local dummy = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    world:add(e1, ct)
 | 
					 | 
				
			||||||
	    world:add(e2, jecs.pair(ct, dummy))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    world:delete(dummy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    CHECK(world:contains(e2))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    world:delete(ct)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    CHECK(not world:contains(e1))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	do CASE "pair(OnDeleteTarget, Delete)"
 | 
					 | 
				
			||||||
		print("start")
 | 
					 | 
				
			||||||
	    local world = jecs.world()
 | 
					 | 
				
			||||||
	    local ct = world:component()
 | 
					 | 
				
			||||||
	    world:add(ct, jecs.pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    local e1 = world:entity()
 | 
					 | 
				
			||||||
	    local e2 = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    -- local dummy = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		print("flags")
 | 
					 | 
				
			||||||
	    world:add(e1, ct)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		print(world.component_index[ct].flags)
 | 
					 | 
				
			||||||
	    -- world:add(e2, jecs.pair(ct, dummy))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    -- world:delete(dummy)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    -- CHECK(not world:contains(e2))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    world:delete(ct)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    CHECK(world:contains(e1))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -433,8 +386,51 @@ TEST("world:contains()", function()
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST("world:delete()", function()
 | 
					TEST("world:delete()", function()
 | 
				
			||||||
 | 
						do CASE "pair(OnDelete, Delete)"
 | 
				
			||||||
 | 
						    local world = jecs.world()
 | 
				
			||||||
 | 
						    local ct = world:component()
 | 
				
			||||||
 | 
						    world:add(ct, jecs.pair(jecs.OnDelete, jecs.Delete))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    local e1 = world:entity()
 | 
				
			||||||
 | 
						    local e2 = world:entity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    local dummy = world:entity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:add(e1, ct)
 | 
				
			||||||
 | 
						    world:add(e2, jecs.pair(ct, dummy))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:delete(dummy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    CHECK(world:contains(e2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:delete(ct)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    CHECK(not world:contains(e1))
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						do CASE "pair(OnDeleteTarget, Delete)"
 | 
				
			||||||
 | 
						    local world = jecs.world()
 | 
				
			||||||
 | 
						    local ct = world:component()
 | 
				
			||||||
 | 
						    world:add(ct, jecs.pair(jecs.OnDeleteTarget, jecs.Delete))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    local e1 = world:entity()
 | 
				
			||||||
 | 
						    local e2 = world:entity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    local dummy = world:entity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:add(e1, ct)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:add(e2, jecs.pair(ct, dummy))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:delete(dummy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    CHECK(not world:contains(e2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    world:delete(ct)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    CHECK(world:contains(e1))
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
	do CASE "remove (*, R) pairs when relationship is invalidated"
 | 
						do CASE "remove (*, R) pairs when relationship is invalidated"
 | 
				
			||||||
		print("-------")
 | 
					 | 
				
			||||||
		local world = jecs.world()
 | 
							local world = jecs.world()
 | 
				
			||||||
		local e1 = world:entity()
 | 
							local e1 = world:entity()
 | 
				
			||||||
		local e2 = world:entity()
 | 
							local e2 = world:entity()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue