Audit usage of reference counting, making it thread safe in a (pretty rare) edge case.

Also uses a more consistent naming convention when reference counting is involved.

Fixes #332
Potentially fixes #329 (I can't reproduce it anymore)
Discovers #338
This commit is contained in:
IntegratedQuantum 2024-04-30 22:28:40 +02:00
parent 1a6454a92c
commit 3e0c666108
11 changed files with 235 additions and 165 deletions

View File

@ -72,7 +72,7 @@ fn findMusic(musicId: []const u8) ?[]f32 {
{ {
taskMutex.lock(); taskMutex.lock();
defer taskMutex.unlock(); defer taskMutex.unlock();
if(musicCache.find(AudioData{.musicId = musicId})) |musicData| { if(musicCache.find(AudioData{.musicId = musicId}, null)) |musicData| {
return musicData.data; return musicData.data;
} }
for(activeTasks.items) |taskFileName| { for(activeTasks.items) |taskFileName| {

View File

@ -669,7 +669,7 @@ pub const ItemDropRenderer = struct {
fn getModelIndex(item: items.Item) u31 { fn getModelIndex(item: items.Item) u31 {
const compareObject = ItemVoxelModel{.item = item}; 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 { pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) void {

View File

@ -243,7 +243,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
worldFrameBuffer.bindTexture(c.GL_TEXTURE3); 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) { if(settings.bloom) {
Bloom.render(lastWidth, lastHeight, playerBlock); Bloom.render(lastWidth, lastHeight, playerBlock);
@ -695,7 +695,7 @@ pub const MeshSelection = struct {
selectedBlockPos = null; selectedBlockPos = null;
while(total_tMax < closestDistance) { 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.typ != 0) {
if(block.blockClass() != .fluid) { // TODO: Buckets could select fluids if(block.blockClass() != .fluid) { // TODO: Buckets could select fluids
const relativePlayerPos: Vec3f = @floatCast(pos - @as(Vec3d, @floatFromInt(voxelPos))); 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 { pub fn placeBlock(inventoryStack: *main.items.ItemStack) void {
if(selectedBlockPos) |selectedPos| { 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| { if(inventoryStack.item) |item| {
switch(item) { switch(item) {
.baseItem => |baseItem| { .baseItem => |baseItem| {
@ -758,7 +758,7 @@ pub const MeshSelection = struct {
const neighborPos = posBeforeBlock; const neighborPos = posBeforeBlock;
neighborDir = selectedPos - posBeforeBlock; neighborDir = selectedPos - posBeforeBlock;
const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos))); 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(block.typ == itemBlock) {
if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, &block, false)) { 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) // 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 { pub fn breakBlock(inventoryStack: *main.items.ItemStack) void {
if(selectedBlockPos) |selectedPos| { 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. // TODO: Breaking animation and tools.
if(inventoryStack.item) |item| { if(inventoryStack.item) |item| {
switch(item) { switch(item) {

View File

@ -22,7 +22,7 @@ const chunk_meshing = @import("chunk_meshing.zig");
const ChunkMeshNode = struct { const ChunkMeshNode = struct {
mesh: Atomic(?*chunk_meshing.ChunkMesh), mesh: ?*chunk_meshing.ChunkMesh,
lod: u3, lod: u3,
min: Vec2f, min: Vec2f,
max: Vec2f, max: Vec2f,
@ -32,7 +32,7 @@ const ChunkMeshNode = struct {
const storageSize = 64; const storageSize = 64;
const storageMask = storageSize - 1; const storageMask = storageSize - 1;
var storageLists: [settings.highestLOD + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined; 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 meshList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator);
var priorityMeshUpdateList = 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); pub var updatableList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator);
@ -58,14 +58,14 @@ pub fn init() void {
for(&storageLists) |*storageList| { for(&storageLists) |*storageList| {
storageList.* = main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode); storageList.* = main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode);
for(storageList.*) |*val| { for(storageList.*) |*val| {
val.mesh = Atomic(?*chunk_meshing.ChunkMesh).init(null); val.mesh = null;
val.rendered = false; val.rendered = false;
val.active = false; val.active = false;
} }
} }
for(&mapStorageLists) |*mapStorageList| { for(&mapStorageLists) |*mapStorageList| {
mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]Atomic(?*LightMap.LightMapFragment)); mapStorageList.* = main.globalAllocator.create([storageSize*storageSize]?*LightMap.LightMapFragment);
@memset(mapStorageList.*, Atomic(?*LightMap.LightMapFragment).init(null)); @memset(mapStorageList.*, null);
} }
} }
@ -106,7 +106,7 @@ pub fn deinit() void {
clearList.deinit(); clearList.deinit();
} }
fn getNodeFromRenderThread(pos: chunk.ChunkPosition) *ChunkMeshNode { fn getNodePointer(pos: chunk.ChunkPosition) *ChunkMeshNode {
const lod = std.math.log2_int(u31, pos.voxelSize); const lod = std.math.log2_int(u31, pos.voxelSize);
var xIndex = pos.wx >> lod+chunk.chunkShift; var xIndex = pos.wx >> lod+chunk.chunkShift;
var yIndex = pos.wy >> 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)]; 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); const lod = std.math.log2_int(u31, voxelSize);
var xIndex = x >> lod+LightMap.LightMapFragment.mapShift; var xIndex = x >> lod+LightMap.LightMapFragment.mapShift;
var yIndex = y >> 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 { pub fn getLightMapPieceAndIncreaseRefCount(x: i32, y: i32, voxelSize: u31) ?*LightMap.LightMapFragment {
const result: *LightMap.LightMapFragment = getMapPieceLocation(x, y, voxelSize).load(.acquire) orelse return null; mutex.lock();
var refCount: u16 = 1; defer mutex.unlock();
while(result.refCount.cmpxchgWeak(refCount, refCount+1, .monotonic, .monotonic)) |otherVal| { const result: *LightMap.LightMapFragment = getMapPiecePointer(x, y, voxelSize).* orelse {
if(otherVal == 0) return null; return null;
refCount = otherVal; };
} result.increaseRefCount();
return result; return result;
} }
pub fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block { pub fn getBlock(x: i32, y: i32, z: i32) ?blocks.Block {
const node = getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}); const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize=1});
const mesh = node.mesh.load(.acquire) orelse return null; 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); const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
return block; 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; var lod: u5 = 0;
while(lod < settings.highestLOD) : (lod += 1) { while(lod < settings.highestLOD) : (lod += 1) {
const node = getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod}); const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod});
const mesh = node.mesh.load(.acquire) orelse continue; mutex.lock();
defer mutex.unlock();
const mesh = node.mesh orelse continue;
const block = mesh.chunk.getBlock(x & chunk.chunkMask<<lod, y & chunk.chunkMask<<lod, z & chunk.chunkMask<<lod); const block = mesh.chunk.getBlock(x & chunk.chunkMask<<lod, y & chunk.chunkMask<<lod, z & chunk.chunkMask<<lod);
return block; return block;
} }
return blocks.Block{.typ = 0, .data = 0}; return blocks.Block{.typ = 0, .data = 0};
} }
pub fn getMeshFromAnyLodFromRenderThread(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk_meshing.ChunkMesh {
var lod: u5 = @ctz(voxelSize);
while(lod < settings.highestLOD) : (lod += 1) {
const node = getNodeFromRenderThread(.{.wx = wx & ~chunk.chunkMask<<lod, .wy = wy & ~chunk.chunkMask<<lod, .wz = wz & ~chunk.chunkMask<<lod, .voxelSize=@as(u31, 1) << lod});
return node.mesh.load(.acquire) orelse continue;
}
return null;
}
pub fn getNeighborFromRenderThread(_pos: chunk.ChunkPosition, resolution: u31, neighbor: u3) ?*chunk_meshing.ChunkMesh {
var pos = _pos;
pos.wx +%= pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relX[neighbor];
pos.wy +%= pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relY[neighbor];
pos.wz +%= pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
pos.voxelSize = resolution;
const node = getNodeFromRenderThread(pos);
return node.mesh.load(.acquire);
}
pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk_meshing.ChunkMesh { pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk_meshing.ChunkMesh {
const node = getNodeFromRenderThread(pos);
const mesh = node.mesh.load(.acquire) orelse return null;
const lod = std.math.log2_int(u31, pos.voxelSize); const lod = std.math.log2_int(u31, pos.voxelSize);
const mask = ~((@as(i32, 1) << lod+chunk.chunkShift) - 1); const mask = ~((@as(i32, 1) << lod+chunk.chunkShift) - 1);
if(pos.wx & mask != mesh.pos.wx or pos.wy & mask != mesh.pos.wy or pos.wz & mask != mesh.pos.wz) return null; const node = getNodePointer(pos);
if(mesh.tryIncreaseRefCount()) { mutex.lock();
return mesh; const mesh = node.mesh orelse {
mutex.unlock();
return null;
};
mesh.increaseRefCount();
mutex.unlock();
if(pos.wx & mask != mesh.pos.wx or pos.wy & mask != mesh.pos.wy or pos.wz & mask != mesh.pos.wz) {
mesh.decreaseRefCount();
return null;
} }
return null; return mesh;
} }
pub fn getMeshFromAnyLodAndIncreaseRefCount(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk_meshing.ChunkMesh { pub fn getMeshFromAnyLodAndIncreaseRefCount(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk_meshing.ChunkMesh {
@ -327,9 +318,12 @@ fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: i32) void {
const index = (xIndex*storageSize + yIndex)*storageSize + zIndex; const index = (xIndex*storageSize + yIndex)*storageSize + zIndex;
const node = &storageLists[_lod][@intCast(index)]; const node = &storageLists[_lod][@intCast(index)];
if(node.mesh.load(.acquire)) |mesh| { mutex.lock();
const oldMesh = node.mesh;
node.mesh = null;
mutex.unlock();
if(oldMesh) |mesh| {
mesh.decreaseRefCount(); mesh.decreaseRefCount();
node.mesh.store(null, .release);
} }
} }
} }
@ -385,9 +379,12 @@ fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: i32) void {
const yIndex = @divExact(y, size) & storageMask; const yIndex = @divExact(y, size) & storageMask;
const index = xIndex*storageSize + yIndex; const index = xIndex*storageSize + yIndex;
const mapAtomic = &mapStorageLists[_lod][@intCast(index)]; const mapPointer = &mapStorageLists[_lod][@intCast(index)];
if(mapAtomic.load(.acquire)) |map| { mutex.lock();
mapAtomic.store(null, .release); const oldMap = mapPointer.*;
mapPointer.* = null;
mutex.unlock();
if(oldMap) |map| {
map.decreaseRefCount(); map.decreaseRefCount();
} }
} }
@ -460,11 +457,13 @@ fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: i32, meshR
const pos = chunk.ChunkPosition{.wx=x, .wy=y, .wz=z, .voxelSize=@as(u31, 1)<<lod}; const pos = chunk.ChunkPosition{.wx=x, .wy=y, .wz=z, .voxelSize=@as(u31, 1)<<lod};
const node = &storageLists[_lod][@intCast(index)]; const node = &storageLists[_lod][@intCast(index)];
if(node.mesh.load(.acquire)) |mesh| { mutex.lock();
if(node.mesh) |mesh| {
std.debug.assert(std.meta.eql(pos, mesh.pos)); std.debug.assert(std.meta.eql(pos, mesh.pos));
} else { } else {
meshRequests.append(pos); meshRequests.append(pos);
} }
mutex.unlock();
} }
} }
} }
@ -521,11 +520,13 @@ fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: i32, meshR
const pos = LightMap.MapFragmentPosition{.wx=x, .wy=y, .voxelSize=@as(u31, 1)<<lod, .voxelSizeShift = lod}; const pos = LightMap.MapFragmentPosition{.wx=x, .wy=y, .voxelSize=@as(u31, 1)<<lod, .voxelSizeShift = lod};
const node = &mapStorageLists[_lod][@intCast(index)]; const node = &mapStorageLists[_lod][@intCast(index)];
if(node.load(.acquire)) |map| { mutex.lock();
if(node.*) |map| {
std.debug.assert(std.meta.eql(pos, map.pos)); std.debug.assert(std.meta.eql(pos, map.pos));
} else { } else {
mapRequests.append(pos); mapRequests.append(pos);
} }
mutex.unlock();
} }
} }
} }
@ -588,8 +589,11 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
firstPos.wz &= ~@as(i32, chunk.chunkMask); firstPos.wz &= ~@as(i32, chunk.chunkMask);
var lod: u3 = 0; var lod: u3 = 0;
while(lod <= settings.highestLOD) : (lod += 1) { while(lod <= settings.highestLOD) : (lod += 1) {
const node = getNodeFromRenderThread(firstPos); const node = getNodePointer(firstPos);
if(node.mesh.load(.acquire) != null and node.mesh.load(.acquire).?.finishedMeshing) { mutex.lock();
const hasMesh = node.mesh != null and node.mesh.?.finishedMeshing;
mutex.unlock();
if(hasMesh) {
node.lod = lod; node.lod = lod;
node.min = @splat(-1); node.min = @splat(-1);
node.max = @splat(1); node.max = @splat(1);
@ -613,8 +617,20 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
while(searchList.removeOrNull()) |data| { while(searchList.removeOrNull()) |data| {
nodeList.append(data.node); nodeList.append(data.node);
data.node.active = false; data.node.active = false;
const mesh = data.node.mesh.load(.acquire).?;
std.debug.assert(mesh.finishedMeshing); mutex.lock();
const mesh = data.node.mesh orelse {
mutex.unlock();
continue;
};
if(!mesh.finishedMeshing) {
mutex.unlock();
continue;
}
mesh.increaseRefCount();
defer mesh.decreaseRefCount();
mutex.unlock();
mesh.visibilityMask = 0xff; mesh.visibilityMask = 0xff;
const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos; const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos;
const relPosFloat: Vec3f = @floatCast(relPos); const relPosFloat: Vec3f = @floatCast(relPos);
@ -770,8 +786,10 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
neighborPos.wz &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize); neighborPos.wz &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.voxelSize *= 2; neighborPos.voxelSize *= 2;
} }
const node = getNodeFromRenderThread(neighborPos); const node = getNodePointer(neighborPos);
if(node.mesh.load(.acquire)) |neighborMesh| { if(getMeshAndIncreaseRefCount(neighborPos)) |neighborMesh| {
std.debug.assert(std.meta.eql(neighborPos, neighborMesh.pos));
defer neighborMesh.decreaseRefCount();
if(!neighborMesh.finishedMeshing) continue; if(!neighborMesh.finishedMeshing) continue;
// Ensure that there are no high-to-low lod transitions, which would produce cracks. // Ensure that there are no high-to-low lod transitions, which would produce cracks.
if(lod == data.node.lod and lod != settings.highestLOD and !node.rendered) { if(lod == data.node.lod and lod != settings.highestLOD and !node.rendered) {
@ -788,7 +806,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
.wz = neighborPos.wz + chunk.Neighbors.relZ[neighbor2]*chunk.chunkSize*neighborPos.voxelSize, .wz = neighborPos.wz + chunk.Neighbors.relZ[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.voxelSize = neighborPos.voxelSize, .voxelSize = neighborPos.voxelSize,
}; };
const node2 = getNodeFromRenderThread(neighborPos2); const node2 = getNodePointer(neighborPos2);
if(node2.rendered) { if(node2.rendered) {
continue; continue;
} }
@ -800,7 +818,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
.wz = neighborPos.wz + chunk.Neighbors.relZ[neighbor2]*chunk.chunkSize*neighborPos.voxelSize, .wz = neighborPos.wz + chunk.Neighbors.relZ[neighbor2]*chunk.chunkSize*neighborPos.voxelSize,
.voxelSize = neighborPos.voxelSize << 1, .voxelSize = neighborPos.voxelSize << 1,
}; };
const node2 = getNodeFromRenderThread(neighborPos2); const node2 = getNodePointer(neighborPos2);
if(node2.rendered) { if(node2.rendered) {
isValid = false; isValid = false;
break; break;
@ -837,10 +855,25 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
} }
for(nodeList.items) |node| { for(nodeList.items) |node| {
node.rendered = false; node.rendered = false;
const mesh = node.mesh.load(.acquire).?;
mutex.lock();
const mesh = node.mesh orelse {
mutex.unlock();
continue;
};
if(!mesh.finishedMeshing) {
mutex.unlock();
continue;
}
mesh.increaseRefCount();
defer mesh.decreaseRefCount();
mutex.unlock();
if(mesh.pos.voxelSize != @as(u31, 1) << settings.highestLOD) { if(mesh.pos.voxelSize != @as(u31, 1) << settings.highestLOD) {
const parent = getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1}); const parent = getNodePointer(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1});
if(parent.mesh.load(.acquire)) |parentMesh| { mutex.lock();
defer mutex.unlock();
if(parent.mesh) |parentMesh| {
const sizeShift = chunk.chunkShift + @ctz(mesh.pos.voxelSize); const sizeShift = chunk.chunkShift + @ctz(mesh.pos.voxelSize);
const octantIndex: u3 = @intCast((mesh.pos.wx>>sizeShift & 1) | (mesh.pos.wy>>sizeShift & 1)<<1 | (mesh.pos.wz>>sizeShift & 1)<<2); const octantIndex: u3 = @intCast((mesh.pos.wx>>sizeShift & 1) | (mesh.pos.wy>>sizeShift & 1)<<1 | (mesh.pos.wz>>sizeShift & 1)<<2);
parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex); parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex);
@ -867,8 +900,8 @@ pub fn updateMeshes(targetTime: i64) void {
defer blockUpdateMutex.unlock(); defer blockUpdateMutex.unlock();
for(blockUpdateList.items) |blockUpdate| { for(blockUpdateList.items) |blockUpdate| {
const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1}; const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1};
const node = getNodeFromRenderThread(pos); if(getMeshAndIncreaseRefCount(pos)) |mesh| {
if(node.mesh.load(.acquire)) |mesh| { defer mesh.decreaseRefCount();
mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock); 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. } // 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; continue;
} }
mesh.needsMeshUpdate = false; mesh.needsMeshUpdate = false;
if(getNodePointer(mesh.pos).mesh != mesh) {
mutex.unlock();
mesh.decreaseRefCount();
continue;
}
mutex.unlock(); mutex.unlock();
defer mutex.lock(); defer mutex.lock();
mesh.decreaseRefCount(); mesh.decreaseRefCount();
if(getNodeFromRenderThread(mesh.pos).mesh.load(.acquire) != mesh) continue; // This mesh isn't used for rendering anymore.
mesh.uploadData(); mesh.uploadData();
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh. 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)) { if(!isMapInRenderDistance(map.pos)) {
map.decreaseRefCount(); map.decreaseRefCount();
} else { } 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(); old.decreaseRefCount();
} }
mapPointer.* = map;
} }
} }
while(updatableList.items.len != 0) { while(updatableList.items.len != 0) {
@ -935,11 +974,15 @@ pub fn updateMeshes(targetTime: i64) void {
mutex.unlock(); mutex.unlock();
defer mutex.lock(); defer mutex.lock();
if(isInRenderDistance(mesh.pos)) { if(isInRenderDistance(mesh.pos)) {
const node = getNodeFromRenderThread(mesh.pos); const node = getNodePointer(mesh.pos);
mesh.finishedMeshing = true; mesh.finishedMeshing = true;
mesh.uploadData(); mesh.uploadData();
if(node.mesh.swap(mesh, .acq_rel)) |oldMesh| { mutex.lock();
oldMesh.decreaseRefCount(); const oldMesh = node.mesh;
node.mesh = mesh;
mutex.unlock();
if(oldMesh) |_oldMesh| {
_oldMesh.decreaseRefCount();
} }
} else { } else {
mesh.decreaseRefCount(); mesh.decreaseRefCount();
@ -973,12 +1016,12 @@ pub fn addMeshToStorage(mesh: *chunk_meshing.ChunkMesh) error{AlreadyStored}!voi
mutex.lock(); mutex.lock();
defer mutex.unlock(); defer mutex.unlock();
if(isInRenderDistance(mesh.pos)) { if(isInRenderDistance(mesh.pos)) {
const node = getNodeFromRenderThread(mesh.pos); const node = getNodePointer(mesh.pos);
if(node.mesh.cmpxchgStrong(null, mesh, .acq_rel, .monotonic) != null) { if(node.mesh != null) {
return error.AlreadyStored; return error.AlreadyStored;
} else {
mesh.increaseRefCount();
} }
node.mesh = mesh;
mesh.increaseRefCount();
} }
} }

View File

@ -50,6 +50,19 @@ pub const CaveBiomeMapFragment = struct {
relZ >>= caveBiomeShift; relZ >>= caveBiomeShift;
return relX << 2*(caveBiomeMapShift - caveBiomeShift) | relY << caveBiomeMapShift-caveBiomeShift | relZ; 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. /// A generator for the cave biome map.
@ -105,20 +118,20 @@ pub const InterpolatableCaveBiomeMapView = struct {
pub fn init(pos: ChunkPosition, width: i32) InterpolatableCaveBiomeMapView { pub fn init(pos: ChunkPosition, width: i32) InterpolatableCaveBiomeMapView {
return InterpolatableCaveBiomeMapView { return InterpolatableCaveBiomeMapView {
.fragments = [_]*CaveBiomeMapFragment { .fragments = [_]*CaveBiomeMapFragment {
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),
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),
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),
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),
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),
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),
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),
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),
}, },
.surfaceFragments = [_]*MapFragment { .surfaceFragments = [_]*MapFragment {
SurfaceMap.getOrGenerateFragment(pos.wx -% 32, pos.wy -% 32, pos.voxelSize), SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% 32, pos.wy -% 32, pos.voxelSize),
SurfaceMap.getOrGenerateFragment(pos.wx -% 32, pos.wy +% width +% 32, pos.voxelSize), SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx -% 32, pos.wy +% width +% 32, pos.voxelSize),
SurfaceMap.getOrGenerateFragment(pos.wx +% width +% 32, pos.wy -% 32, pos.voxelSize), SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(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 +% width +% 32, pos.wy +% width +% 32, pos.voxelSize),
}, },
.pos = pos, .pos = pos,
.width = width, .width = width,
@ -127,10 +140,10 @@ pub const InterpolatableCaveBiomeMapView = struct {
pub fn deinit(self: InterpolatableCaveBiomeMapView) void { pub fn deinit(self: InterpolatableCaveBiomeMapView) void {
for(self.fragments) |mapFragment| { for(self.fragments) |mapFragment| {
mapFragmentDeinit(mapFragment); mapFragment.decreaseRefCount();
} }
for(self.surfaceFragments) |mapFragment| { 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 cacheSize = 1 << 8; // Must be a power of 2!
const cacheMask = cacheSize - 1; const cacheMask = cacheSize - 1;
const associativity = 8; const associativity = 8;
var cache: Cache(CaveBiomeMapFragment, cacheSize, associativity, mapFragmentDeinit) = .{}; var cache: Cache(CaveBiomeMapFragment, cacheSize, associativity, CaveBiomeMapFragment.decreaseRefCount) = .{};
var profile: TerrainGenerationProfile = undefined; var profile: TerrainGenerationProfile = undefined;
@ -457,12 +470,6 @@ pub fn deinit() void {
cache.clear(); 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 { fn cacheInit(pos: ChunkPosition) *CaveBiomeMapFragment {
const mapFragment = main.globalAllocator.create(CaveBiomeMapFragment); const mapFragment = main.globalAllocator.create(CaveBiomeMapFragment);
mapFragment.init(pos.wx, pos.wy, pos.wz); mapFragment.init(pos.wx, pos.wy, pos.wz);
@ -473,7 +480,7 @@ fn cacheInit(pos: ChunkPosition) *CaveBiomeMapFragment {
return mapFragment; 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 wx = _wx & ~@as(i32, CaveBiomeMapFragment.caveBiomeMapMask);
const wy = _wy & ~@as(i32, CaveBiomeMapFragment.caveBiomeMapMask); const wy = _wy & ~@as(i32, CaveBiomeMapFragment.caveBiomeMapMask);
const wz = _wz & ~@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, .wx = wx, .wy = wy, .wz = wz,
.voxelSize = CaveBiomeMapFragment.caveBiomeSize, .voxelSize = CaveBiomeMapFragment.caveBiomeSize,
}; };
const result = cache.findOrCreate(compare, cacheInit); const result = cache.findOrCreate(compare, cacheInit, CaveBiomeMapFragment.increaseRefCount);
std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0);
return result; return result;
} }

View File

@ -61,6 +61,19 @@ pub const CaveMapFragment = struct {
return maskLower | maskUpper; 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 { pub fn addRange(self: *CaveMapFragment, _relX: i32, _relY: i32, _start: i32, _end: i32) void {
const relX = _relX >> self.voxelShift; const relX = _relX >> self.voxelShift;
const relY = _relY >> self.voxelShift; const relY = _relY >> self.voxelShift;
@ -135,21 +148,21 @@ pub const CaveMapView = struct {
return CaveMapView { return CaveMapView {
.reference = chunk, .reference = chunk,
.fragments = [_]*CaveMapFragment { .fragments = [_]*CaveMapFragment {
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),
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),
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),
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),
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),
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),
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),
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),
}, },
}; };
} }
pub fn deinit(self: CaveMapView) void { pub fn deinit(self: CaveMapView) void {
for(self.fragments) |mapFragment| { 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 cacheSize = 1 << 9; // Must be a power of 2!
const cacheMask = cacheSize - 1; const cacheMask = cacheSize - 1;
const associativity = 8; // 512 MiB Cache size 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; 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 { fn cacheInit(pos: ChunkPosition) *CaveMapFragment {
const mapFragment = main.globalAllocator.create(CaveMapFragment); const mapFragment = main.globalAllocator.create(CaveMapFragment);
mapFragment.init(pos.wx, pos.wy, pos.wz, pos.voxelSize); mapFragment.init(pos.wx, pos.wy, pos.wz, pos.voxelSize);
@ -323,14 +330,13 @@ pub fn deinit() void {
cache.clear(); 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 { const compare = ChunkPosition {
.wx = wx & ~@as(i32, CaveMapFragment.widthMask*voxelSize | voxelSize-1), .wx = wx & ~@as(i32, CaveMapFragment.widthMask*voxelSize | voxelSize-1),
.wy = wy & ~@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), .wz = wz & ~@as(i32, CaveMapFragment.heightMask*voxelSize | voxelSize-1),
.voxelSize = voxelSize, .voxelSize = voxelSize,
}; };
const result = cache.findOrCreate(compare, cacheInit); const result = cache.findOrCreate(compare, cacheInit, CaveMapFragment.increaseRefCount);
std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0);
return result; return result;
} }

