diff --git a/src/audio.zig b/src/audio.zig index af8b8b0a..4a65178b 100644 --- a/src/audio.zig +++ b/src/audio.zig @@ -72,7 +72,7 @@ fn findMusic(musicId: []const u8) ?[]f32 { { taskMutex.lock(); defer taskMutex.unlock(); - if(musicCache.find(AudioData{.musicId = musicId})) |musicData| { + if(musicCache.find(AudioData{.musicId = musicId}, null)) |musicData| { return musicData.data; } for(activeTasks.items) |taskFileName| { diff --git a/src/itemdrop.zig b/src/itemdrop.zig index 6d2a2ee8..f537efaa 100644 --- a/src/itemdrop.zig +++ b/src/itemdrop.zig @@ -669,7 +669,7 @@ pub const ItemDropRenderer = struct { fn getModelIndex(item: items.Item) u31 { const compareObject = ItemVoxelModel{.item = item}; - return voxelModels.findOrCreate(compareObject, ItemVoxelModel.init).index; + return voxelModels.findOrCreate(compareObject, ItemVoxelModel.init, null).index; } pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) void { diff --git a/src/renderer.zig b/src/renderer.zig index 8f05103b..25600209 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -243,7 +243,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo worldFrameBuffer.bindTexture(c.GL_TEXTURE3); - const playerBlock = mesh_storage.getBlockFromAnyLodFromRenderThread(@intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2]))); + const playerBlock = mesh_storage.getBlockFromAnyLod(@intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2]))); if(settings.bloom) { Bloom.render(lastWidth, lastHeight, playerBlock); @@ -695,7 +695,7 @@ pub const MeshSelection = struct { selectedBlockPos = null; while(total_tMax < closestDistance) { - const block = mesh_storage.getBlockFromRenderThread(voxelPos[0], voxelPos[1], voxelPos[2]) orelse break; + const block = mesh_storage.getBlock(voxelPos[0], voxelPos[1], voxelPos[2]) orelse break; if(block.typ != 0) { if(block.blockClass() != .fluid) { // TODO: Buckets could select fluids const relativePlayerPos: Vec3f = @floatCast(pos - @as(Vec3d, @floatFromInt(voxelPos))); @@ -737,7 +737,7 @@ pub const MeshSelection = struct { pub fn placeBlock(inventoryStack: *main.items.ItemStack) void { if(selectedBlockPos) |selectedPos| { - var block = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; + var block = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; if(inventoryStack.item) |item| { switch(item) { .baseItem => |baseItem| { @@ -758,7 +758,7 @@ pub const MeshSelection = struct { const neighborPos = posBeforeBlock; neighborDir = selectedPos - posBeforeBlock; const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); - block = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; + block = mesh_storage.getBlock(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return; if(block.typ == itemBlock) { if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, &block, false)) { // TODO: world.updateBlock(bi.x, bi.y, bi.z, block.data); (→ Sending it over the network) @@ -790,7 +790,7 @@ pub const MeshSelection = struct { pub fn breakBlock(inventoryStack: *main.items.ItemStack) void { if(selectedBlockPos) |selectedPos| { - var block = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; + var block = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return; // TODO: Breaking animation and tools. if(inventoryStack.item) |item| { switch(item) { diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index fdf1c36e..63195117 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -22,7 +22,7 @@ const chunk_meshing = @import("chunk_meshing.zig"); const ChunkMeshNode = struct { - mesh: Atomic(?*chunk_meshing.ChunkMesh), + mesh: ?*chunk_meshing.ChunkMesh, lod: u3, min: Vec2f, max: Vec2f, @@ -32,7 +32,7 @@ const ChunkMeshNode = struct { const storageSize = 64; const storageMask = storageSize - 1; var storageLists: [settings.highestLOD + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined; -var mapStorageLists: [settings.highestLOD + 1]*[storageSize*storageSize]Atomic(?*LightMap.LightMapFragment) = undefined; +var mapStorageLists: [settings.highestLOD + 1]*[storageSize*storageSize]?*LightMap.LightMapFragment = undefined; var meshList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator); var priorityMeshUpdateList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator); pub var updatableList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator); @@ -58,14 +58,14 @@ pub fn init() void { for(&storageLists) |*storageList| { storageList.* = main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode); for(storageList.*) |*val| { - val.mesh = Atomic(?*chunk_meshing.ChunkMesh).init(null); + val.mesh = null; val.rendered = false; val.active = false; } } for(&mapStorageLists) |*mapStorageList| { - mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]Atomic(?*LightMap.LightMapFragment)); - @memset(mapStorageList.*, Atomic(?*LightMap.LightMapFragment).init(null)); + mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]?*LightMap.LightMapFragment); + @memset(mapStorageList.*, null); } } @@ -106,7 +106,7 @@ pub fn deinit() void { clearList.deinit(); } -fn getNodeFromRenderThread(pos: chunk.ChunkPosition) *ChunkMeshNode { +fn getNodePointer(pos: chunk.ChunkPosition) *ChunkMeshNode { const lod = std.math.log2_int(u31, pos.voxelSize); var xIndex = pos.wx >> lod+chunk.chunkShift; var yIndex = pos.wy >> lod+chunk.chunkShift; @@ -118,7 +118,7 @@ fn getNodeFromRenderThread(pos: chunk.ChunkPosition) *ChunkMeshNode { return &storageLists[lod][@intCast(index)]; } -fn getMapPieceLocation(x: i32, y: i32, voxelSize: u31) *Atomic(?*LightMap.LightMapFragment) { +fn getMapPiecePointer(x: i32, y: i32, voxelSize: u31) *?*LightMap.LightMapFragment { const lod = std.math.log2_int(u31, voxelSize); var xIndex = x >> lod+LightMap.LightMapFragment.mapShift; var yIndex = y >> lod+LightMap.LightMapFragment.mapShift; @@ -129,62 +129,53 @@ fn getMapPieceLocation(x: i32, y: i32, voxelSize: u31) *Atomic(?*LightMap.LightM } pub fn getLightMapPieceAndIncreaseRefCount(x: i32, y: i32, voxelSize: u31) ?*LightMap.LightMapFragment { - const result: *LightMap.LightMapFragment = getMapPieceLocation(x, y, voxelSize).load(.acquire) orelse return null; - var refCount: u16 = 1; - while(result.refCount.cmpxchgWeak(refCount, refCount+1, .monotonic, .monotonic)) |otherVal| { - if(otherVal == 0) return null; - refCount = otherVal; - } + mutex.lock(); + defer mutex.unlock(); + const result: *LightMap.LightMapFragment = getMapPiecePointer(x, y, voxelSize).* orelse { + return null; + }; + result.increaseRefCount(); return result; } -pub fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block { - const node = getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}); - const mesh = node.mesh.load(.acquire) orelse return null; +pub fn getBlock(x: i32, y: i32, z: i32) ?blocks.Block { + const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}); + mutex.lock(); + defer mutex.unlock(); + const mesh = node.mesh orelse return null; const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask); return block; } -pub fn getBlockFromAnyLodFromRenderThread(x: i32, y: i32, z: i32) blocks.Block { +pub fn getBlockFromAnyLod(x: i32, y: i32, z: i32) blocks.Block { var lod: u5 = 0; while(lod < settings.highestLOD) : (lod += 1) { - const node = getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod}); - const mesh = node.mesh.load(.acquire) orelse continue; + const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod}); + mutex.lock(); + defer mutex.unlock(); + const mesh = node.mesh orelse continue; const block = mesh.chunk.getBlock(x & chunk.chunkMask<>sizeShift & 1) | (mesh.pos.wy>>sizeShift & 1)<<1 | (mesh.pos.wz>>sizeShift & 1)<<2); parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex); @@ -867,8 +900,8 @@ pub fn updateMeshes(targetTime: i64) void { defer blockUpdateMutex.unlock(); for(blockUpdateList.items) |blockUpdate| { const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1}; - const node = getNodeFromRenderThread(pos); - if(node.mesh.load(.acquire)) |mesh| { + if(getMeshAndIncreaseRefCount(pos)) |mesh| { + defer mesh.decreaseRefCount(); mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock); } // TODO: It seems like we simply ignore the block update if we don't have the mesh yet. } @@ -890,10 +923,14 @@ pub fn updateMeshes(targetTime: i64) void { continue; } mesh.needsMeshUpdate = false; + if(getNodePointer(mesh.pos).mesh != mesh) { + mutex.unlock(); + mesh.decreaseRefCount(); + continue; + } mutex.unlock(); defer mutex.lock(); mesh.decreaseRefCount(); - if(getNodeFromRenderThread(mesh.pos).mesh.load(.acquire) != mesh) continue; // This mesh isn't used for rendering anymore. mesh.uploadData(); if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh. } @@ -901,9 +938,11 @@ pub fn updateMeshes(targetTime: i64) void { if(!isMapInRenderDistance(map.pos)) { map.decreaseRefCount(); } else { - if(getMapPieceLocation(map.pos.wx, map.pos.wy, map.pos.voxelSize).swap(map, .acq_rel)) |old| { + const mapPointer = getMapPiecePointer(map.pos.wx, map.pos.wy, map.pos.voxelSize); + if(mapPointer.*) |old| { old.decreaseRefCount(); } + mapPointer.* = map; } } while(updatableList.items.len != 0) { @@ -935,11 +974,15 @@ pub fn updateMeshes(targetTime: i64) void { mutex.unlock(); defer mutex.lock(); if(isInRenderDistance(mesh.pos)) { - const node = getNodeFromRenderThread(mesh.pos); + const node = getNodePointer(mesh.pos); mesh.finishedMeshing = true; mesh.uploadData(); - if(node.mesh.swap(mesh, .acq_rel)) |oldMesh| { - oldMesh.decreaseRefCount(); + mutex.lock(); + const oldMesh = node.mesh; + node.mesh = mesh; + mutex.unlock(); + if(oldMesh) |_oldMesh| { + _oldMesh.decreaseRefCount(); } } else { mesh.decreaseRefCount(); @@ -973,12 +1016,12 @@ pub fn addMeshToStorage(mesh: *chunk_meshing.ChunkMesh) error{AlreadyStored}!voi mutex.lock(); defer mutex.unlock(); if(isInRenderDistance(mesh.pos)) { - const node = getNodeFromRenderThread(mesh.pos); - if(node.mesh.cmpxchgStrong(null, mesh, .acq_rel, .monotonic) != null) { + const node = getNodePointer(mesh.pos); + if(node.mesh != null) { return error.AlreadyStored; - } else { - mesh.increaseRefCount(); } + node.mesh = mesh; + mesh.increaseRefCount(); } } diff --git a/src/server/terrain/CaveBiomeMap.zig b/src/server/terrain/CaveBiomeMap.zig index 101b4c2d..7da69f96 100644 --- a/src/server/terrain/CaveBiomeMap.zig +++ b/src/server/terrain/CaveBiomeMap.zig @@ -50,6 +50,19 @@ pub const CaveBiomeMapFragment = struct { relZ >>= caveBiomeShift; return relX << 2*(caveBiomeMapShift - caveBiomeShift) | relY << caveBiomeMapShift-caveBiomeShift | relZ; } + + pub fn increaseRefCount(self: *CaveBiomeMapFragment) void { + const prevVal = self.refCount.fetchAdd(1, .monotonic); + std.debug.assert(prevVal != 0); + } + + pub fn decreaseRefCount(self: *CaveBiomeMapFragment) void { + const prevVal = self.refCount.fetchSub(1, .monotonic); + std.debug.assert(prevVal != 0); + if(prevVal == 1) { + main.globalAllocator.destroy(self); + } + } }; /// A generator for the cave biome map. @@ -105,20 +118,20 @@ pub const InterpolatableCaveBiomeMapView = struct { pub fn init(pos: ChunkPosition, width: i32) InterpolatableCaveBiomeMapView { return InterpolatableCaveBiomeMapView { .fragments = [_]*CaveBiomeMapFragment { - getOrGenerateFragment(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), - getOrGenerateFragment(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy -% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz -% CaveBiomeMapFragment.caveBiomeMapSize/2), + getOrGenerateFragmentAndIncreaseRefCount(pos.wx +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wy +% CaveBiomeMapFragment.caveBiomeMapSize/2, pos.wz +% CaveBiomeMapFragment.caveBiomeMapSize/2), }, .surfaceFragments = [_]*MapFragment { - SurfaceMap.getOrGenerateFragment(pos.wx -% 32, pos.wy -% 32, pos.voxelSize), - SurfaceMap.getOrGenerateFragment(pos.wx -% 32, pos.wy +% width +% 32, pos.voxelSize), - SurfaceMap.getOrGenerateFragment(pos.wx +% width +% 32, pos.wy -% 32, pos.voxelSize), - SurfaceMap.getOrGenerateFragment(pos.wx +% width +% 32, pos.wy +% width +% 32, pos.voxelSize), + SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% 32, pos.wy -% 32, pos.voxelSize), + SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% 32, pos.wy +% width +% 32, pos.voxelSize), + SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx +% width +% 32, pos.wy -% 32, pos.voxelSize), + SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx +% width +% 32, pos.wy +% width +% 32, pos.voxelSize), }, .pos = pos, .width = width, @@ -127,10 +140,10 @@ pub const InterpolatableCaveBiomeMapView = struct { pub fn deinit(self: InterpolatableCaveBiomeMapView) void { for(self.fragments) |mapFragment| { - mapFragmentDeinit(mapFragment); + mapFragment.decreaseRefCount(); } for(self.surfaceFragments) |mapFragment| { - mapFragment.deinit(); + mapFragment.decreaseRefCount(); } } @@ -434,7 +447,7 @@ pub const CaveBiomeMapView = struct { const cacheSize = 1 << 8; // Must be a power of 2! const cacheMask = cacheSize - 1; const associativity = 8; -var cache: Cache(CaveBiomeMapFragment, cacheSize, associativity, mapFragmentDeinit) = .{}; +var cache: Cache(CaveBiomeMapFragment, cacheSize, associativity, CaveBiomeMapFragment.decreaseRefCount) = .{}; var profile: TerrainGenerationProfile = undefined; @@ -457,12 +470,6 @@ pub fn deinit() void { cache.clear(); } -fn mapFragmentDeinit(mapFragment: *CaveBiomeMapFragment) void { - if(@atomicRmw(u16, &mapFragment.refCount.raw, .Sub, 1, .monotonic) == 1) { - main.globalAllocator.destroy(mapFragment); - } -} - fn cacheInit(pos: ChunkPosition) *CaveBiomeMapFragment { const mapFragment = main.globalAllocator.create(CaveBiomeMapFragment); mapFragment.init(pos.wx, pos.wy, pos.wz); @@ -473,7 +480,7 @@ fn cacheInit(pos: ChunkPosition) *CaveBiomeMapFragment { return mapFragment; } -fn getOrGenerateFragment(_wx: i32, _wy: i32, _wz: i32) *CaveBiomeMapFragment { +fn getOrGenerateFragmentAndIncreaseRefCount(_wx: i32, _wy: i32, _wz: i32) *CaveBiomeMapFragment { const wx = _wx & ~@as(i32, CaveBiomeMapFragment.caveBiomeMapMask); const wy = _wy & ~@as(i32, CaveBiomeMapFragment.caveBiomeMapMask); const wz = _wz & ~@as(i32, CaveBiomeMapFragment.caveBiomeMapMask); @@ -481,7 +488,6 @@ fn getOrGenerateFragment(_wx: i32, _wy: i32, _wz: i32) *CaveBiomeMapFragment { .wx = wx, .wy = wy, .wz = wz, .voxelSize = CaveBiomeMapFragment.caveBiomeSize, }; - const result = cache.findOrCreate(compare, cacheInit); - std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0); + const result = cache.findOrCreate(compare, cacheInit, CaveBiomeMapFragment.increaseRefCount); return result; } \ No newline at end of file diff --git a/src/server/terrain/CaveMap.zig b/src/server/terrain/CaveMap.zig index 18baff77..6f482d8d 100644 --- a/src/server/terrain/CaveMap.zig +++ b/src/server/terrain/CaveMap.zig @@ -61,6 +61,19 @@ pub const CaveMapFragment = struct { return maskLower | maskUpper; } + pub fn increaseRefCount(self: *CaveMapFragment) void { + const prevVal = self.refCount.fetchAdd(1, .monotonic); + std.debug.assert(prevVal != 0); + } + + pub fn decreaseRefCount(self: *CaveMapFragment) void { + const prevVal = self.refCount.fetchSub(1, .monotonic); + std.debug.assert(prevVal != 0); + if(prevVal == 1) { + main.globalAllocator.destroy(self); + } + } + pub fn addRange(self: *CaveMapFragment, _relX: i32, _relY: i32, _start: i32, _end: i32) void { const relX = _relX >> self.voxelShift; const relY = _relY >> self.voxelShift; @@ -135,21 +148,21 @@ pub const CaveMapView = struct { return CaveMapView { .reference = chunk, .fragments = [_]*CaveMapFragment { - getOrGenerateFragment(chunk.pos.wx -% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx -% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx -% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx -% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx +% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx +% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx +% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), - getOrGenerateFragment(chunk.pos.wx +% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx -% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx -% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx -% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx -% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx +% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx +% chunk.width, chunk.pos.wy -% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx +% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz -% chunk.width, chunk.pos.voxelSize), + getOrGenerateFragmentAndIncreaseRefCount(chunk.pos.wx +% chunk.width, chunk.pos.wy +% chunk.width, chunk.pos.wz +% chunk.width, chunk.pos.voxelSize), }, }; } pub fn deinit(self: CaveMapView) void { for(self.fragments) |mapFragment| { - mapFragmentDeinit(mapFragment); + mapFragment.decreaseRefCount(); } } @@ -285,15 +298,9 @@ pub const CaveMapView = struct { const cacheSize = 1 << 9; // Must be a power of 2! const cacheMask = cacheSize - 1; const associativity = 8; // 512 MiB Cache size -var cache: Cache(CaveMapFragment, cacheSize, associativity, mapFragmentDeinit) = .{}; +var cache: Cache(CaveMapFragment, cacheSize, associativity, CaveMapFragment.decreaseRefCount) = .{}; var profile: TerrainGenerationProfile = undefined; -fn mapFragmentDeinit(mapFragment: *CaveMapFragment) void { - if(@atomicRmw(u16, &mapFragment.refCount.raw, .Sub, 1, .monotonic) == 1) { - main.globalAllocator.destroy(mapFragment); - } -} - fn cacheInit(pos: ChunkPosition) *CaveMapFragment { const mapFragment = main.globalAllocator.create(CaveMapFragment); mapFragment.init(pos.wx, pos.wy, pos.wz, pos.voxelSize); @@ -323,14 +330,13 @@ pub fn deinit() void { cache.clear(); } -fn getOrGenerateFragment(wx: i32, wy: i32, wz: i32, voxelSize: u31) *CaveMapFragment { +fn getOrGenerateFragmentAndIncreaseRefCount(wx: i32, wy: i32, wz: i32, voxelSize: u31) *CaveMapFragment { const compare = ChunkPosition { .wx = wx & ~@as(i32, CaveMapFragment.widthMask*voxelSize | voxelSize-1), .wy = wy & ~@as(i32, CaveMapFragment.widthMask*voxelSize | voxelSize-1), .wz = wz & ~@as(i32, CaveMapFragment.heightMask*voxelSize | voxelSize-1), .voxelSize = voxelSize, }; - const result = cache.findOrCreate(compare, cacheInit); - std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0); + const result = cache.findOrCreate(compare, cacheInit, CaveMapFragment.increaseRefCount); return result; } \ No newline at end of file diff --git a/src/server/terrain/ClimateMap.zig b/src/server/terrain/ClimateMap.zig index 41ec63e5..c5a4e8fd 100644 --- a/src/server/terrain/ClimateMap.zig +++ b/src/server/terrain/ClimateMap.zig @@ -60,6 +60,19 @@ pub const ClimateMapFragment = struct { pub fn hashCode(wx: i32, wy: i32) u32 { return @bitCast((wx >> mapShift)*%33 + (wy >> mapShift)); } + + pub fn increaseRefCount(self: *ClimateMapFragment) void { + const prevVal = self.refCount.fetchAdd(1, .monotonic); + std.debug.assert(prevVal != 0); + } + + pub fn decreaseRefCount(self: *ClimateMapFragment) void { + const prevVal = self.refCount.fetchSub(1, .monotonic); + std.debug.assert(prevVal != 0); + if(prevVal == 1) { + main.globalAllocator.destroy(self); + } + } }; /// Generates the climate(aka Biome) map, which is a rough representation of the world. @@ -91,7 +104,7 @@ pub const ClimateMapGenerator = struct { const cacheSize = 1 << 8; // Must be a power of 2! const cacheMask = cacheSize - 1; const associativity = 4; -var cache: Cache(ClimateMapFragment, cacheSize, associativity, mapFragmentDeinit) = .{}; +var cache: Cache(ClimateMapFragment, cacheSize, associativity, ClimateMapFragment.decreaseRefCount) = .{}; var profile: TerrainGenerationProfile = undefined; pub fn initGenerators() void { @@ -105,12 +118,6 @@ pub fn deinitGenerators() void { ClimateMapGenerator.generatorRegistry.clearAndFree(main.globalAllocator.allocator); } -fn mapFragmentDeinit(mapFragment: *ClimateMapFragment) void { - if(@atomicRmw(u16, &mapFragment.refCount.raw, .Sub, 1, .monotonic) == 1) { - main.globalAllocator.destroy(mapFragment); - } -} - fn cacheInit(pos: ClimateMapFragmentPosition) *ClimateMapFragment { const mapFragment = main.globalAllocator.create(ClimateMapFragment); mapFragment.init(pos.wx, pos.wy); @@ -127,11 +134,9 @@ pub fn deinit() void { cache.clear(); } -/// Call deinit on the result. -fn getOrGenerateFragment(wx: i32, wy: i32) *ClimateMapFragment { +fn getOrGenerateFragmentAndIncreaseRefCount(wx: i32, wy: i32) *ClimateMapFragment { const compare = ClimateMapFragmentPosition{.wx = wx, .wy = wy}; - const result = cache.findOrCreate(compare, cacheInit); - std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0); + const result = cache.findOrCreate(compare, cacheInit, ClimateMapFragment.increaseRefCount); return result; } @@ -145,8 +150,8 @@ pub fn getBiomeMap(allocator: NeverFailingAllocator, wx: i32, wy: i32, width: u3 while(wxEnd -% x >= 0) : (x +%= ClimateMapFragment.mapSize) { var y = wzStart; while(wzEnd -% y >= 0) : (y +%= ClimateMapFragment.mapSize) { - const mapPiece = getOrGenerateFragment(x, y); - defer mapFragmentDeinit(mapPiece); + const mapPiece = getOrGenerateFragmentAndIncreaseRefCount(x, y); + defer mapPiece.decreaseRefCount(); // Offset of the indices in the result map: const xOffset = (x -% wx) >> MapFragment.biomeShift; const yOffset = (y -% wy) >> MapFragment.biomeShift; diff --git a/src/server/terrain/LightMap.zig b/src/server/terrain/LightMap.zig index efb8adf6..53dddcd4 100644 --- a/src/server/terrain/LightMap.zig +++ b/src/server/terrain/LightMap.zig @@ -29,8 +29,15 @@ pub const LightMapFragment = struct { }; } + pub fn increaseRefCount(self: *LightMapFragment) void { + const prevVal = self.refCount.fetchAdd(1, .monotonic); + std.debug.assert(prevVal != 0); + } + pub fn decreaseRefCount(self: *LightMapFragment) void { - if(@atomicRmw(u16, &self.refCount.raw, .Sub, 1, .monotonic) == 1) { + const prevVal = self.refCount.fetchSub(1, .monotonic); + std.debug.assert(prevVal != 0); + if(prevVal == 1) { main.globalAllocator.destroy(self); } } @@ -51,8 +58,8 @@ var cache: Cache(LightMapFragment, cacheSize, associativity, LightMapFragment.de fn cacheInit(pos: MapFragmentPosition) *LightMapFragment { const mapFragment = main.globalAllocator.create(LightMapFragment); mapFragment.init(pos.wx, pos.wy, pos.voxelSize); - const surfaceMap = terrain.SurfaceMap.getOrGenerateFragment(pos.wx, pos.wy, pos.voxelSize); - defer surfaceMap.deinit(); + const surfaceMap = terrain.SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx, pos.wy, pos.voxelSize); + defer surfaceMap.decreaseRefCount(); comptime std.debug.assert(LightMapFragment.mapSize == terrain.SurfaceMap.MapFragment.mapSize); for(0..LightMapFragment.mapSize) |x| { for(0..LightMapFragment.mapSize) |y| { @@ -68,14 +75,12 @@ pub fn deinit() void { cache.clear(); } -/// Call decreaseRefCount on the result. -pub fn getOrGenerateFragment(wx: i32, wy: i32, voxelSize: u31) *LightMapFragment { +pub fn getOrGenerateFragmentAndIncreaseRefCount(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); - std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0); + const result = cache.findOrCreate(compare, cacheInit, LightMapFragment.increaseRefCount); return result; } \ No newline at end of file diff --git a/src/server/terrain/SurfaceMap.zig b/src/server/terrain/SurfaceMap.zig index 7ad4252c..2b04cc13 100644 --- a/src/server/terrain/SurfaceMap.zig +++ b/src/server/terrain/SurfaceMap.zig @@ -78,8 +78,17 @@ pub const MapFragment = struct { }; } - pub fn deinit(self: *MapFragment) void { - mapFragmentDeinit(self); + pub fn increaseRefCount(self: *MapFragment) void { + const prevVal = self.refCount.fetchAdd(1, .monotonic); + std.debug.assert(prevVal != 0); + } + + pub fn decreaseRefCount(self: *MapFragment) void { + const prevVal = self.refCount.fetchSub(1, .monotonic); + std.debug.assert(prevVal != 0); + if(prevVal == 1) { + main.globalAllocator.destroy(self); + } } pub fn getBiome(self: *MapFragment, wx: i32, wy: i32) *const Biome { @@ -124,7 +133,7 @@ pub const MapGenerator = struct { const cacheSize = 1 << 6; // Must be a power of 2! const cacheMask = cacheSize - 1; const associativity = 8; // ~400MiB MiB Cache size -var cache: Cache(MapFragment, cacheSize, associativity, mapFragmentDeinit) = .{}; +var cache: Cache(MapFragment, cacheSize, associativity, MapFragment.decreaseRefCount) = .{}; var profile: TerrainGenerationProfile = undefined; pub fn initGenerators() void { @@ -138,12 +147,6 @@ pub fn deinitGenerators() void { MapGenerator.generatorRegistry.clearAndFree(main.globalAllocator.allocator); } -fn mapFragmentDeinit(mapFragment: *MapFragment) void { - if(@atomicRmw(u16, &mapFragment.refCount.raw, .Sub, 1, .monotonic) == 1) { - main.globalAllocator.destroy(mapFragment); - } -} - fn cacheInit(pos: MapFragmentPosition) *MapFragment { const mapFragment = main.globalAllocator.create(MapFragment); mapFragment.init(pos.wx, pos.wy, pos.voxelSize); @@ -161,13 +164,12 @@ pub fn deinit() void { } /// Call deinit on the result. -pub fn getOrGenerateFragment(wx: i32, wy: i32, voxelSize: u31) *MapFragment { +pub fn getOrGenerateFragmentAndIncreaseRefCount(wx: i32, wy: i32, voxelSize: u31) *MapFragment { const compare = MapFragmentPosition.init( wx & ~@as(i32, MapFragment.mapMask*voxelSize | voxelSize-1), wy & ~@as(i32, MapFragment.mapMask*voxelSize | voxelSize-1), voxelSize ); - const result = cache.findOrCreate(compare, cacheInit); - std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0); + const result = cache.findOrCreate(compare, cacheInit, MapFragment.increaseRefCount); return result; } \ No newline at end of file diff --git a/src/server/world.zig b/src/server/world.zig index 2bcf976d..3410cb74 100644 --- a/src/server/world.zig +++ b/src/server/world.zig @@ -127,7 +127,7 @@ const ChunkManager = struct { pub fn run(self: *LightMapLoadTask) void { defer self.clean(); - const map = terrain.LightMap.getOrGenerateFragment(self.pos.wx, self.pos.wy, self.pos.voxelSize); + const map = terrain.LightMap.getOrGenerateFragmentAndIncreaseRefCount(self.pos.wx, self.pos.wy, self.pos.voxelSize); defer map.decreaseRefCount(); if(self.source) |source| { main.network.Protocols.lightMapTransmission.sendLightMap(source.conn, map); @@ -204,8 +204,8 @@ const ChunkManager = struct { // TODO: Store chunk. } /// Generates a normal chunk at a given location, or if possible gets it from the cache. - pub fn getOrGenerateChunk(pos: ChunkPosition) *Chunk { - return chunkCache.findOrCreate(pos, chunkInitFunctionForCache); + pub fn getOrGenerateChunk(pos: ChunkPosition) *Chunk { // TODO: This is not thread safe! The chunk could get removed from the cache while in use. Reference counting should probably be used here. + return chunkCache.findOrCreate(pos, chunkInitFunctionForCache, null); } pub fn getChunkFromCache(pos: ChunkPosition) ?*Chunk { @@ -368,8 +368,8 @@ pub const ServerWorld = struct { std.log.info("Trying ({}, {})", .{self.spawn[0], self.spawn[1]}); if(self.isValidSpawnLocation(self.spawn[0], self.spawn[1])) break; } - const map = terrain.SurfaceMap.getOrGenerateFragment(self.spawn[0], self.spawn[1], 1); - defer map.deinit(); + const map = terrain.SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(self.spawn[0], self.spawn[1], 1); + defer map.decreaseRefCount(); self.spawn[2] = @intFromFloat(map.getHeight(self.spawn[0], self.spawn[1]) + 1); } self.generated = true; @@ -401,8 +401,8 @@ pub const ServerWorld = struct { } fn isValidSpawnLocation(_: *ServerWorld, wx: i32, wy: i32) bool { - const map = terrain.SurfaceMap.getOrGenerateFragment(wx, wy, 1); - defer map.deinit(); + const map = terrain.SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(wx, wy, 1); + defer map.decreaseRefCount(); return map.getBiome(wx, wy).isValidPlayerSpawn; } diff --git a/src/utils.zig b/src/utils.zig index f95d5262..2ac9344e 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1387,12 +1387,13 @@ pub fn Cache(comptime T: type, comptime numberOfBuckets: u32, comptime bucketSiz cacheMisses: Atomic(usize) = Atomic(usize).init(0), /// Tries to find the entry that fits to the supplied hashable. - pub fn find(self: *@This(), compareAndHash: anytype) ?*T { + pub fn find(self: *@This(), compareAndHash: anytype, comptime postGetFunction: ?fn(*T) void) ?*T { const index: u32 = compareAndHash.hashCode() & hashMask; _ = @atomicRmw(usize, &self.cacheRequests.raw, .Add, 1, .monotonic); self.buckets[index].mutex.lock(); defer self.buckets[index].mutex.unlock(); if(self.buckets[index].find(compareAndHash)) |item| { + if(postGetFunction) |fun| fun(item); return item; } _ = @atomicRmw(usize, &self.cacheMisses.raw, .Add, 1, .monotonic); @@ -1420,11 +1421,13 @@ pub fn Cache(comptime T: type, comptime numberOfBuckets: u32, comptime bucketSiz return self.buckets[index].add(item); } - pub fn findOrCreate(self: *@This(), compareAndHash: anytype, comptime initFunction: fn(@TypeOf(compareAndHash)) *T) *T { + pub fn findOrCreate(self: *@This(), compareAndHash: anytype, comptime initFunction: fn(@TypeOf(compareAndHash)) *T, comptime postGetFunction: ?fn(*T) void) *T { const index: u32 = compareAndHash.hashCode() & hashMask; self.buckets[index].mutex.lock(); defer self.buckets[index].mutex.unlock(); - return self.buckets[index].findOrCreate(compareAndHash, initFunction); + const result = self.buckets[index].findOrCreate(compareAndHash, initFunction); + if(postGetFunction) |fun| fun(result); + return result; } }; }