Some small performance improvements to the CPU-side mesh traversal.

Should help implementing #526
This commit is contained in:
IntegratedQuantum 2024-09-15 13:02:02 +02:00
parent bfea4d9105
commit d7055f6998
2 changed files with 17 additions and 29 deletions

View File

@ -687,7 +687,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
refCount: std.atomic.Value(u32) = .init(1), refCount: std.atomic.Value(u32) = .init(1),
needsLightRefresh: std.atomic.Value(bool) = .init(false), needsLightRefresh: std.atomic.Value(bool) = .init(false),
needsMeshUpdate: bool = false, needsMeshUpdate: bool = false,
finishedMeshing: bool = false, finishedMeshing: bool = false, // Must be synced with node.finishedMeshing in mesh_storage.zig
finishedLighting: bool = false, finishedLighting: bool = false,
litNeighbors: Atomic(u32) = .init(0), litNeighbors: Atomic(u32) = .init(0),
mutex: std.Thread.Mutex = .{}, mutex: std.Thread.Mutex = .{},

View File

@ -28,6 +28,7 @@ const ChunkMeshNode = struct {
max: Vec2f = undefined, max: Vec2f = undefined,
active: bool = false, active: bool = false,
rendered: bool = false, rendered: bool = false,
finishedMeshing: bool = false, // Must be synced with mesh.finishedMeshing
mutex: std.Thread.Mutex = .{}, mutex: std.Thread.Mutex = .{},
}; };
const storageSize = 64; const storageSize = 64;
@ -323,6 +324,7 @@ fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: i32) void {
node.mutex.lock(); node.mutex.lock();
const oldMesh = node.mesh; const oldMesh = node.mesh;
node.mesh = null; node.mesh = null;
node.finishedMeshing = false;
node.mutex.unlock(); node.mutex.unlock();
if(oldMesh) |mesh| { if(oldMesh) |mesh| {
mesh.decreaseRefCount(); mesh.decreaseRefCount();
@ -592,9 +594,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
var lod: u3 = 0; var lod: u3 = 0;
while(lod <= settings.highestLOD) : (lod += 1) { while(lod <= settings.highestLOD) : (lod += 1) {
const node = getNodePointer(firstPos); const node = getNodePointer(firstPos);
node.mutex.lock(); const hasMesh = node.finishedMeshing;
const hasMesh = node.mesh != null and node.mesh.?.finishedMeshing;
node.mutex.unlock();
if(hasMesh) { if(hasMesh) {
node.lod = lod; node.lod = lod;
node.min = @splat(-1); node.min = @splat(-1);
@ -617,6 +617,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
defer nodeList.deinit(); defer nodeList.deinit();
const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix); const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix);
while(searchList.removeOrNull()) |data| { while(searchList.removeOrNull()) |data| {
std.debug.assert(data.node.finishedMeshing);
nodeList.append(data.node); nodeList.append(data.node);
data.node.active = false; data.node.active = false;
@ -625,10 +626,6 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
mutex.unlock(); mutex.unlock();
continue; continue;
}; };
if(!mesh.finishedMeshing) {
mutex.unlock();
continue;
}
mesh.increaseRefCount(); mesh.increaseRefCount();
defer mesh.decreaseRefCount(); defer mesh.decreaseRefCount();
mutex.unlock(); mutex.unlock();
@ -638,10 +635,10 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
const relPosFloat: Vec3f = @floatCast(relPos); const relPosFloat: Vec3f = @floatCast(relPos);
var isNeighborLod: [6]bool = .{false} ** 6; var isNeighborLod: [6]bool = .{false} ** 6;
for(chunk.Neighbor.iterable) |neighbor| continueNeighborLoop: { for(chunk.Neighbor.iterable) |neighbor| continueNeighborLoop: {
const component = neighbor.extractDirectionComponent(relPos); const component = neighbor.extractDirectionComponent(relPosFloat);
if(neighbor.isPositive() and component + @as(f64, @floatFromInt(chunk.chunkSize*mesh.pos.voxelSize)) <= 0) continue; if(neighbor.isPositive() and component + @as(f32, @floatFromInt(chunk.chunkSize*mesh.pos.voxelSize)) <= 0) continue;
if(!neighbor.isPositive() and component >= 0) continue; if(!neighbor.isPositive() and component >= 0) continue;
if(@reduce(.Or, @min(mesh.chunkBorders[neighbor.toInt()].min, mesh.chunkBorders[neighbor.toInt()].max) != mesh.chunkBorders[neighbor.toInt()].min)) continue; // There was not a single block in the chunk. TODO: Find a better solution. if(@reduce(.Or, mesh.chunkBorders[neighbor.toInt()].max < mesh.chunkBorders[neighbor.toInt()].min)) continue; // There was not a single transparent block along the chunk border. TODO: Find a better solution.
const minVec: Vec3f = @floatFromInt(mesh.chunkBorders[neighbor.toInt()].min*@as(Vec3i, @splat(mesh.pos.voxelSize))); const minVec: Vec3f = @floatFromInt(mesh.chunkBorders[neighbor.toInt()].min*@as(Vec3i, @splat(mesh.pos.voxelSize)));
const maxVec: Vec3f = @floatFromInt(mesh.chunkBorders[neighbor.toInt()].max*@as(Vec3i, @splat(mesh.pos.voxelSize))); const maxVec: Vec3f = @floatFromInt(mesh.chunkBorders[neighbor.toInt()].max*@as(Vec3i, @splat(mesh.pos.voxelSize)));
var xyMin: Vec2f = .{10, 10}; var xyMin: Vec2f = .{10, 10};
@ -781,7 +778,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
.voxelSize = mesh.pos.voxelSize, .voxelSize = mesh.pos.voxelSize,
}; };
var lod: u3 = data.node.lod; var lod: u3 = data.node.lod;
while(lod <= settings.highestLOD) : (lod += 1) { lodLoop: while(lod <= settings.highestLOD) : (lod += 1) {
defer { defer {
neighborPos.wx &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize); neighborPos.wx &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
neighborPos.wy &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize); neighborPos.wy &= ~@as(i32, neighborPos.voxelSize*chunk.chunkSize);
@ -789,17 +786,13 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
neighborPos.voxelSize *= 2; neighborPos.voxelSize *= 2;
} }
const node = getNodePointer(neighborPos); const node = getNodePointer(neighborPos);
if(getMeshAndIncreaseRefCount(neighborPos)) |neighborMesh| { if(node.finishedMeshing) {
std.debug.assert(std.meta.eql(neighborPos, neighborMesh.pos));
defer neighborMesh.decreaseRefCount();
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) {
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.Neighbor.iterable) |neighbor2| { for(chunk.Neighbor.iterable) |neighbor2| {
const component2 = neighbor2.extractDirectionComponent(relPos2); const component2 = neighbor2.extractDirectionComponent(relPos2);
if(neighbor2.isPositive() and component2 + @as(f64, @floatFromInt(chunk.chunkSize*neighborMesh.pos.voxelSize)) >= 0) continue; if(neighbor2.isPositive() and component2 + @as(f64, @floatFromInt(chunk.chunkSize*neighborPos.voxelSize)) >= 0) continue;
if(!neighbor2.isPositive() and component2 <= 0) continue; if(!neighbor2.isPositive() and component2 <= 0) continue;
{ // Check the chunk of same lod: { // Check the chunk of same lod:
const neighborPos2 = chunk.ChunkPosition{ const neighborPos2 = chunk.ChunkPosition{
@ -822,15 +815,11 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
}; };
const node2 = getNodePointer(neighborPos2); const node2 = getNodePointer(neighborPos2);
if(node2.rendered) { if(node2.rendered) {
isValid = false; isNeighborLod[neighbor.toInt()] = true;
break; continue :lodLoop;
} }
} }
} }
if(!isValid) {
isNeighborLod[neighbor.toInt()] = true;
continue;
}
} }
if(lod != data.node.lod) { if(lod != data.node.lod) {
isNeighborLod[neighbor.toInt()] = true; isNeighborLod[neighbor.toInt()] = true;
@ -845,7 +834,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
node.active = true; node.active = true;
searchList.add(.{ searchList.add(.{
.node = node, .node = node,
.distance = neighborMesh.pos.getMaxDistanceSquared(playerPos), .distance = neighborPos.getMaxDistanceSquared(playerPos),
}) catch unreachable; }) catch unreachable;
node.rendered = true; node.rendered = true;
} }
@ -857,16 +846,13 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: V
} }
for(nodeList.items) |node| { for(nodeList.items) |node| {
node.rendered = false; node.rendered = false;
if(!node.finishedMeshing) continue;
node.mutex.lock(); node.mutex.lock();
const mesh = node.mesh orelse { const mesh = node.mesh orelse {
node.mutex.unlock(); node.mutex.unlock();
continue; continue;
}; };
if(!mesh.finishedMeshing) {
node.mutex.unlock();
continue;
}
mesh.increaseRefCount(); mesh.increaseRefCount();
defer mesh.decreaseRefCount(); defer mesh.decreaseRefCount();
node.mutex.unlock(); node.mutex.unlock();
@ -982,6 +968,7 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()
defer mutex.lock(); defer mutex.lock();
if(isInRenderDistance(mesh.pos)) { if(isInRenderDistance(mesh.pos)) {
const node = getNodePointer(mesh.pos); const node = getNodePointer(mesh.pos);
node.finishedMeshing = true;
mesh.finishedMeshing = true; mesh.finishedMeshing = true;
mesh.uploadData(); mesh.uploadData();
node.mutex.lock(); node.mutex.lock();
@ -1032,6 +1019,7 @@ pub fn addMeshToStorage(mesh: *chunk_meshing.ChunkMesh) error{AlreadyStored}!voi
return error.AlreadyStored; return error.AlreadyStored;
} }
node.mesh = mesh; node.mesh = mesh;
node.finishedMeshing = mesh.finishedMeshing;
mesh.increaseRefCount(); mesh.increaseRefCount();
} }
} }