Prepare some interfaces for multithreaded mesh access.

This commit is contained in:
IntegratedQuantum 2023-11-26 17:07:21 +01:00
parent a6248dc5c2
commit 410db62d85
2 changed files with 38 additions and 20 deletions

View File

@ -814,6 +814,15 @@ pub const meshing = struct {
std.debug.assert(prevVal != 0); std.debug.assert(prevVal != 0);
} }
/// In cases where it's not certain whether the thing was cleared already.
pub fn tryIncreaseRefCount(self: *ChunkMesh) bool {
const prevVal = self.refCount.load(.Monotonic);
while(prevVal != 0) {
prevVal = self.refCount.compareAndSwap(prevVal, prevVal + 1, .Monotonic, .Monotonic) orelse return true;
}
return false;
}
pub fn decreaseRefCount(self: *ChunkMesh) !void { pub fn decreaseRefCount(self: *ChunkMesh) !void {
const prevVal = self.refCount.fetchSub(1, .Monotonic); const prevVal = self.refCount.fetchSub(1, .Monotonic);
std.debug.assert(prevVal != 0); std.debug.assert(prevVal != 0);

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Atomic = std.atomic.Atomic;
const blocks = @import("blocks.zig"); const blocks = @import("blocks.zig");
const chunk = @import("chunk.zig"); const chunk = @import("chunk.zig");
@ -869,7 +870,7 @@ pub const MeshSelection = struct {
pub const RenderStructure = struct { pub const RenderStructure = struct {
const ChunkMeshNode = struct { const ChunkMeshNode = struct {
mesh: ?*chunk.meshing.ChunkMesh, mesh: Atomic(?*chunk.meshing.ChunkMesh),
lod: u3, lod: u3,
min: Vec2f, min: Vec2f,
max: Vec2f, max: Vec2f,
@ -903,7 +904,7 @@ pub const RenderStructure = struct {
for(&storageLists) |*storageList| { for(&storageLists) |*storageList| {
storageList.* = try main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode); storageList.* = try main.globalAllocator.create([storageSize*storageSize*storageSize]ChunkMeshNode);
for(storageList.*) |*val| { for(storageList.*) |*val| {
val.mesh = null; val.mesh = Atomic(?*chunk.meshing.ChunkMesh).init(null);
val.rendered = false; val.rendered = false;
} }
} }
@ -947,7 +948,7 @@ pub const RenderStructure = struct {
fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block { fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block {
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}); const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1});
const mesh = node.mesh orelse return null; const mesh = node.mesh.load(.Unordered) 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;
} }
@ -956,7 +957,7 @@ pub const RenderStructure = struct {
var lod: u5 = 0; var lod: u5 = 0;
while(lod < settings.highestLOD) : (lod += 1) { while(lod < settings.highestLOD) : (lod += 1) {
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod}); const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod});
const mesh = node.mesh orelse continue; const mesh = node.mesh.load(.Unordered) 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;
} }
@ -967,7 +968,7 @@ pub const RenderStructure = struct {
var lod: u5 = @ctz(voxelSize); var lod: u5 = @ctz(voxelSize);
while(lod < settings.highestLOD) : (lod += 1) { while(lod < settings.highestLOD) : (lod += 1) {
const node = RenderStructure.getNodeFromRenderThread(.{.wx = wx & ~chunk.chunkMask<<lod, .wy = wy & ~chunk.chunkMask<<lod, .wz = wz & ~chunk.chunkMask<<lod, .voxelSize=@as(u31, 1) << lod}); const node = RenderStructure.getNodeFromRenderThread(.{.wx = wx & ~chunk.chunkMask<<lod, .wy = wy & ~chunk.chunkMask<<lod, .wz = wz & ~chunk.chunkMask<<lod, .voxelSize=@as(u31, 1) << lod});
return node.mesh orelse continue; return node.mesh.load(.Unordered) orelse continue;
} }
return null; return null;
} }
@ -979,7 +980,16 @@ pub const RenderStructure = struct {
pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor]; pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
pos.voxelSize = resolution; pos.voxelSize = resolution;
const node = getNodeFromRenderThread(pos); const node = getNodeFromRenderThread(pos);
return node.mesh; return node.mesh.load(.Unordered);
}
pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk.meshing.ChunkMesh {
const node = RenderStructure.getNodeFromRenderThread(pos);
const mesh = node.mesh.load(.Unordered) orelse return null;
if(mesh.tryIncreaseRefCount()) {
return mesh;
}
return null;
} }
fn reduceRenderDistance(fullRenderDistance: i64, reduction: i64) i32 { fn reduceRenderDistance(fullRenderDistance: i64, reduction: i64) i32 {
@ -1083,10 +1093,10 @@ pub const RenderStructure = struct {
const node = &storageLists[_lod][@intCast(index)]; const node = &storageLists[_lod][@intCast(index)];
// Update the neighbors, so we don't get cracks when we look back: // Update the neighbors, so we don't get cracks when we look back:
if(node.mesh) |mesh| { if(node.mesh.load(.Unordered)) |mesh| {
const pos = mesh.pos; const pos = mesh.pos;
try mesh.decreaseRefCount(); try mesh.decreaseRefCount();
node.mesh = null; node.mesh.store(null, .Unordered);
if(renderDistance != 0) { if(renderDistance != 0) {
for(chunk.Neighbors.iterable) |neighbor| { for(chunk.Neighbors.iterable) |neighbor| {
if(getNeighborFromRenderThread(pos, pos.voxelSize, neighbor)) |neighborMesh| { if(getNeighborFromRenderThread(pos, pos.voxelSize, neighbor)) |neighborMesh| {
@ -1168,7 +1178,7 @@ pub const RenderStructure = struct {
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)];
std.debug.assert(node.mesh == null); std.debug.assert(node.mesh.load(.Unordered) == null);
try meshRequests.append(pos); try meshRequests.append(pos);
} }
} }
@ -1220,7 +1230,7 @@ pub const RenderStructure = struct {
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 = getNodeFromRenderThread(firstPos);
if(node.mesh != null) { if(node.mesh.load(.Unordered) != null) {
node.lod = lod; node.lod = lod;
node.min = @splat(-1); node.min = @splat(-1);
node.max = @splat(1); node.max = @splat(1);
@ -1244,7 +1254,7 @@ pub const RenderStructure = struct {
while(searchList.removeOrNull()) |data| { while(searchList.removeOrNull()) |data| {
try nodeList.append(data.node); try nodeList.append(data.node);
data.node.active = false; data.node.active = false;
const mesh = data.node.mesh.?; const mesh = data.node.mesh.load(.Unordered).?;
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);
@ -1401,14 +1411,14 @@ pub const RenderStructure = struct {
neighborPos.voxelSize *= 2; neighborPos.voxelSize *= 2;
} }
const node = getNodeFromRenderThread(neighborPos); const node = getNodeFromRenderThread(neighborPos);
if(node.mesh != null) { if(node.mesh.load(.Unordered)) |neighborMesh| {
// 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) {
var isValid: bool = true; var isValid: bool = true;
const relPos2: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{neighborPos.wx, neighborPos.wy, neighborPos.wz})) - playerPos; const relPos2: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{neighborPos.wx, neighborPos.wy, neighborPos.wz})) - playerPos;
for(chunk.Neighbors.iterable) |neighbor2| { for(chunk.Neighbors.iterable) |neighbor2| {
const component2 = chunk.Neighbors.extractDirectionComponent(neighbor2, relPos2); const component2 = chunk.Neighbors.extractDirectionComponent(neighbor2, relPos2);
if(chunk.Neighbors.isPositive[neighbor2] and component2 + @as(f64, @floatFromInt(chunk.chunkSize*node.mesh.?.pos.voxelSize)) >= 0) continue; if(chunk.Neighbors.isPositive[neighbor2] and component2 + @as(f64, @floatFromInt(chunk.chunkSize*neighborMesh.pos.voxelSize)) >= 0) continue;
if(!chunk.Neighbors.isPositive[neighbor2] and component2 <= 0) continue; if(!chunk.Neighbors.isPositive[neighbor2] and component2 <= 0) continue;
{ // Check the chunk of same lod: { // Check the chunk of same lod:
const neighborPos2 = chunk.ChunkPosition{ const neighborPos2 = chunk.ChunkPosition{
@ -1451,7 +1461,7 @@ pub const RenderStructure = struct {
node.active = true; node.active = true;
try searchList.add(.{ try searchList.add(.{
.node = node, .node = node,
.distance = node.mesh.?.pos.getMaxDistanceSquared(playerPos), .distance = neighborMesh.pos.getMaxDistanceSquared(playerPos),
}); });
node.rendered = true; node.rendered = true;
} }
@ -1463,10 +1473,10 @@ pub const RenderStructure = struct {
} }
for(nodeList.items) |node| { for(nodeList.items) |node| {
node.rendered = false; node.rendered = false;
const mesh = node.mesh.?; const mesh = node.mesh.load(.Unordered).?;
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 = getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1});
if(parent.mesh) |parentMesh| { if(parent.mesh.load(.Unordered)) |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);
@ -1498,7 +1508,7 @@ pub const RenderStructure = struct {
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); const node = getNodeFromRenderThread(pos);
if(node.mesh) |mesh| { if(node.mesh.load(.Unordered)) |mesh| {
try mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock); try 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.
} }
@ -1516,7 +1526,7 @@ pub const RenderStructure = struct {
mutex.unlock(); mutex.unlock();
defer mutex.lock(); defer mutex.lock();
try mesh.decreaseRefCount(); try mesh.decreaseRefCount();
if(getNodeFromRenderThread(mesh.pos).mesh != mesh) continue; // This mesh isn't used for rendering anymore. if(getNodeFromRenderThread(mesh.pos).mesh.load(.Unordered) != mesh) continue; // This mesh isn't used for rendering anymore.
if(!mesh.needsNeighborUpdate) continue; if(!mesh.needsNeighborUpdate) continue;
try mesh.uploadDataAndFinishNeighbors(); try mesh.uploadDataAndFinishNeighbors();
mesh.needsNeighborUpdate = false; mesh.needsNeighborUpdate = false;
@ -1553,10 +1563,9 @@ pub const RenderStructure = struct {
if(isInRenderDistance(mesh.pos)) { if(isInRenderDistance(mesh.pos)) {
const node = getNodeFromRenderThread(mesh.pos); const node = getNodeFromRenderThread(mesh.pos);
try mesh.uploadDataAndFinishNeighbors(); try mesh.uploadDataAndFinishNeighbors();
if(node.mesh) |oldMesh| { if(node.mesh.swap(mesh, .Monotonic)) |oldMesh| {
try oldMesh.decreaseRefCount(); try oldMesh.decreaseRefCount();
} }
node.mesh = mesh;
} else { } else {
try mesh.decreaseRefCount(); try mesh.decreaseRefCount();
} }