View File

@ -60,6 +60,19 @@ pub const ClimateMapFragment = struct {
pub fn hashCode(wx: i32, wy: i32) u32 { pub fn hashCode(wx: i32, wy: i32) u32 {
return @bitCast((wx >> mapShift)*%33 + (wy >> mapShift)); 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. /// 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 cacheSize = 1 << 8; // Must be a power of 2!
const cacheMask = cacheSize - 1; const cacheMask = cacheSize - 1;
const associativity = 4; const associativity = 4;
var cache: Cache(ClimateMapFragment, cacheSize, associativity, mapFragmentDeinit) = .{}; var cache: Cache(ClimateMapFragment, cacheSize, associativity, ClimateMapFragment.decreaseRefCount) = .{};
var profile: TerrainGenerationProfile = undefined; var profile: TerrainGenerationProfile = undefined;
pub fn initGenerators() void { pub fn initGenerators() void {
@ -105,12 +118,6 @@ pub fn deinitGenerators() void {
ClimateMapGenerator.generatorRegistry.clearAndFree(main.globalAllocator.allocator); 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 { fn cacheInit(pos: ClimateMapFragmentPosition) *ClimateMapFragment {
const mapFragment = main.globalAllocator.create(ClimateMapFragment); const mapFragment = main.globalAllocator.create(ClimateMapFragment);
mapFragment.init(pos.wx, pos.wy); mapFragment.init(pos.wx, pos.wy);
@ -127,11 +134,9 @@ pub fn deinit() void {
cache.clear(); cache.clear();
} }
/// Call deinit on the result. fn getOrGenerateFragmentAndIncreaseRefCount(wx: i32, wy: i32) *ClimateMapFragment {
fn getOrGenerateFragment(wx: i32, wy: i32) *ClimateMapFragment {
const compare = ClimateMapFragmentPosition{.wx = wx, .wy = wy}; const compare = ClimateMapFragmentPosition{.wx = wx, .wy = wy};
const result = cache.findOrCreate(compare, cacheInit); const result = cache.findOrCreate(compare, cacheInit, ClimateMapFragment.increaseRefCount);
std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0);
return result; return result;
} }
@ -145,8 +150,8 @@ pub fn getBiomeMap(allocator: NeverFailingAllocator, wx: i32, wy: i32, width: u3
while(wxEnd -% x >= 0) : (x +%= ClimateMapFragment.mapSize) { while(wxEnd -% x >= 0) : (x +%= ClimateMapFragment.mapSize) {
var y = wzStart; var y = wzStart;
while(wzEnd -% y >= 0) : (y +%= ClimateMapFragment.mapSize) { while(wzEnd -% y >= 0) : (y +%= ClimateMapFragment.mapSize) {
const mapPiece = getOrGenerateFragment(x, y); const mapPiece = getOrGenerateFragmentAndIncreaseRefCount(x, y);
defer mapFragmentDeinit(mapPiece); defer mapPiece.decreaseRefCount();
// Offset of the indices in the result map: // Offset of the indices in the result map:
const xOffset = (x -% wx) >> MapFragment.biomeShift; const xOffset = (x -% wx) >> MapFragment.biomeShift;
const yOffset = (y -% wy) >> MapFragment.biomeShift; const yOffset = (y -% wy) >> MapFragment.biomeShift;

View File

@ -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 { 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); main.globalAllocator.destroy(self);
} }
} }
@ -51,8 +58,8 @@ var cache: Cache(LightMapFragment, cacheSize, associativity, LightMapFragment.de
fn cacheInit(pos: MapFragmentPosition) *LightMapFragment { fn cacheInit(pos: MapFragmentPosition) *LightMapFragment {
const mapFragment = main.globalAllocator.create(LightMapFragment); const mapFragment = main.globalAllocator.create(LightMapFragment);
mapFragment.init(pos.wx, pos.wy, pos.voxelSize); mapFragment.init(pos.wx, pos.wy, pos.voxelSize);
const surfaceMap = terrain.SurfaceMap.getOrGenerateFragment(pos.wx, pos.wy, pos.voxelSize); const surfaceMap = terrain.SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(pos.wx, pos.wy, pos.voxelSize);
defer surfaceMap.deinit(); defer surfaceMap.decreaseRefCount();
comptime std.debug.assert(LightMapFragment.mapSize == terrain.SurfaceMap.MapFragment.mapSize); comptime std.debug.assert(LightMapFragment.mapSize == terrain.SurfaceMap.MapFragment.mapSize);
for(0..LightMapFragment.mapSize) |x| { for(0..LightMapFragment.mapSize) |x| {
for(0..LightMapFragment.mapSize) |y| { for(0..LightMapFragment.mapSize) |y| {
@ -68,14 +75,12 @@ pub fn deinit() void {
cache.clear(); cache.clear();
} }
/// Call decreaseRefCount on the result. pub fn getOrGenerateFragmentAndIncreaseRefCount(wx: i32, wy: i32, voxelSize: u31) *LightMapFragment {
pub fn getOrGenerateFragment(wx: i32, wy: i32, voxelSize: u31) *LightMapFragment {
const compare = MapFragmentPosition.init( const compare = MapFragmentPosition.init(
wx & ~@as(i32, LightMapFragment.mapMask*voxelSize | voxelSize-1), wx & ~@as(i32, LightMapFragment.mapMask*voxelSize | voxelSize-1),
wy & ~@as(i32, LightMapFragment.mapMask*voxelSize | voxelSize-1), wy & ~@as(i32, LightMapFragment.mapMask*voxelSize | voxelSize-1),
voxelSize voxelSize
); );
const result = cache.findOrCreate(compare, cacheInit); const result = cache.findOrCreate(compare, cacheInit, LightMapFragment.increaseRefCount);
std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0);
return result; return result;
} }

View File

@ -78,8 +78,17 @@ pub const MapFragment = struct {
}; };
} }
pub fn deinit(self: *MapFragment) void { pub fn increaseRefCount(self: *MapFragment) void {
mapFragmentDeinit(self); 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 { 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 cacheSize = 1 << 6; // Must be a power of 2!
const cacheMask = cacheSize - 1; const cacheMask = cacheSize - 1;
const associativity = 8; // ~400MiB MiB Cache size 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; var profile: TerrainGenerationProfile = undefined;
pub fn initGenerators() void { pub fn initGenerators() void {
@ -138,12 +147,6 @@ pub fn deinitGenerators() void {
MapGenerator.generatorRegistry.clearAndFree(main.globalAllocator.allocator); 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 { fn cacheInit(pos: MapFragmentPosition) *MapFragment {
const mapFragment = main.globalAllocator.create(MapFragment); const mapFragment = main.globalAllocator.create(MapFragment);
mapFragment.init(pos.wx, pos.wy, pos.voxelSize); mapFragment.init(pos.wx, pos.wy, pos.voxelSize);
@ -161,13 +164,12 @@ pub fn deinit() void {
} }
/// Call deinit on the result. /// 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( const compare = MapFragmentPosition.init(
wx & ~@as(i32, MapFragment.mapMask*voxelSize | voxelSize-1), wx & ~@as(i32, MapFragment.mapMask*voxelSize | voxelSize-1),
wy & ~@as(i32, MapFragment.mapMask*voxelSize | voxelSize-1), wy & ~@as(i32, MapFragment.mapMask*voxelSize | voxelSize-1),
voxelSize voxelSize
); );
const result = cache.findOrCreate(compare, cacheInit); const result = cache.findOrCreate(compare, cacheInit, MapFragment.increaseRefCount);
std.debug.assert(@atomicRmw(u16, &result.refCount.raw, .Add, 1, .monotonic) != 0);
return result; return result;
} }

View File

@ -127,7 +127,7 @@ const ChunkManager = struct {
pub fn run(self: *LightMapLoadTask) void { pub fn run(self: *LightMapLoadTask) void {
defer self.clean(); 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(); defer map.decreaseRefCount();
if(self.source) |source| { if(self.source) |source| {
main.network.Protocols.lightMapTransmission.sendLightMap(source.conn, map); main.network.Protocols.lightMapTransmission.sendLightMap(source.conn, map);
@ -204,8 +204,8 @@ const ChunkManager = struct {
// TODO: Store chunk. // TODO: Store chunk.
} }
/// Generates a normal chunk at a given location, or if possible gets it from the cache. /// Generates a normal chunk at a given location, or if possible gets it from the cache.
pub fn getOrGenerateChunk(pos: ChunkPosition) *Chunk { 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); return chunkCache.findOrCreate(pos, chunkInitFunctionForCache, null);
} }
pub fn getChunkFromCache(pos: ChunkPosition) ?*Chunk { pub fn getChunkFromCache(pos: ChunkPosition) ?*Chunk {
@ -368,8 +368,8 @@ pub const ServerWorld = struct {
std.log.info("Trying ({}, {})", .{self.spawn[0], self.spawn[1]}); std.log.info("Trying ({}, {})", .{self.spawn[0], self.spawn[1]});
if(self.isValidSpawnLocation(self.spawn[0], self.spawn[1])) break; if(self.isValidSpawnLocation(self.spawn[0], self.spawn[1])) break;
} }
const map = terrain.SurfaceMap.getOrGenerateFragment(self.spawn[0], self.spawn[1], 1); const map = terrain.SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(self.spawn[0], self.spawn[1], 1);
defer map.deinit(); defer map.decreaseRefCount();
self.spawn[2] = @intFromFloat(map.getHeight(self.spawn[0], self.spawn[1]) + 1); self.spawn[2] = @intFromFloat(map.getHeight(self.spawn[0], self.spawn[1]) + 1);
} }
self.generated = true; self.generated = true;
@ -401,8 +401,8 @@ pub const ServerWorld = struct {
} }
fn isValidSpawnLocation(_: *ServerWorld, wx: i32, wy: i32) bool { fn isValidSpawnLocation(_: *ServerWorld, wx: i32, wy: i32) bool {
const map = terrain.SurfaceMap.getOrGenerateFragment(wx, wy, 1); const map = terrain.SurfaceMap.getOrGenerateFragmentAndIncreaseRefCount(wx, wy, 1);
defer map.deinit(); defer map.decreaseRefCount();
return map.getBiome(wx, wy).isValidPlayerSpawn; return map.getBiome(wx, wy).isValidPlayerSpawn;
} }

View File

@ -1387,12 +1387,13 @@ pub fn Cache(comptime T: type, comptime numberOfBuckets: u32, comptime bucketSiz
cacheMisses: Atomic(usize) = Atomic(usize).init(0), cacheMisses: Atomic(usize) = Atomic(usize).init(0),
/// Tries to find the entry that fits to the supplied hashable. /// 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; const index: u32 = compareAndHash.hashCode() & hashMask;
_ = @atomicRmw(usize, &self.cacheRequests.raw, .Add, 1, .monotonic); _ = @atomicRmw(usize, &self.cacheRequests.raw, .Add, 1, .monotonic);
self.buckets[index].mutex.lock(); self.buckets[index].mutex.lock();
defer self.buckets[index].mutex.unlock(); defer self.buckets[index].mutex.unlock();
if(self.buckets[index].find(compareAndHash)) |item| { if(self.buckets[index].find(compareAndHash)) |item| {
if(postGetFunction) |fun| fun(item);
return item; return item;
} }
_ = @atomicRmw(usize, &self.cacheMisses.raw, .Add, 1, .monotonic); _ = @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); 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; const index: u32 = compareAndHash.hashCode() & hashMask;
self.buckets[index].mutex.lock(); self.buckets[index].mutex.lock();
defer self.buckets[index].mutex.unlock(); 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;
} }
}; };
} }