module: fix ECS alignment issues caught only on Windows
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
10f35a49ef
commit
8578613adc
2 changed files with 21 additions and 33 deletions
|
|
@ -73,18 +73,6 @@ pub fn appendUndefined(storage: *Archetype, gpa: Allocator) !u32 {
|
||||||
return row_index;
|
return row_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: comptime: missing a runtime variant of this function
|
|
||||||
pub fn append(storage: *Archetype, gpa: Allocator, row: anytype) !u32 {
|
|
||||||
debugAssertRowType(storage, row);
|
|
||||||
|
|
||||||
try storage.ensureUnusedCapacity(gpa, 1);
|
|
||||||
assert(storage.len < storage.capacity);
|
|
||||||
storage.len += 1;
|
|
||||||
|
|
||||||
storage.setRow(storage.len - 1, row);
|
|
||||||
return storage.len - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn undoAppend(storage: *Archetype) void {
|
pub fn undoAppend(storage: *Archetype) void {
|
||||||
storage.len -= 1;
|
storage.len -= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -107,6 +95,8 @@ pub fn ensureTotalCapacity(storage: *Archetype, gpa: Allocator, new_capacity: us
|
||||||
return storage.setCapacity(gpa, better_capacity);
|
return storage.setCapacity(gpa, better_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const max_align_padding = 64;
|
||||||
|
|
||||||
/// Sets the capacity to exactly `new_capacity` rows total
|
/// Sets the capacity to exactly `new_capacity` rows total
|
||||||
///
|
///
|
||||||
/// Asserts `new_capacity >= storage.len`, if you want to shrink capacity then change the len
|
/// Asserts `new_capacity >= storage.len`, if you want to shrink capacity then change the len
|
||||||
|
|
@ -117,8 +107,9 @@ pub fn setCapacity(storage: *Archetype, gpa: Allocator, new_capacity: usize) !vo
|
||||||
// TODO: ensure columns are sorted by type_id
|
// TODO: ensure columns are sorted by type_id
|
||||||
for (storage.columns) |*column| {
|
for (storage.columns) |*column| {
|
||||||
const old_values = column.values;
|
const old_values = column.values;
|
||||||
const new_values = try gpa.alloc(u8, new_capacity * column.size);
|
const new_values = try gpa.alloc(u8, (new_capacity * column.size) + max_align_padding);
|
||||||
if (storage.capacity > 0) {
|
if (storage.capacity > 0) {
|
||||||
|
// Note: this copies alignment padding (which is fine, since it is a constant amount.)
|
||||||
@memcpy(new_values[0..old_values.len], old_values);
|
@memcpy(new_values[0..old_values.len], old_values);
|
||||||
gpa.free(old_values);
|
gpa.free(old_values);
|
||||||
}
|
}
|
||||||
|
|
@ -127,22 +118,6 @@ pub fn setCapacity(storage: *Archetype, gpa: Allocator, new_capacity: usize) !vo
|
||||||
storage.capacity = @as(u32, @intCast(new_capacity));
|
storage.capacity = @as(u32, @intCast(new_capacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: comptime: missing a runtime variant of this function
|
|
||||||
/// Sets the entire row's values in the table.
|
|
||||||
pub fn setRow(storage: *Archetype, row_index: u32, row: anytype) void {
|
|
||||||
debugAssertRowType(storage, row);
|
|
||||||
|
|
||||||
const fields = std.meta.fields(@TypeOf(row));
|
|
||||||
inline for (fields, 0..) |field, index| {
|
|
||||||
const ColumnType = field.type;
|
|
||||||
if (@sizeOf(ColumnType) == 0) continue;
|
|
||||||
|
|
||||||
const column = storage.columns[index];
|
|
||||||
const column_values = @as([*]ColumnType, @ptrCast(@alignCast(column.values.ptr)));
|
|
||||||
column_values[row_index] = @field(row, field.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the value of the named components (columns) for the given row in the table.
|
/// Sets the value of the named components (columns) for the given row in the table.
|
||||||
pub fn set(storage: *Archetype, row_index: u32, name: StringTable.Index, component: anytype) void {
|
pub fn set(storage: *Archetype, row_index: u32, name: StringTable.Index, component: anytype) void {
|
||||||
const ColumnType = @TypeOf(component);
|
const ColumnType = @TypeOf(component);
|
||||||
|
|
@ -197,10 +172,11 @@ pub fn remove(storage: *Archetype, row_index: u32) void {
|
||||||
assert(row_index < storage.len);
|
assert(row_index < storage.len);
|
||||||
if (storage.len > 1 and row_index != storage.len - 1) {
|
if (storage.len > 1 and row_index != storage.len - 1) {
|
||||||
for (storage.columns) |column| {
|
for (storage.columns) |column| {
|
||||||
|
const aligned_values = storage.aligned(&column, column.values);
|
||||||
const dstStart = column.size * row_index;
|
const dstStart = column.size * row_index;
|
||||||
const dst = column.values[dstStart .. dstStart + column.size];
|
const dst = aligned_values[dstStart .. dstStart + column.size];
|
||||||
const srcStart = column.size * (storage.len - 1);
|
const srcStart = column.size * (storage.len - 1);
|
||||||
const src = column.values[srcStart .. srcStart + column.size];
|
const src = aligned_values[srcStart .. srcStart + column.size];
|
||||||
@memcpy(dst, src);
|
@memcpy(dst, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +196,17 @@ pub fn hasComponent(storage: *Archetype, name: StringTable.Index) bool {
|
||||||
return storage.columnByName(name) != null;
|
return storage.columnByName(name) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a column.values slice which is unaligned, adds the neccessary padding
|
||||||
|
/// to achieve alignment for the column's data type, and returns the padded slice.
|
||||||
|
inline fn aligned(storage: *Archetype, column: *const Column, values: []u8) []u8 {
|
||||||
|
const aligned_addr = std.mem.alignForward(usize, @intFromPtr(values.ptr), column.alignment);
|
||||||
|
if (is_debug) {
|
||||||
|
const padding_bytes = aligned_addr - @as(usize, @intFromPtr(values.ptr));
|
||||||
|
if (padding_bytes > max_align_padding) @panic("mach: max_align_padding is too low, this is a bug");
|
||||||
|
}
|
||||||
|
return @as([*]u8, @ptrFromInt(aligned_addr))[0 .. storage.capacity * column.size];
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getColumnValues(storage: *Archetype, name: StringTable.Index, comptime ColumnType: type) ?[]ColumnType {
|
pub fn getColumnValues(storage: *Archetype, name: StringTable.Index, comptime ColumnType: type) ?[]ColumnType {
|
||||||
const values = storage.getColumnValuesRaw(name) orelse return null;
|
const values = storage.getColumnValuesRaw(name) orelse return null;
|
||||||
if (is_debug) debugAssertColumnType(storage, storage.columnByName(name).?, ColumnType);
|
if (is_debug) debugAssertColumnType(storage, storage.columnByName(name).?, ColumnType);
|
||||||
|
|
@ -230,7 +217,7 @@ pub fn getColumnValues(storage: *Archetype, name: StringTable.Index, comptime Co
|
||||||
|
|
||||||
pub fn getColumnValuesRaw(storage: *Archetype, name: StringTable.Index) ?[]u8 {
|
pub fn getColumnValuesRaw(storage: *Archetype, name: StringTable.Index) ?[]u8 {
|
||||||
const column = storage.columnByName(name) orelse return null;
|
const column = storage.columnByName(name) orelse return null;
|
||||||
return column.values;
|
return storage.aligned(column, column.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn columnByName(storage: *Archetype, name: StringTable.Index) ?*Column {
|
pub inline fn columnByName(storage: *Archetype, name: StringTable.Index) ?*Column {
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,8 @@ pub fn Database(comptime modules: anytype) type {
|
||||||
assert(archetype_entry.found_existing);
|
assert(archetype_entry.found_existing);
|
||||||
|
|
||||||
var void_archetype = archetype_entry.ptr;
|
var void_archetype = archetype_entry.ptr;
|
||||||
const new_row = try void_archetype.append(entities.allocator, .{ .id = new_id });
|
const new_row = try void_archetype.appendUndefined(entities.allocator);
|
||||||
|
void_archetype.set(new_row, entities.id_name, new_id);
|
||||||
const void_pointer = Pointer{
|
const void_pointer = Pointer{
|
||||||
.archetype_index = 0, // void archetype is guaranteed to be first index
|
.archetype_index = 0, // void archetype is guaranteed to be first index
|
||||||
.row_index = new_row,
|
.row_index = new_row,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue