mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 18:39:19 +00:00 
			
		
		
		
	Remove archetype recycling
This commit is contained in:
		
							parent
							
								
									210d62d463
								
							
						
					
					
						commit
						7b253e1c2a
					
				
					 2 changed files with 113 additions and 138 deletions
				
			
		
							
								
								
									
										248
									
								
								jecs.luau
									
									
									
									
									
								
							
							
						
						
									
										248
									
								
								jecs.luau
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -19,8 +19,7 @@ export type Archetype = {
 | 
			
		|||
	type: string,
 | 
			
		||||
	entities: { Entity },
 | 
			
		||||
	columns: { Column },
 | 
			
		||||
	columns_map: { [Id]: Column },
 | 
			
		||||
	dead: boolean,
 | 
			
		||||
	columns_map: { [Id]: Column }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type QueryInner = {
 | 
			
		||||
| 
						 | 
				
			
			@ -861,8 +860,7 @@ local function archetype_create(world: World, id_types: { Id }, ty, prev: i53?):
 | 
			
		|||
		entities = {},
 | 
			
		||||
		id = archetype_id,
 | 
			
		||||
		type = ty,
 | 
			
		||||
		types = id_types,
 | 
			
		||||
		dead = false,
 | 
			
		||||
		types = id_types
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	archetype_register(world, archetype, false)
 | 
			
		||||
| 
						 | 
				
			
			@ -901,10 +899,6 @@ local function archetype_ensure(world: World, id_types: { Id }): Archetype
 | 
			
		|||
	local ty = hash(id_types)
 | 
			
		||||
	local archetype = world.archetype_index[ty]
 | 
			
		||||
	if archetype then
 | 
			
		||||
		if archetype.dead then
 | 
			
		||||
			archetype_register(world, archetype)
 | 
			
		||||
			archetype.dead = false :: any
 | 
			
		||||
		end
 | 
			
		||||
		return archetype
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1076,14 +1070,16 @@ local function archetype_destroy(world: World, archetype: Archetype)
 | 
			
		|||
	local component_index = world.component_index
 | 
			
		||||
	local archetype_edges = world.archetype_edges
 | 
			
		||||
	local edges = archetype_edges[archetype.id]
 | 
			
		||||
	print("delete archetype id", archetype.id, archetype.type)
 | 
			
		||||
	for id, node in edges do
 | 
			
		||||
		print("node id", node.id)
 | 
			
		||||
		archetype_edges[node.id][id] = nil
 | 
			
		||||
		edges[id] = nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local archetype_id = archetype.id
 | 
			
		||||
	-- world.archetypes[archetype_id] = nil :: any
 | 
			
		||||
	-- world.archetype_index[archetype.type] = nil :: any
 | 
			
		||||
	world.archetypes[archetype_id] = nil :: any
 | 
			
		||||
	world.archetype_index[archetype.type] = nil :: any
 | 
			
		||||
	local columns_map = archetype.columns_map
 | 
			
		||||
 | 
			
		||||
	for id in columns_map do
 | 
			
		||||
| 
						 | 
				
			
			@ -1104,8 +1100,6 @@ local function archetype_destroy(world: World, archetype: Archetype)
 | 
			
		|||
			end
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	archetype.dead = true
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function NOOP() end
 | 
			
		||||
| 
						 | 
				
			
			@ -2307,6 +2301,92 @@ local function world_new()
 | 
			
		|||
		return r
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function inner_world_set<T, a>(world: World, entity: Entity<T>, id: Id<a>, data: a): ()
 | 
			
		||||
		local record = inner_entity_index_try_get_unsafe(entity :: number)
 | 
			
		||||
		if not record then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local from: Archetype = record.archetype
 | 
			
		||||
		local src = from or ROOT_ARCHETYPE
 | 
			
		||||
		local column = src.columns_map[id]
 | 
			
		||||
		if column then
 | 
			
		||||
			local idr = component_index[id]
 | 
			
		||||
			column[record.row] = data
 | 
			
		||||
 | 
			
		||||
			-- If the archetypes are the same it can avoid moving the entity
 | 
			
		||||
			-- and just set the data directly.
 | 
			
		||||
			local on_change = idr.on_change
 | 
			
		||||
			if on_change then
 | 
			
		||||
				on_change(entity, id, data)
 | 
			
		||||
			end
 | 
			
		||||
		else
 | 
			
		||||
			local to: Archetype
 | 
			
		||||
			local idr: ComponentRecord
 | 
			
		||||
			if ECS_IS_PAIR(id::number) then
 | 
			
		||||
				local edge = archetype_edges[src.id]
 | 
			
		||||
				to = edge[id]
 | 
			
		||||
				if not to then
 | 
			
		||||
					local first = ECS_PAIR_FIRST(id::number)
 | 
			
		||||
					local wc = ECS_PAIR(first, EcsWildcard)
 | 
			
		||||
					idr = component_index[wc]
 | 
			
		||||
					if idr and bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
 | 
			
		||||
						local cr = idr.records[src.id]
 | 
			
		||||
						if cr then
 | 
			
		||||
							local on_remove = idr.on_remove
 | 
			
		||||
							local id_types = src.types
 | 
			
		||||
							if on_remove then
 | 
			
		||||
								on_remove(entity, id_types[cr])
 | 
			
		||||
								src = record.archetype
 | 
			
		||||
								id_types = src.types
 | 
			
		||||
								cr = idr.records[src.id]
 | 
			
		||||
							end
 | 
			
		||||
							local dst = table.clone(id_types)
 | 
			
		||||
							dst[cr] = id
 | 
			
		||||
							to = archetype_ensure(world, dst)
 | 
			
		||||
						else
 | 
			
		||||
							to = find_archetype_with(world, id, src)
 | 
			
		||||
							idr = component_index[id]
 | 
			
		||||
						end
 | 
			
		||||
					else
 | 
			
		||||
						to = find_archetype_with(world, id, src)
 | 
			
		||||
						idr = component_index[id]
 | 
			
		||||
					end
 | 
			
		||||
					edge[id] = to
 | 
			
		||||
					archetype_edges[to.id][id] = src
 | 
			
		||||
				else
 | 
			
		||||
					idr = component_index[id]
 | 
			
		||||
				end
 | 
			
		||||
			else
 | 
			
		||||
				local edges = archetype_edges
 | 
			
		||||
				local edge = edges[src.id]
 | 
			
		||||
 | 
			
		||||
				to = edge[id] :: Archetype
 | 
			
		||||
				if not to then
 | 
			
		||||
					to = find_archetype_with(world, id, src)
 | 
			
		||||
					edge[id] = to
 | 
			
		||||
					edges[to.id][id] = src
 | 
			
		||||
				end
 | 
			
		||||
				idr = component_index[id]
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			if from then
 | 
			
		||||
				-- If there was a previous archetype, then the entity needs to move the archetype
 | 
			
		||||
				inner_entity_move(entity_index, entity, record, to)
 | 
			
		||||
			else
 | 
			
		||||
				new_entity(entity, record, to)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			column = to.columns_map[id]
 | 
			
		||||
			column[record.row] = data
 | 
			
		||||
 | 
			
		||||
			local on_add = idr.on_add
 | 
			
		||||
			if on_add then
 | 
			
		||||
				on_add(entity, id, data)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function inner_world_add<T, a>(
 | 
			
		||||
		world: World,
 | 
			
		||||
		entity: Entity<T>,
 | 
			
		||||
| 
						 | 
				
			
			@ -2319,11 +2399,15 @@ local function world_new()
 | 
			
		|||
		end
 | 
			
		||||
 | 
			
		||||
		local from = record.archetype
 | 
			
		||||
		local src = from or ROOT_ARCHETYPE
 | 
			
		||||
		if src.columns_map[id] then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
		local to: Archetype
 | 
			
		||||
		local idr: ComponentRecord
 | 
			
		||||
		if ECS_IS_PAIR(id::number) then
 | 
			
		||||
			local src = from or ROOT_ARCHETYPE
 | 
			
		||||
			local edge = archetype_edges[src.id]
 | 
			
		||||
			local to = edge[id]
 | 
			
		||||
			local idr: ComponentRecord
 | 
			
		||||
			to = edge[id]
 | 
			
		||||
			if not to then
 | 
			
		||||
				local first = ECS_PAIR_FIRST(id::number)
 | 
			
		||||
				local wc = ECS_PAIR(first, EcsWildcard)
 | 
			
		||||
| 
						 | 
				
			
			@ -2351,38 +2435,23 @@ local function world_new()
 | 
			
		|||
					idr = component_index[id]
 | 
			
		||||
				end
 | 
			
		||||
				edge[id] = to
 | 
			
		||||
				archetype_edges[to.id][id] = src
 | 
			
		||||
			else
 | 
			
		||||
				if to.dead then
 | 
			
		||||
					archetype_register(world, to, true)
 | 
			
		||||
					edge[id] = to
 | 
			
		||||
					archetype_edges[to.id][id] = src
 | 
			
		||||
					to.dead = false
 | 
			
		||||
				end
 | 
			
		||||
				idr = component_index[id]
 | 
			
		||||
			end
 | 
			
		||||
			if from == to then
 | 
			
		||||
				return
 | 
			
		||||
			end
 | 
			
		||||
			if from then
 | 
			
		||||
				inner_entity_move(entity_index, entity, record, to)
 | 
			
		||||
			else
 | 
			
		||||
				if #to.types > 0 then
 | 
			
		||||
					new_entity(entity, record, to)
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		else
 | 
			
		||||
			local edges = archetype_edges
 | 
			
		||||
			local edge = edges[src.id]
 | 
			
		||||
 | 
			
		||||
			local on_add = idr.on_add
 | 
			
		||||
 | 
			
		||||
			if on_add then
 | 
			
		||||
				on_add(entity, id)
 | 
			
		||||
			to = edge[id] :: Archetype
 | 
			
		||||
			if not to then
 | 
			
		||||
				to = find_archetype_with(world, id, src)
 | 
			
		||||
				edge[id] = to
 | 
			
		||||
				edges[to.id][id] = src
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
		local to = archetype_traverse_add(world, id, from)
 | 
			
		||||
		if from == to then
 | 
			
		||||
			return
 | 
			
		||||
			idr = component_index[id]
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		if from then
 | 
			
		||||
			inner_entity_move(entity_index, entity, record, to)
 | 
			
		||||
		else
 | 
			
		||||
| 
						 | 
				
			
			@ -2391,7 +2460,6 @@ local function world_new()
 | 
			
		|||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local idr = component_index[id]
 | 
			
		||||
		local on_add = idr.on_add
 | 
			
		||||
 | 
			
		||||
		if on_add then
 | 
			
		||||
| 
						 | 
				
			
			@ -2495,102 +2563,6 @@ local function world_new()
 | 
			
		|||
		return inner_world_target(world, entity, EcsChildOf, 0)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function inner_archetype_traverse_add(id: Id, from: Archetype): Archetype
 | 
			
		||||
		from = from or ROOT_ARCHETYPE
 | 
			
		||||
		if from.columns_map[id] then
 | 
			
		||||
			return from
 | 
			
		||||
		end
 | 
			
		||||
		local edges = archetype_edges
 | 
			
		||||
		local edge = edges[from.id]
 | 
			
		||||
 | 
			
		||||
		local to = edge[id] :: Archetype
 | 
			
		||||
		if not to then
 | 
			
		||||
			to = find_archetype_with(world, id, from)
 | 
			
		||||
			edge[id] = to
 | 
			
		||||
			edges[to.id][id] = from
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		return to
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function inner_world_set<T, a>(world: World, entity: Entity<T>, id: Id<a>, data: a): ()
 | 
			
		||||
		local record = inner_entity_index_try_get_unsafe(entity :: number)
 | 
			
		||||
		if not record then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
		local from: Archetype = record.archetype
 | 
			
		||||
		local src = from or ROOT_ARCHETYPE
 | 
			
		||||
		local column = src.columns_map[id]
 | 
			
		||||
		if column then
 | 
			
		||||
			local idr = component_index[id]
 | 
			
		||||
			column[record.row] = data
 | 
			
		||||
 | 
			
		||||
			-- If the archetypes are the same it can avoid moving the entity
 | 
			
		||||
			-- and just set the data directly.
 | 
			
		||||
			local on_change = idr.on_change
 | 
			
		||||
			if on_change then
 | 
			
		||||
				on_change(entity, id, data)
 | 
			
		||||
			end
 | 
			
		||||
		else
 | 
			
		||||
			local to: Archetype
 | 
			
		||||
			local idr: ComponentRecord
 | 
			
		||||
			if ECS_IS_PAIR(id::number) then
 | 
			
		||||
				local edge = archetype_edges[src.id]
 | 
			
		||||
				to = edge[id]
 | 
			
		||||
				if not to then
 | 
			
		||||
					local first = ECS_PAIR_FIRST(id::number)
 | 
			
		||||
					local wc = ECS_PAIR(first, EcsWildcard)
 | 
			
		||||
					idr = component_index[wc]
 | 
			
		||||
					if idr and bit32.btest(idr.flags, ECS_ID_IS_EXCLUSIVE) then
 | 
			
		||||
						local cr = idr.records[src.id]
 | 
			
		||||
						if cr then
 | 
			
		||||
							local on_remove = idr.on_remove
 | 
			
		||||
							local id_types = src.types
 | 
			
		||||
							if on_remove then
 | 
			
		||||
								on_remove(entity, id_types[cr])
 | 
			
		||||
								src = record.archetype
 | 
			
		||||
								id_types = src.types
 | 
			
		||||
								cr = idr.records[src.id]
 | 
			
		||||
							end
 | 
			
		||||
							local dst = table.clone(id_types)
 | 
			
		||||
							dst[cr] = id
 | 
			
		||||
							to = archetype_ensure(world, dst)
 | 
			
		||||
						else
 | 
			
		||||
							to = find_archetype_with(world, id, src)
 | 
			
		||||
							idr = component_index[id]
 | 
			
		||||
						end
 | 
			
		||||
					else
 | 
			
		||||
						to = find_archetype_with(world, id, src)
 | 
			
		||||
						idr = component_index[id]
 | 
			
		||||
					end
 | 
			
		||||
					edge[id] = to
 | 
			
		||||
					archetype_edges[to.id][id] = src
 | 
			
		||||
				else
 | 
			
		||||
					idr = component_index[id]
 | 
			
		||||
				end
 | 
			
		||||
			else
 | 
			
		||||
				to = inner_archetype_traverse_add(id, from)
 | 
			
		||||
				idr = component_index[id]
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			if from then
 | 
			
		||||
				-- If there was a previous archetype, then the entity needs to move the archetype
 | 
			
		||||
				inner_entity_move(entity_index, entity, record, to)
 | 
			
		||||
			else
 | 
			
		||||
				new_entity(entity, record, to)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
			column = to.columns_map[id]
 | 
			
		||||
			column[record.row] = data
 | 
			
		||||
 | 
			
		||||
			local on_add = idr.on_add
 | 
			
		||||
			if on_add then
 | 
			
		||||
				on_add(entity, id, data)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	local function inner_world_entity<T>(world: World, entity: Entity<T>?): Entity<T>
 | 
			
		||||
		if entity then
 | 
			
		||||
			local index = ECS_ID(entity :: number)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -129,6 +129,7 @@ TEST("repeated pairs", function()
 | 
			
		|||
	CHECK(count == 1)
 | 
			
		||||
	CHECK(world:each(p2)() == e2) -- Fails
 | 
			
		||||
end)
 | 
			
		||||
 | 
			
		||||
TEST("repro", function()
 | 
			
		||||
	local world = jecs.world()
 | 
			
		||||
	local data = world:component()
 | 
			
		||||
| 
						 | 
				
			
			@ -157,6 +158,7 @@ TEST("repro", function()
 | 
			
		|||
	end
 | 
			
		||||
	CHECK(count == 1)
 | 
			
		||||
	count = 0
 | 
			
		||||
	print("----")
 | 
			
		||||
	world:add(e2v1, jecs.pair(relation, e1v1))
 | 
			
		||||
	CHECK(world:has(e2v1, jecs.pair(relation, e1v1)))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -164,6 +166,7 @@ TEST("repro", function()
 | 
			
		|||
		count += 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
	print(count)
 | 
			
		||||
	CHECK(count==1)
 | 
			
		||||
end)
 | 
			
		||||
TEST("bulk", function()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue