Use a SparseSet in BlockEntity data, instead of manually managing a dense list. (#1470)

fixes #1453
This commit is contained in:
IntegratedQuantum 2025-05-20 20:37:41 +02:00 committed by GitHub
parent 2e705d9cc9
commit a4809555e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 41 deletions

View File

@ -1,7 +1,6 @@
const std = @import("std"); const std = @import("std");
const main = @import("main.zig"); const main = @import("main.zig");
const List = main.List;
const Vec3i = main.vec.Vec3i; const Vec3i = main.vec.Vec3i;
const Block = main.blocks.Block; const Block = main.blocks.Block;
const Chunk = main.chunk.Chunk; const Chunk = main.chunk.Chunk;
@ -11,7 +10,7 @@ const server = main.server;
const User = server.User; const User = server.User;
const mesh_storage = main.renderer.mesh_storage; const mesh_storage = main.renderer.mesh_storage;
pub const BlockEntityIndex = u32; pub const BlockEntityIndex = main.utils.DenseId(u32);
pub const BlockEntityType = struct { pub const BlockEntityType = struct {
id: []const u8, id: []const u8,
@ -77,31 +76,40 @@ pub const EventStatus = enum {
ignored, ignored,
}; };
fn BlockEntityDataStorage(comptime side: enum {client, server}, T: type) type { fn BlockEntityDataStorage(T: type) type {
return struct { return struct {
pub const DataT = T; pub const DataT = T;
pub const EntryT = struct { pub const EntryT = struct {
absoluteBlockPosition: Vec3i, absoluteBlockPosition: Vec3i,
data: DataT, data: DataT,
}; };
var storage: List(EntryT) = undefined; var freeIndexList: main.ListUnmanaged(BlockEntityIndex) = .{};
var nextIndex: BlockEntityIndex = @enumFromInt(0);
var storage: main.utils.SparseSet(EntryT, BlockEntityIndex) = .{};
pub var mutex: std.Thread.Mutex = .{}; pub var mutex: std.Thread.Mutex = .{};
pub fn init() void { pub fn init() void {
storage = .init(main.globalAllocator); storage = .{};
freeIndexList = .{};
} }
pub fn deinit() void { pub fn deinit() void {
storage.deinit(); storage.deinit(main.globalAllocator);
freeIndexList.deinit(main.globalAllocator);
nextIndex = @enumFromInt(0);
} }
pub fn reset() void { pub fn reset() void {
storage.clearRetainingCapacity(); storage.clear();
freeIndexList.clearRetainingCapacity();
} }
pub fn add(pos: Vec3i, value: DataT, chunk: *Chunk) void { pub fn add(pos: Vec3i, value: DataT, chunk: *Chunk) void {
mutex.lock(); mutex.lock();
defer mutex.unlock(); defer mutex.unlock();
const dataIndex = storage.items.len; const dataIndex: BlockEntityIndex = freeIndexList.popOrNull() orelse blk: {
storage.append(.{.absoluteBlockPosition = pos, .data = value}); defer nextIndex = @enumFromInt(@intFromEnum(nextIndex) + 1);
break :blk nextIndex;
};
storage.set(main.globalAllocator, dataIndex, value);
const blockIndex = chunk.getLocalBlockIndex(pos); const blockIndex = chunk.getLocalBlockIndex(pos);
@ -120,41 +128,15 @@ fn BlockEntityDataStorage(comptime side: enum {client, server}, T: type) type {
chunk.blockPosToEntityDataMapMutex.unlock(); chunk.blockPosToEntityDataMapMutex.unlock();
const entry = entityNullable orelse { const entry = entityNullable orelse {
std.log.warn("Couldn't remove entity data of block at position {}", .{pos}); std.log.err("Couldn't remove entity data of block at position {}", .{pos});
return; return;
}; };
const dataIndex = entry.value; const dataIndex = entry.value;
_ = storage.swapRemove(dataIndex); freeIndexList.append(main.globalAllocator, dataIndex);
if(dataIndex == storage.items.len) { storage.remove(dataIndex) catch |err| {
return; std.log.err("Error while remvoing block entity at position {}: {s}", .{pos, @errorName(err)});
} };
const movedEntry = storage.items[dataIndex];
switch(side) {
.server => propagateRemoveServer(movedEntry.absoluteBlockPosition, dataIndex),
.client => propagateRemoveClient(movedEntry.absoluteBlockPosition, dataIndex),
}
}
fn propagateRemoveServer(pos: Vec3i, index: BlockEntityIndex) void {
const severChunk = server.world.?.getChunkFromCacheAndIncreaseRefCount(ChunkPosition.initFromWorldPos(pos, 1)).?;
defer severChunk.decreaseRefCount();
severChunk.super.blockPosToEntityDataMapMutex.lock();
defer severChunk.super.blockPosToEntityDataMapMutex.unlock();
const otherDataIndex = severChunk.super.getLocalBlockIndex(pos);
severChunk.super.blockPosToEntityDataMap.put(main.globalAllocator.allocator, otherDataIndex, index) catch unreachable;
}
fn propagateRemoveClient(pos: Vec3i, index: BlockEntityIndex) void {
const mesh = mesh_storage.getMeshAndIncreaseRefCount(ChunkPosition.initFromWorldPos(pos, 1)).?;
defer mesh.decreaseRefCount();
mesh.chunk.blockPosToEntityDataMapMutex.lock();
defer mesh.chunk.blockPosToEntityDataMapMutex.unlock();
const otherDataIndex = mesh.chunk.getLocalBlockIndex(pos);
mesh.chunk.blockPosToEntityDataMap.put(main.globalAllocator.allocator, otherDataIndex, index) catch unreachable;
} }
pub fn get(pos: Vec3i, chunk: *Chunk) ?*DataT { pub fn get(pos: Vec3i, chunk: *Chunk) ?*DataT {
main.utils.assertLocked(&mutex); main.utils.assertLocked(&mutex);
@ -176,7 +158,6 @@ fn BlockEntityDataStorage(comptime side: enum {client, server}, T: type) type {
pub const BlockEntityTypes = struct { pub const BlockEntityTypes = struct {
pub const Chest = struct { pub const Chest = struct {
const StorageServer = BlockEntityDataStorage( const StorageServer = BlockEntityDataStorage(
.server,
struct { struct {
id: ?u32, id: ?u32,
}, },

View File

@ -1927,6 +1927,12 @@ pub fn SparseSet(comptime T: type, comptime IdType: type) type { // MARK: Sparse
denseToSparseIndex: main.ListUnmanaged(IdType) = .{}, denseToSparseIndex: main.ListUnmanaged(IdType) = .{},
sparseToDenseIndex: main.ListUnmanaged(IdType) = .{}, sparseToDenseIndex: main.ListUnmanaged(IdType) = .{},
pub fn clear(self: *Self) void {
self.dense.clearRetainingCapacity();
self.denseToSparseIndex.clearRetainingCapacity();
self.sparseToDenseIndex.clearRetainingCapacity();
}
pub fn deinit(self: *Self, allocator: NeverFailingAllocator) void { pub fn deinit(self: *Self, allocator: NeverFailingAllocator) void {
self.dense.deinit(allocator); self.dense.deinit(allocator);
self.denseToSparseIndex.deinit(allocator); self.denseToSparseIndex.deinit(allocator);