From ab691cec7327dca60a2b3ead3fbf43fbdcd57c47 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Wed, 23 Jul 2025 22:29:50 +0200 Subject: [PATCH] Use GarbageCollection for LightMapFragments --- src/network.zig | 1 - src/renderer/chunk_meshing.zig | 3 +-- src/renderer/mesh_storage.zig | 47 ++++++++++++--------------------- src/server/terrain/LightMap.zig | 22 +++++---------- src/server/world.zig | 3 +-- 5 files changed, 26 insertions(+), 50 deletions(-) diff --git a/src/network.zig b/src/network.zig index 142dfb07..0bbb1517 100644 --- a/src/network.zig +++ b/src/network.zig @@ -1215,7 +1215,6 @@ pub const Protocols = struct { var ligthMapReader = utils.BinaryReader.init(_inflatedData); const map = main.globalAllocator.create(main.server.terrain.LightMap.LightMapFragment); map.init(pos.wx, pos.wy, pos.voxelSize); - _ = map.refCount.fetchAdd(1, .monotonic); for(&map.startHeight) |*val| { val.* = try ligthMapReader.readInt(i16); } diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 1dda2821..643016d9 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -807,8 +807,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var allSun: bool = self.chunk.data.paletteLength == 1 and self.chunk.data.palette[0].typ == 0; var sunStarters: [chunk.chunkSize*chunk.chunkSize][3]u8 = undefined; var index: usize = 0; - const lightStartMap = mesh_storage.getLightMapPieceAndIncreaseRefCount(self.pos.wx, self.pos.wy, self.pos.voxelSize) orelse break :sunLight; - defer lightStartMap.decreaseRefCount(); + const lightStartMap = mesh_storage.getLightMapPiece(self.pos.wx, self.pos.wy, self.pos.voxelSize) orelse break :sunLight; x = 0; while(x < chunk.chunkSize) : (x += 1) { var y: u8 = 0; diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index ff07e23f..92d46a35 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -33,7 +33,7 @@ const ChunkMeshNode = struct { const storageSize = 64; const storageMask = storageSize - 1; var storageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined; -var mapStorageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize]?*LightMap.LightMapFragment = undefined; +var mapStorageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize]Atomic(?*LightMap.LightMapFragment) = undefined; var meshList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator); var priorityMeshUpdateList: main.utils.ConcurrentQueue(chunk.ChunkPosition) = undefined; pub var updatableList = main.List(chunk.ChunkPosition).init(main.globalAllocator); @@ -85,8 +85,8 @@ pub fn init() void { // MARK: init() } } for(&mapStorageLists) |*mapStorageList| { - mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]?*LightMap.LightMapFragment); - @memset(mapStorageList.*, null); + mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]Atomic(?*LightMap.LightMapFragment)); + @memset(mapStorageList.*, .init(null)); } priorityMeshUpdateList = .init(main.globalAllocator, 16); mapUpdatableList = .init(main.globalAllocator, 16); @@ -102,7 +102,6 @@ pub fn deinit() void { lastPz = 0; lastRD = 0; freeOldMeshes(olderPx, olderPy, olderPz, olderRD); - main.heap.GarbageCollection.waitForFreeCompletion(); for(storageLists) |storageList| { main.globalAllocator.destroy(storageList); } @@ -112,7 +111,7 @@ pub fn deinit() void { updatableList.clearAndFree(); while(mapUpdatableList.dequeue()) |map| { - map.decreaseRefCount(); + map.deferredDeinit(); } mapUpdatableList.deinit(); priorityMeshUpdateList.deinit(); @@ -121,6 +120,7 @@ pub fn deinit() void { } blockUpdateList.deinit(); meshList.clearAndFree(); + main.heap.GarbageCollection.waitForFreeCompletion(); meshMemoryPool.deinit(); } @@ -159,7 +159,7 @@ fn updateHigherLodNodeFinishedMeshing(pos_: chunk.ChunkPosition, finishedMeshing } } -fn getMapPiecePointer(x: i32, y: i32, voxelSize: u31) *?*LightMap.LightMapFragment { +fn getMapPiecePointer(x: i32, y: i32, voxelSize: u31) *Atomic(?*LightMap.LightMapFragment) { const lod = std.math.log2_int(u31, voxelSize); var xIndex = x >> lod + LightMap.LightMapFragment.mapShift; var yIndex = y >> lod + LightMap.LightMapFragment.mapShift; @@ -169,14 +169,8 @@ fn getMapPiecePointer(x: i32, y: i32, voxelSize: u31) *?*LightMap.LightMapFragme return &(&mapStorageLists)[lod][@intCast(index)]; } -pub fn getLightMapPieceAndIncreaseRefCount(x: i32, y: i32, voxelSize: u31) ?*LightMap.LightMapFragment { - mutex.lock(); - defer mutex.unlock(); - const result: *LightMap.LightMapFragment = getMapPiecePointer(x, y, voxelSize).* orelse { - return null; - }; - result.increaseRefCount(); - return result; +pub fn getLightMapPiece(x: i32, y: i32, voxelSize: u31) ?*LightMap.LightMapFragment { + return getMapPiecePointer(x, y, voxelSize).load(.acquire); } pub fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block { @@ -437,13 +431,9 @@ fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16) void { const yIndex = @divExact(y, size) & storageMask; const index = xIndex*storageSize + yIndex; - const mapPointer = &mapStorageLists[_lod][@intCast(index)]; - mutex.lock(); - const oldMap = mapPointer.*; - mapPointer.* = null; - mutex.unlock(); + const oldMap = mapStorageLists[_lod][@intCast(index)].swap(null, .monotonic); if(oldMap) |map| { - map.decreaseRefCount(); + map.deferredDeinit(); } } } @@ -576,14 +566,12 @@ fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16, meshR const index = xIndex*storageSize + yIndex; const pos = LightMap.MapFragmentPosition{.wx = x, .wy = y, .voxelSize = @as(u31, 1) << lod, .voxelSizeShift = lod}; - const node = &mapStorageLists[_lod][@intCast(index)]; - mutex.lock(); - if(node.*) |map| { - std.debug.assert(std.meta.eql(pos, map.pos)); + const map = mapStorageLists[_lod][@intCast(index)].load(.unordered); + if(map) |_map| { + std.debug.assert(std.meta.eql(pos, _map.pos)); } else { mapRequests.append(pos); } - mutex.unlock(); } } } @@ -770,13 +758,12 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()= } while(mapUpdatableList.dequeue()) |map| { if(!isMapInRenderDistance(map.pos)) { - map.decreaseRefCount(); + map.deferredDeinit(); } else { - const mapPointer = getMapPiecePointer(map.pos.wx, map.pos.wy, map.pos.voxelSize); - if(mapPointer.*) |old| { - old.decreaseRefCount(); + const mapPointer = getMapPiecePointer(map.pos.wx, map.pos.wy, map.pos.voxelSize).swap(map, .release); + if(mapPointer) |old| { + old.deferredDeinit(); } - mapPointer.* = map; } } while(updatableList.items.len != 0) { diff --git a/src/server/terrain/LightMap.zig b/src/server/terrain/LightMap.zig index 5430054e..a0e57546 100644 --- a/src/server/terrain/LightMap.zig +++ b/src/server/terrain/LightMap.zig @@ -20,25 +20,18 @@ pub const LightMapFragment = struct { startHeight: [mapSize*mapSize]i16 = undefined, pos: MapFragmentPosition, - refCount: Atomic(u16) = .init(0), - pub fn init(self: *LightMapFragment, wx: i32, wy: i32, voxelSize: u31) void { self.* = .{ .pos = MapFragmentPosition.init(wx, wy, voxelSize), }; } - pub fn increaseRefCount(self: *LightMapFragment) void { - const prevVal = self.refCount.fetchAdd(1, .monotonic); - std.debug.assert(prevVal != 0); + fn deinit(self: *const LightMapFragment, _: usize) void { + main.globalAllocator.destroy(self); } - pub fn decreaseRefCount(self: *LightMapFragment) void { - const prevVal = self.refCount.fetchSub(1, .monotonic); - std.debug.assert(prevVal != 0); - if(prevVal == 1) { - main.globalAllocator.destroy(self); - } + pub fn deferredDeinit(self: *LightMapFragment) void { + main.heap.GarbageCollection.deferredFree(.{.ptr = self, .freeFunction = main.utils.castFunctionSelfToAnyopaque(LightMapFragment.deinit)}); } pub fn getHeight(self: *LightMapFragment, wx: i32, wy: i32) i32 { @@ -51,7 +44,7 @@ pub const LightMapFragment = struct { const cacheSize = 1 << 6; // Must be a power of 2! const cacheMask = cacheSize - 1; const associativity = 8; // 64MiB MiB Cache size -var cache: Cache(LightMapFragment, cacheSize, associativity, LightMapFragment.decreaseRefCount) = .{}; +var cache: Cache(LightMapFragment, cacheSize, associativity, LightMapFragment.deferredDeinit) = .{}; fn cacheInit(pos: MapFragmentPosition) *LightMapFragment { const mapFragment = main.globalAllocator.create(LightMapFragment); @@ -65,7 +58,6 @@ fn cacheInit(pos: MapFragmentPosition) *LightMapFragment { mapFragment.startHeight[x << LightMapFragment.mapShift | y] = @max(0, baseHeight +| 16); // Simple heuristic. TODO: Update this value once chunks get generated in the region. } } - _ = @atomicRmw(u16, &mapFragment.refCount.raw, .Add, 1, .monotonic); return mapFragment; } @@ -73,12 +65,12 @@ pub fn deinit() void { cache.clear(); } -pub fn getOrGenerateFragmentAndIncreaseRefCount(wx: i32, wy: i32, voxelSize: u31) *LightMapFragment { +pub fn getOrGenerateFragment(wx: i32, wy: i32, voxelSize: u31) *LightMapFragment { const compare = MapFragmentPosition.init( wx & ~@as(i32, LightMapFragment.mapMask*voxelSize | voxelSize - 1), wy & ~@as(i32, LightMapFragment.mapMask*voxelSize | voxelSize - 1), voxelSize, ); - const result = cache.findOrCreate(compare, cacheInit, LightMapFragment.increaseRefCount); + const result = cache.findOrCreate(compare, cacheInit, null); return result; } diff --git a/src/server/world.zig b/src/server/world.zig index 896914cb..73a71039 100644 --- a/src/server/world.zig +++ b/src/server/world.zig @@ -226,8 +226,7 @@ const ChunkManager = struct { // MARK: ChunkManager pub fn run(self: *LightMapLoadTask) void { defer self.clean(); - const map = terrain.LightMap.getOrGenerateFragmentAndIncreaseRefCount(self.pos.wx, self.pos.wy, self.pos.voxelSize); - defer map.decreaseRefCount(); + const map = terrain.LightMap.getOrGenerateFragment(self.pos.wx, self.pos.wy, self.pos.voxelSize); if(self.source) |source| { if(source.connected.load(.unordered)) main.network.Protocols.lightMapTransmission.sendLightMap(source.conn, map); } else {