progress on sort

master
Mike 5 years ago
parent e098c99632
commit 34b2702333

@ -28,7 +28,7 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
/// doesnt really belong here...used to denote group ownership /// doesnt really belong here...used to denote group ownership
super: usize = 0, super: usize = 0,
safe_deinit: fn (*Self) void, safe_deinit: fn (*Self) void,
safe_swap: fn (*Self, Entity, Entity) void, safe_swap: fn (*Self, Entity, Entity, bool) void,
construction: Signal(Entity), construction: Signal(Entity),
update: Signal(Entity), update: Signal(Entity),
destruction: Signal(Entity), destruction: Signal(Entity),
@ -45,11 +45,11 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
} }
}.deinit, }.deinit,
.safe_swap = struct { .safe_swap = struct {
fn swap(self: *Self, lhs: Entity, rhs: Entity) void { fn swap(self: *Self, lhs: Entity, rhs: Entity, instances_only: bool) void {
if (!is_empty_struct) { if (!is_empty_struct) {
std.mem.swap(Component, &self.instances.items[self.set.index(lhs)], &self.instances.items[self.set.index(rhs)]); std.mem.swap(Component, &self.instances.items[self.set.index(lhs)], &self.instances.items[self.set.index(rhs)]);
} }
self.set.swap(lhs, rhs); if (!instances_only) self.set.swap(lhs, rhs);
} }
}.swap, }.swap,
.allocator = null, .allocator = null,
@ -87,11 +87,11 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
}.deinit; }.deinit;
store.safe_swap = struct { store.safe_swap = struct {
fn swap(self: *Self, lhs: Entity, rhs: Entity) void { fn swap(self: *Self, lhs: Entity, rhs: Entity, instances_only: bool) void {
if (!is_empty_struct) { if (!is_empty_struct) {
std.mem.swap(Component, &self.instances.items[self.set.index(lhs)], &self.instances.items[self.set.index(rhs)]); std.mem.swap(Component, &self.instances.items[self.set.index(lhs)], &self.instances.items[self.set.index(rhs)]);
} }
self.set.swap(lhs, rhs); if (!instances_only) self.set.swap(lhs, rhs);
} }
}.swap; }.swap;
@ -201,11 +201,25 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
return if (self.set.contains(entity)) self.instances.items[self.set.index(entity)] else null; return if (self.set.contains(entity)) self.instances.items[self.set.index(entity)] else null;
} }
/// Sort Entities or Components according to the given comparison function /// Sort Entities or Components according to the given comparison function. Valid types for T are Entity or Component.
pub fn sort(self: Self, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void { pub fn sort(self: *Self, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
std.debug.assert(T == Entity or T == Component); std.debug.assert(T == Entity or T == Component);
if (T == Entity) { if (T == Entity) {
self.set.sortSub(context, lessThan, Component, self.instances.items); // wtf? When an OwningGroup calls us we are gonna be fake-typed and if we are fake-typed its not safe to pass our slice to
// the SparseSet and let it handle sorting. Instead, we'll use swap _without a set swap_ and do it ourselves.
if (Component == u1) {
const SortContext = struct {
storage: *Self,
pub fn swap(this: @This(), a: Entity, b: Entity) void {
this.storage.safe_swap(this.storage, a, b, true);
}
};
const swap_context = SortContext{.storage = self};
self.set.sortSwap(context, lessThan, swap_context);
} else {
self.set.sortSub(context, lessThan, Component, self.instances.items);
}
} else if (T == Component) { } else if (T == Component) {
self.set.sortSubSub(context, Component, lessThan, self.instances.items); self.set.sortSubSub(context, Component, lessThan, self.instances.items);
} }
@ -224,7 +238,7 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
/// Swaps entities and objects in the internal packed arrays /// Swaps entities and objects in the internal packed arrays
pub fn swap(self: *Self, lhs: Entity, rhs: Entity) void { pub fn swap(self: *Self, lhs: Entity, rhs: Entity) void {
self.safe_swap(self, lhs, rhs); self.safe_swap(self, lhs, rhs, false);
} }
pub fn clear(self: *Self) void { pub fn clear(self: *Self) void {
@ -361,7 +375,7 @@ test "sort by entity" {
store.add(11, @as(f32, 1.1)); store.add(11, @as(f32, 1.1));
store.add(33, @as(f32, 3.3)); store.add(33, @as(f32, 3.3));
const SortContext = struct{ const SortContext = struct {
store: *ComponentStorage(f32, u32), store: *ComponentStorage(f32, u32),
fn sort(this: @This(), a: u32, b: u32) bool { fn sort(this: @This(), a: u32, b: u32) bool {
@ -370,7 +384,7 @@ test "sort by entity" {
return real_a > real_b; return real_a > real_b;
} }
}; };
const context = SortContext{.store = store}; const context = SortContext{ .store = store };
store.sort(u32, context, SortContext.sort); store.sort(u32, context, SortContext.sort);
var compare: f32 = 5; var compare: f32 = 5;

@ -28,11 +28,11 @@ pub const BasicGroup = struct {
return self.group_data.entity_set.data(); return self.group_data.entity_set.data();
} }
pub fn get(self: *BasicGroup, comptime T: type, entity: Entity) *T { pub fn get(self: BasicGroup, comptime T: type, entity: Entity) *T {
return self.registry.assure(T).get(entity); return self.registry.assure(T).get(entity);
} }
pub fn getConst(self: *BasicGroup, comptime T: type, entity: Entity) T { pub fn getConst(self: BasicGroup, comptime T: type, entity: Entity) T {
return self.registry.assure(T).getConst(entity); return self.registry.assure(T).getConst(entity);
} }
@ -42,13 +42,13 @@ pub const BasicGroup = struct {
return self.group_data.entity_set.reverseIterator(); return self.group_data.entity_set.reverseIterator();
} }
pub fn sort(self: *BasicGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void { pub fn sort(self: BasicGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
if (T == Entity) { if (T == Entity) {
self.group_data.entity_set.sort(context, lessThan); self.group_data.entity_set.sort(context, lessThan);
} else { } else {
// TODO: in debug mode, validate that T is present in the group // TODO: in debug mode, validate that T is present in the group
const SortContext = struct{ const SortContext = struct {
group: *BasicGroup, group: BasicGroup,
wrapped_context: @TypeOf(context), wrapped_context: @TypeOf(context),
lessThan: fn (@TypeOf(context), T, T) bool, lessThan: fn (@TypeOf(context), T, T) bool,
@ -58,7 +58,7 @@ pub const BasicGroup = struct {
return this.lessThan(this.wrapped_context, real_a, real_b); return this.lessThan(this.wrapped_context, real_a, real_b);
} }
}; };
var wrapper = SortContext{.group = self, .wrapped_context = context, .lessThan = lessThan}; var wrapper = SortContext{ .group = self, .wrapped_context = context, .lessThan = lessThan };
self.group_data.entity_set.sort(wrapper, SortContext.sort); self.group_data.entity_set.sort(wrapper, SortContext.sort);
} }
} }
@ -72,6 +72,7 @@ pub const OwningGroup = struct {
/// iterator the provides the data from all the requested owned components in a single struct. Access to the current Entity /// iterator the provides the data from all the requested owned components in a single struct. Access to the current Entity
/// being iterated is available via the entity() method, useful for accessing non-owned component data. The get() method can /// being iterated is available via the entity() method, useful for accessing non-owned component data. The get() method can
/// also be used to fetch non-owned component data for the currently iterated Entity. /// also be used to fetch non-owned component data for the currently iterated Entity.
/// TODO: support const types in the Components struct in addition to the current ptrs
fn Iterator(comptime Components: var) type { fn Iterator(comptime Components: var) type {
return struct { return struct {
group: OwningGroup, group: OwningGroup,
@ -233,15 +234,15 @@ pub const OwningGroup = struct {
} }
/// returns the component storage for the given type for direct access /// returns the component storage for the given type for direct access
pub fn getStorage(self: *OwningGroup, comptime T: type) *Storage(T) { pub fn getStorage(self: OwningGroup, comptime T: type) *Storage(T) {
return self.registry.assure(T); return self.registry.assure(T);
} }
pub fn get(self: *OwningGroup, comptime T: type, entity: Entity) *T { pub fn get(self: OwningGroup, comptime T: type, entity: Entity) *T {
return self.registry.assure(T).get(entity); return self.registry.assure(T).get(entity);
} }
pub fn getConst(self: *OwningGroup, comptime T: type, entity: Entity) T { pub fn getConst(self: OwningGroup, comptime T: type, entity: Entity) T {
return self.registry.assure(T).getConst(entity); return self.registry.assure(T).getConst(entity);
} }
@ -250,7 +251,8 @@ pub const OwningGroup = struct {
} }
/// returns an iterator with optimized access to the owend Components. Note that Components should be a struct with /// returns an iterator with optimized access to the owend Components. Note that Components should be a struct with
/// fields that are pointers to the component types that you want to fetch. Only types that are owned are valid! /// fields that are pointers to the component types that you want to fetch. Only types that are owned are valid! Non-owned
/// types should be fetched via Iterator.get.
pub fn iterator(self: OwningGroup, comptime Components: var) Iterator(Components) { pub fn iterator(self: OwningGroup, comptime Components: var) Iterator(Components) {
self.validate(Components); self.validate(Components);
return Iterator(Components).init(self); return Iterator(Components).init(self);
@ -259,6 +261,64 @@ pub const OwningGroup = struct {
pub fn entityIterator(self: OwningGroup) utils.ReverseSliceIterator(Entity) { pub fn entityIterator(self: OwningGroup) utils.ReverseSliceIterator(Entity) {
return utils.ReverseSliceIterator(Entity).init(self.firstOwnedStorage().set.dense.items[0..self.group_data.current]); return utils.ReverseSliceIterator(Entity).init(self.firstOwnedStorage().set.dense.items[0..self.group_data.current]);
} }
pub fn sort(self: OwningGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
var first_storage = self.firstOwnedStorage();
if (T == Entity) {
// only sort up to self.group_data.current
first_storage.sort(Entity, context, lessThan);
} else {
// TODO: in debug mode, validate that T is present in the group
const SortContext = struct {
group: OwningGroup,
wrapped_context: @TypeOf(context),
lessThan: fn (@TypeOf(context), T, T) bool,
fn sort(this: @This(), a: Entity, b: Entity) bool {
const real_a = this.group.getConst(T, a);
const real_b = this.group.getConst(T, b);
return this.lessThan(this.wrapped_context, real_a, real_b);
}
};
const wrapper = SortContext{ .group = self, .wrapped_context = context, .lessThan = lessThan };
first_storage.sort(Entity, wrapper, SortContext.sort);
}
// sync up the rest of the owned components. First get our Storages in
// var tmp_storages: [20]*Storage(u1) = undefined;
// for (self.group_data.owned[1..]) |type_id, i| {
// var other_ptr = self.registry.components.getValue(type_id).?;
// tmp_storages[i] = @intToPtr(*Storage(u1), other_ptr);
// }
// var storages = tmp_storages[0 .. self.group_data.owned.len - 1];
var next: usize = self.group_data.current;
while (true) : (next -= 1) {
if (next == 0) break;
const pos = next - 1;
const entity = first_storage.data()[pos];
// skip the first one since its what we are using to sort with
for (self.group_data.owned[1..]) |type_id| {
var other_ptr = self.registry.components.getValue(type_id).?;
var storage = @intToPtr(*Storage(u1), other_ptr);
storage.swap(storage.data()[pos], entity);
}
}
// for (self.group_data.owned[1..]) |type_id| {
// var other_ptr = self.registry.components.getValue(type_id).?;
// var other = @intToPtr(*Storage(u1), other_ptr);
// var i: usize = self.group_data.current - 1;
// while (true) : (i -= 1) {
// if (i == 0) break;
// const pos = i - 1;
// const entity =
// }
// }
}
}; };
test "BasicGroup creation/iteration" { test "BasicGroup creation/iteration" {

@ -101,8 +101,9 @@ pub const Registry = struct {
}; };
if (self.owned.len == 0) { if (self.owned.len == 0) {
if (isValid and !self.entity_set.contains(entity)) if (isValid and !self.entity_set.contains(entity)) {
self.entity_set.add(entity); self.entity_set.add(entity);
}
} else { } else {
if (isValid) { if (isValid) {
const ptr = self.registry.components.getValue(self.owned[0]).?; const ptr = self.registry.components.getValue(self.owned[0]).?;
@ -122,8 +123,9 @@ pub const Registry = struct {
pub fn discardIf(self: *GroupData, entity: Entity) void { pub fn discardIf(self: *GroupData, entity: Entity) void {
if (self.owned.len == 0) { if (self.owned.len == 0) {
if (self.entity_set.contains(entity)) if (self.entity_set.contains(entity)) {
self.entity_set.remove(entity); self.entity_set.remove(entity);
}
} else { } else {
const ptr = self.registry.components.getValue(self.owned[0]).?; const ptr = self.registry.components.getValue(self.owned[0]).?;
var store = @intToPtr(*Storage(u1), ptr); var store = @intToPtr(*Storage(u1), ptr);

@ -170,6 +170,24 @@ pub fn SparseSet(comptime SparseT: type) type {
} }
} }
/// Sort elements according to the given comparison function and keeps sub_items with the same sort
pub fn sortSwap(self: *Self, context: var, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool, swap_context: var) void {
std.sort.insertionSort(SparseT, self.dense.items, context, lessThan);
for (self.dense.items) |sparse, pos| {
var curr = @intCast(SparseT, pos);
var next = self.index(self.dense.items[curr]);
while (curr != next) {
swap_context.swap(self.dense.items[curr], self.dense.items[next]);
self.sparse.items[self.dense.items[self.page(@intCast(SparseT, curr))]] = @intCast(SparseT, curr);
curr = next;
next = self.index(self.dense.items[curr]);
}
}
}
/// flips the script and uses the sparse set as the subordinate and does the sorting on the items slice /// flips the script and uses the sparse set as the subordinate and does the sorting on the items slice
pub fn sortSubSub(self: *Self, context: var, comptime T: type, comptime lessThan: fn (@TypeOf(context), T, T) bool, items: []T) void { pub fn sortSubSub(self: *Self, context: var, comptime T: type, comptime lessThan: fn (@TypeOf(context), T, T) bool, items: []T) void {
utils.sortSubSub(T, SparseT, items, self.dense.items, context, lessThan); utils.sortSubSub(T, SparseT, items, self.dense.items, context, lessThan);

@ -25,7 +25,7 @@ test "sort BasicGroup by Entity" {
var reg = Registry.init(std.testing.allocator); var reg = Registry.init(std.testing.allocator);
defer reg.deinit(); defer reg.deinit();
var group = reg.group(.{}, .{Sprite, Renderable}, .{}); var group = reg.group(.{}, .{ Sprite, Renderable }, .{});
var i: usize = 0; var i: usize = 0;
while (i < 5) : (i += 1) { while (i < 5) : (i += 1) {
@ -34,7 +34,7 @@ test "sort BasicGroup by Entity" {
reg.add(e, Renderable{ .x = @intToFloat(f32, i) }); reg.add(e, Renderable{ .x = @intToFloat(f32, i) });
} }
const SortContext = struct{ const SortContext = struct {
group: BasicGroup, group: BasicGroup,
fn sort(this: *@This(), a: ecs.Entity, b: ecs.Entity) bool { fn sort(this: *@This(), a: ecs.Entity, b: ecs.Entity) bool {
@ -44,7 +44,7 @@ test "sort BasicGroup by Entity" {
} }
}; };
var context = SortContext{.group = group}; var context = SortContext{ .group = group };
group.sort(ecs.Entity, &context, SortContext.sort); group.sort(ecs.Entity, &context, SortContext.sort);
var val: f32 = 0; var val: f32 = 0;
@ -59,7 +59,7 @@ test "sort BasicGroup by Component" {
var reg = Registry.init(std.testing.allocator); var reg = Registry.init(std.testing.allocator);
defer reg.deinit(); defer reg.deinit();
var group = reg.group(.{}, .{Sprite, Renderable}, .{}); var group = reg.group(.{}, .{ Sprite, Renderable }, .{});
var i: usize = 0; var i: usize = 0;
while (i < 5) : (i += 1) { while (i < 5) : (i += 1) {
@ -68,7 +68,7 @@ test "sort BasicGroup by Component" {
reg.add(e, Renderable{ .x = @intToFloat(f32, i) }); reg.add(e, Renderable{ .x = @intToFloat(f32, i) });
} }
const SortContext = struct{ const SortContext = struct {
fn sort(this: void, a: Sprite, b: Sprite) bool { fn sort(this: void, a: Sprite, b: Sprite) bool {
return a.x > b.x; return a.x > b.x;
} }
@ -83,9 +83,34 @@ test "sort BasicGroup by Component" {
} }
} }
test "sort OwningGroup by Entity" { test "sort OwningGroup by Component" {
std.debug.warn("\n", .{});
var reg = Registry.init(std.testing.allocator); var reg = Registry.init(std.testing.allocator);
defer reg.deinit(); defer reg.deinit();
var group = reg.group(.{ Sprite, Renderable }, .{}, .{});
var i: usize = 0;
while (i < 5) : (i += 1) {
var e = reg.create();
reg.add(e, Sprite{ .x = @intToFloat(f32, i) });
reg.add(e, Renderable{ .x = @intToFloat(f32, i) });
}
const SortContext = struct {
fn sort(this: void, a: Sprite, b: Sprite) bool {
return a.x > b.x;
}
};
group.sort(Sprite, {}, SortContext.sort);
var val: f32 = 0;
var iter = group.iterator(struct {s: *Sprite, r: *Renderable});
while (iter.next()) |entity| {
std.debug.warn("e{}: {d}\n", .{iter.entity(), entity});
std.testing.expectEqual(val, entity.s.*.x);
val += 1;
}
} }
test "nested OwningGroups add/remove components" { test "nested OwningGroups add/remove components" {

Loading…
Cancel
Save