Remove locking from the mesh loading code.

This makes it easier to work with.
This commit is contained in:
IntegratedQuantum 2023-10-27 16:15:03 +02:00
parent 49179d0891
commit 021f762c01
2 changed files with 162 additions and 221 deletions

View File

@ -639,12 +639,10 @@ pub const meshing = struct {
}; };
pos: ChunkPosition, pos: ChunkPosition,
size: i32, size: i32,
chunk: std.atomic.Atomic(?*Chunk), chunk: *Chunk,
opaqueMesh: PrimitiveMesh, opaqueMesh: PrimitiveMesh,
voxelMesh: PrimitiveMesh, voxelMesh: PrimitiveMesh,
transparentMesh: PrimitiveMesh, transparentMesh: PrimitiveMesh,
generated: bool = false,
mutex: std.Thread.Mutex = std.Thread.Mutex{},
visibilityMask: u8 = 0xff, visibilityMask: u8 = 0xff,
currentSorting: []SortingData = &.{}, currentSorting: []SortingData = &.{},
sortingOutputBuffer: []FaceData = &.{}, sortingOutputBuffer: []FaceData = &.{},
@ -653,7 +651,7 @@ pub const meshing = struct {
chunkBorders: [6]BoundingRectToNeighborChunk = [1]BoundingRectToNeighborChunk{.{}} ** 6, chunkBorders: [6]BoundingRectToNeighborChunk = [1]BoundingRectToNeighborChunk{.{}} ** 6,
pub fn init(allocator: Allocator, pos: ChunkPosition) ChunkMesh { pub fn init(allocator: Allocator, pos: ChunkPosition, chunk: *Chunk) ChunkMesh {
return ChunkMesh{ return ChunkMesh{
.pos = pos, .pos = pos,
.size = chunkSize*pos.voxelSize, .size = chunkSize*pos.voxelSize,
@ -666,7 +664,7 @@ pub const meshing = struct {
.transparentMesh = .{ .transparentMesh = .{
.faces = std.ArrayList(FaceData).init(allocator) .faces = std.ArrayList(FaceData).init(allocator)
}, },
.chunk = std.atomic.Atomic(?*Chunk).init(null), .chunk = chunk,
}; };
} }
@ -676,9 +674,7 @@ pub const meshing = struct {
self.transparentMesh.deinit(); self.transparentMesh.deinit();
main.globalAllocator.free(self.currentSorting); main.globalAllocator.free(self.currentSorting);
main.globalAllocator.free(self.sortingOutputBuffer); main.globalAllocator.free(self.sortingOutputBuffer);
if(self.chunk.load(.Monotonic)) |ch| { main.globalAllocator.destroy(self.chunk);
main.globalAllocator.destroy(ch);
}
} }
pub fn isEmpty(self: *const ChunkMesh) bool { pub fn isEmpty(self: *const ChunkMesh) bool {
@ -705,8 +701,7 @@ pub const meshing = struct {
); );
} }
pub fn regenerateMainMesh(self: *ChunkMesh, chunk: *Chunk) !void { pub fn regenerateMainMesh(self: *ChunkMesh) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
self.opaqueMesh.reset(); self.opaqueMesh.reset();
self.voxelMesh.reset(); self.voxelMesh.reset();
self.transparentMesh.reset(); self.transparentMesh.reset();
@ -717,7 +712,7 @@ pub const meshing = struct {
while(y < chunkSize): (y += 1) { while(y < chunkSize): (y += 1) {
var z: u8 = 0; var z: u8 = 0;
while(z < chunkSize): (z += 1) { while(z < chunkSize): (z += 1) {
const block = (&chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed. const block = (&self.chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
if(block.typ == 0) continue; if(block.typ == 0) continue;
// Check all neighbors: // Check all neighbors:
for(Neighbors.iterable) |i| { for(Neighbors.iterable) |i| {
@ -726,7 +721,7 @@ pub const meshing = struct {
const y2 = y + Neighbors.relY[i]; const y2 = y + Neighbors.relY[i];
const z2 = z + Neighbors.relZ[i]; const z2 = z + Neighbors.relZ[i];
if(x2&chunkMask != x2 or y2&chunkMask != y2 or z2&chunkMask != z2) continue; // Neighbor is outside the chunk. if(x2&chunkMask != x2 or y2&chunkMask != y2 or z2&chunkMask != z2) continue; // Neighbor is outside the chunk.
const neighborBlock = (&chunk.blocks)[getIndex(x2, y2, z2)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed. const neighborBlock = (&self.chunk.blocks)[getIndex(x2, y2, z2)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) { if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) {
if(block.transparent()) { if(block.transparent()) {
if(block.hasBackFace()) { if(block.hasBackFace()) {
@ -750,25 +745,21 @@ pub const meshing = struct {
while(x < chunkSize): (x += 1) { while(x < chunkSize): (x += 1) {
var y: u8 = 0; var y: u8 = 0;
while(y < chunkSize): (y += 1) { while(y < chunkSize): (y += 1) {
self.chunkBorders[Neighbors.dirNegX].adjustToBlock((&chunk.blocks)[getIndex(0, x, y)], .{0, x, y}, Neighbors.dirNegX); // TODO: Wait for the compiler bug to get fixed. self.chunkBorders[Neighbors.dirNegX].adjustToBlock((&self.chunk.blocks)[getIndex(0, x, y)], .{0, x, y}, Neighbors.dirNegX); // TODO: Wait for the compiler bug to get fixed.
self.chunkBorders[Neighbors.dirPosX].adjustToBlock((&chunk.blocks)[getIndex(chunkSize-1, x, y)], .{chunkSize, x, y}, Neighbors.dirPosX); // TODO: Wait for the compiler bug to get fixed. self.chunkBorders[Neighbors.dirPosX].adjustToBlock((&self.chunk.blocks)[getIndex(chunkSize-1, x, y)], .{chunkSize, x, y}, Neighbors.dirPosX); // TODO: Wait for the compiler bug to get fixed.
self.chunkBorders[Neighbors.dirDown].adjustToBlock((&chunk.blocks)[getIndex(x, 0, y)], .{x, 0, y}, Neighbors.dirDown); // TODO: Wait for the compiler bug to get fixed. self.chunkBorders[Neighbors.dirDown].adjustToBlock((&self.chunk.blocks)[getIndex(x, 0, y)], .{x, 0, y}, Neighbors.dirDown); // TODO: Wait for the compiler bug to get fixed.
self.chunkBorders[Neighbors.dirUp].adjustToBlock((&chunk.blocks)[getIndex(x, chunkSize-1, y)], .{x, chunkSize, y}, Neighbors.dirUp); // TODO: Wait for the compiler bug to get fixed. self.chunkBorders[Neighbors.dirUp].adjustToBlock((&self.chunk.blocks)[getIndex(x, chunkSize-1, y)], .{x, chunkSize, y}, Neighbors.dirUp); // TODO: Wait for the compiler bug to get fixed.
self.chunkBorders[Neighbors.dirNegZ].adjustToBlock((&chunk.blocks)[getIndex(x, y, 0)], .{x, y, 0}, Neighbors.dirNegZ); // TODO: Wait for the compiler bug to get fixed. self.chunkBorders[Neighbors.dirNegZ].adjustToBlock((&self.chunk.blocks)[getIndex(x, y, 0)], .{x, y, 0}, Neighbors.dirNegZ); // TODO: Wait for the compiler bug to get fixed.
self.chunkBorders[Neighbors.dirPosZ].adjustToBlock((&chunk.blocks)[getIndex(x, y, chunkSize-1)], .{x, y, chunkSize}, Neighbors.dirPosZ); // TODO: Wait for the compiler bug to get fixed. self.chunkBorders[Neighbors.dirPosZ].adjustToBlock((&self.chunk.blocks)[getIndex(x, y, chunkSize-1)], .{x, y, chunkSize}, Neighbors.dirPosZ); // TODO: Wait for the compiler bug to get fixed.
} }
} }
if(self.chunk.swap(chunk, .Monotonic)) |oldChunk| {
main.globalAllocator.destroy(oldChunk);
}
self.transparentMesh.updateCore(); self.transparentMesh.updateCore();
self.opaqueMesh.updateCore(); self.opaqueMesh.updateCore();
self.voxelMesh.updateCore(); self.voxelMesh.updateCore();
} }
fn addFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) !void { fn addFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
if(transparent) { if(transparent) {
try self.transparentMesh.addFace(faceData, fromNeighborChunk); try self.transparentMesh.addFace(faceData, fromNeighborChunk);
} else { } else {
@ -781,7 +772,6 @@ pub const meshing = struct {
} }
fn removeFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) void { fn removeFace(self: *ChunkMesh, faceData: FaceData, fromNeighborChunk: ?u3, transparent: bool) void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
if(transparent) { if(transparent) {
self.transparentMesh.removeFace(faceData, fromNeighborChunk); self.transparentMesh.removeFace(faceData, fromNeighborChunk);
} else { } else {
@ -797,10 +787,7 @@ pub const meshing = struct {
const x = _x & chunkMask; const x = _x & chunkMask;
const y = _y & chunkMask; const y = _y & chunkMask;
const z = _z & chunkMask; const z = _z & chunkMask;
self.mutex.lock(); const oldBlock = self.chunk.blocks[getIndex(x, y, z)];
defer self.mutex.unlock();
if(!self.generated) return;
const oldBlock = self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)];
for(Neighbors.iterable) |neighbor| { for(Neighbors.iterable) |neighbor| {
var neighborMesh = self; var neighborMesh = self;
var nx = x + Neighbors.relX[neighbor]; var nx = x + Neighbors.relX[neighbor];
@ -808,14 +795,11 @@ pub const meshing = struct {
var nz = z + Neighbors.relZ[neighbor]; var nz = z + Neighbors.relZ[neighbor];
if(nx & chunkMask != nx or ny & chunkMask != ny or nz & chunkMask != nz) { // Outside this chunk. if(nx & chunkMask != nx or ny & chunkMask != ny or nz & chunkMask != nz) { // Outside this chunk.
neighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor) orelse continue; neighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor) orelse continue;
if(!neighborMesh.generated) continue;
neighborMesh.mutex.lock();
} }
defer if(neighborMesh != self) neighborMesh.mutex.unlock();
nx &= chunkMask; nx &= chunkMask;
ny &= chunkMask; ny &= chunkMask;
nz &= chunkMask; nz &= chunkMask;
const neighborBlock = neighborMesh.chunk.load(.Monotonic).?.blocks[getIndex(nx, ny, nz)]; const neighborBlock = neighborMesh.chunk.blocks[getIndex(nx, ny, nz)];
{ // TODO: Batch all the changes and apply them in one go for more efficiency. { // TODO: Batch all the changes and apply them in one go for more efficiency.
{ // The face of the changed block { // The face of the changed block
const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor); const newVisibility = canBeSeenThroughOtherBlock(newBlock, neighborBlock, neighbor);
@ -896,7 +880,7 @@ pub const meshing = struct {
try neighborMesh.transparentMesh.uploadData(); try neighborMesh.transparentMesh.uploadData();
} }
} }
self.chunk.load(.Monotonic).?.blocks[getIndex(x, y, z)] = newBlock; self.chunk.blocks[getIndex(x, y, z)] = newBlock;
try self.opaqueMesh.uploadData(); try self.opaqueMesh.uploadData();
try self.voxelMesh.uploadData(); try self.voxelMesh.uploadData();
try self.transparentMesh.uploadData(); try self.transparentMesh.uploadData();
@ -911,8 +895,6 @@ pub const meshing = struct {
} }
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void { pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
std.debug.assert(!self.mutex.tryLock()); // The mutex should be locked when calling this function.
const chunk = self.chunk.load(.Monotonic) orelse return; // In the mean-time the mesh was discarded and recreated and all the data was lost.
self.opaqueMesh.resetToCore(); self.opaqueMesh.resetToCore();
self.voxelMesh.resetToCore(); self.voxelMesh.resetToCore();
self.transparentMesh.resetToCore(); self.transparentMesh.resetToCore();
@ -923,9 +905,6 @@ pub const meshing = struct {
const nullNeighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor); const nullNeighborMesh = renderer.RenderStructure.getNeighbor(self.pos, self.pos.voxelSize, neighbor);
if(nullNeighborMesh) |neighborMesh| { if(nullNeighborMesh) |neighborMesh| {
std.debug.assert(neighborMesh != self); std.debug.assert(neighborMesh != self);
neighborMesh.mutex.lock();
defer neighborMesh.mutex.unlock();
if(neighborMesh.generated) {
var additionalNeighborFacesOpaque = std.ArrayList(FaceData).init(main.threadAllocator); var additionalNeighborFacesOpaque = std.ArrayList(FaceData).init(main.threadAllocator);
defer additionalNeighborFacesOpaque.deinit(); defer additionalNeighborFacesOpaque.deinit();
var additionalNeighborFacesVoxel = std.ArrayList(FaceData).init(main.threadAllocator); var additionalNeighborFacesVoxel = std.ArrayList(FaceData).init(main.threadAllocator);
@ -956,8 +935,8 @@ pub const meshing = struct {
const otherX: u8 = @intCast(x+%Neighbors.relX[neighbor] & chunkMask); const otherX: u8 = @intCast(x+%Neighbors.relX[neighbor] & chunkMask);
const otherY: u8 = @intCast(y+%Neighbors.relY[neighbor] & chunkMask); const otherY: u8 = @intCast(y+%Neighbors.relY[neighbor] & chunkMask);
const otherZ: u8 = @intCast(z+%Neighbors.relZ[neighbor] & chunkMask); const otherZ: u8 = @intCast(z+%Neighbors.relZ[neighbor] & chunkMask);
const block = (&chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed. const block = (&self.chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
const otherBlock = (&neighborMesh.chunk.load(.Monotonic).?.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed. const otherBlock = (&neighborMesh.chunk.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
if(block.transparent()) { if(block.transparent()) {
if(block.hasBackFace()) { if(block.hasBackFace()) {
@ -993,13 +972,9 @@ pub const meshing = struct {
try neighborMesh.transparentMesh.replaceNeighbors(neighbor, additionalNeighborFacesTransparent.items); try neighborMesh.transparentMesh.replaceNeighbors(neighbor, additionalNeighborFacesTransparent.items);
continue; continue;
} }
}
// lod border: // lod border:
if(self.pos.voxelSize == 1 << settings.highestLOD) continue; if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
const neighborMesh = renderer.RenderStructure.getNeighbor(self.pos, 2*self.pos.voxelSize, neighbor) orelse return error.LODMissing; const neighborMesh = renderer.RenderStructure.getNeighbor(self.pos, 2*self.pos.voxelSize, neighbor) orelse continue;
neighborMesh.mutex.lock();
defer neighborMesh.mutex.unlock();
if(neighborMesh.generated) {
const x3: u8 = if(neighbor & 1 == 0) @intCast(chunkMask) else 0; const x3: u8 = if(neighbor & 1 == 0) @intCast(chunkMask) else 0;
const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunkSize; const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunkSize;
const offsetY = @divExact(self.pos.wy, self.pos.voxelSize) & chunkSize; const offsetY = @divExact(self.pos.wy, self.pos.voxelSize) & chunkSize;
@ -1027,8 +1002,8 @@ pub const meshing = struct {
const otherX: u8 = @intCast((x+%Neighbors.relX[neighbor]+%offsetX >> 1) & chunkMask); const otherX: u8 = @intCast((x+%Neighbors.relX[neighbor]+%offsetX >> 1) & chunkMask);
const otherY: u8 = @intCast((y+%Neighbors.relY[neighbor]+%offsetY >> 1) & chunkMask); const otherY: u8 = @intCast((y+%Neighbors.relY[neighbor]+%offsetY >> 1) & chunkMask);
const otherZ: u8 = @intCast((z+%Neighbors.relZ[neighbor]+%offsetZ >> 1) & chunkMask); const otherZ: u8 = @intCast((z+%Neighbors.relZ[neighbor]+%offsetZ >> 1) & chunkMask);
const block = (&chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed. const block = (&self.chunk.blocks)[getIndex(x, y, z)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
const otherBlock = (&neighborMesh.chunk.load(.Monotonic).?.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed. const otherBlock = (&neighborMesh.chunk.blocks)[getIndex(otherX, otherY, otherZ)]; // a temporary fix to a compiler performance bug. TODO: check if this was fixed.
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) { if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) {
if(otherBlock.transparent()) { if(otherBlock.transparent()) {
try self.transparentMesh.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z, false)); try self.transparentMesh.append(constructFaceData(otherBlock, neighbor ^ 1, x, y, z, false));
@ -1047,20 +1022,13 @@ pub const meshing = struct {
} }
} }
} }
} else {
return error.LODMissing;
}
} }
try self.opaqueMesh.finish(); try self.opaqueMesh.finish();
try self.voxelMesh.finish(); try self.voxelMesh.finish();
try self.transparentMesh.finish(); try self.transparentMesh.finish();
self.generated = true;
} }
pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void { pub fn render(self: *ChunkMesh, playerPosition: Vec3d) void {
if(!self.generated) {
return;
}
if(self.opaqueMesh.vertexCount == 0) return; if(self.opaqueMesh.vertexCount == 0) return;
c.glUniform3f( c.glUniform3f(
uniforms.modelPosition, uniforms.modelPosition,
@ -1075,9 +1043,6 @@ pub const meshing = struct {
} }
pub fn renderVoxelModels(self: *ChunkMesh, playerPosition: Vec3d) void { pub fn renderVoxelModels(self: *ChunkMesh, playerPosition: Vec3d) void {
if(!self.generated) {
return;
}
if(self.voxelMesh.vertexCount == 0) return; if(self.voxelMesh.vertexCount == 0) return;
c.glUniform3f( c.glUniform3f(
voxelUniforms.modelPosition, voxelUniforms.modelPosition,
@ -1092,9 +1057,6 @@ pub const meshing = struct {
} }
pub fn renderTransparent(self: *ChunkMesh, playerPosition: Vec3d) !void { pub fn renderTransparent(self: *ChunkMesh, playerPosition: Vec3d) !void {
if(!self.generated) {
return;
}
if(self.transparentMesh.vertexCount == 0) return; if(self.transparentMesh.vertexCount == 0) return;
var needsUpdate: bool = false; var needsUpdate: bool = false;

View File

@ -873,7 +873,7 @@ pub const MeshSelection = struct {
pub const RenderStructure = struct { pub const RenderStructure = struct {
const ChunkMeshNode = struct { const ChunkMeshNode = struct {
mesh: chunk.meshing.ChunkMesh, mesh: ?*chunk.meshing.ChunkMesh,
shouldBeRemoved: bool, // Internal use. shouldBeRemoved: bool, // Internal use.
drawableChildren: u32, // How many children can be renderer. If this is 8 then there is no need to render this mesh. drawableChildren: u32, // How many children can be renderer. If this is 8 then there is no need to render this mesh.
lod: u3, lod: u3,
@ -884,9 +884,7 @@ pub const RenderStructure = struct {
var storageLists: [settings.highestLOD + 1][]?*ChunkMeshNode = [1][]?*ChunkMeshNode{&.{}} ** (settings.highestLOD + 1); var storageLists: [settings.highestLOD + 1][]?*ChunkMeshNode = [1][]?*ChunkMeshNode{&.{}} ** (settings.highestLOD + 1);
var storageListsSwap: [settings.highestLOD + 1][]?*ChunkMeshNode = [1][]?*ChunkMeshNode{&.{}} ** (settings.highestLOD + 1); var storageListsSwap: [settings.highestLOD + 1][]?*ChunkMeshNode = [1][]?*ChunkMeshNode{&.{}} ** (settings.highestLOD + 1);
var meshList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator); var meshList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
var updatableList: std.ArrayList(chunk.ChunkPosition) = undefined; var updatableList: std.ArrayList(*chunk.meshing.ChunkMesh) = undefined;
var updatableListSwap: std.ArrayList(chunk.ChunkPosition) = undefined;
var clearList: std.ArrayList(*ChunkMeshNode) = undefined;
var lastRD: i32 = 0; var lastRD: i32 = 0;
var lastFactor: f32 = 0; var lastFactor: f32 = 0;
var lastX: [settings.highestLOD + 1]i32 = [_]i32{0} ** (settings.highestLOD + 1); var lastX: [settings.highestLOD + 1]i32 = [_]i32{0} ** (settings.highestLOD + 1);
@ -907,9 +905,8 @@ pub const RenderStructure = struct {
pub fn init() !void { pub fn init() !void {
lastRD = 0; lastRD = 0;
lastFactor = 0; lastFactor = 0;
updatableList = std.ArrayList(chunk.ChunkPosition).init(main.globalAllocator); updatableList = std.ArrayList(*chunk.meshing.ChunkMesh).init(main.globalAllocator);
blockUpdateList = std.ArrayList(BlockUpdate).init(main.globalAllocator); blockUpdateList = std.ArrayList(BlockUpdate).init(main.globalAllocator);
clearList = std.ArrayList(*ChunkMeshNode).init(main.globalAllocator);
for(&storageLists) |*storageList| { for(&storageLists) |*storageList| {
storageList.* = try main.globalAllocator.alloc(?*ChunkMeshNode, 0); storageList.* = try main.globalAllocator.alloc(?*ChunkMeshNode, 0);
} }
@ -917,10 +914,13 @@ pub const RenderStructure = struct {
pub fn deinit() void { pub fn deinit() void {
for(storageLists) |storageList| { for(storageLists) |storageList| {
for(storageList) |nullChunkMesh| { for(storageList) |nullNode| {
if(nullChunkMesh) |chunkMesh| { if(nullNode) |node| {
chunkMesh.mesh.deinit(); if(node.mesh) |mesh| {
main.globalAllocator.destroy(chunkMesh); mesh.deinit();
main.globalAllocator.destroy(mesh);
}
main.globalAllocator.destroy(node);
} }
} }
main.globalAllocator.free(storageList); main.globalAllocator.free(storageList);
@ -928,13 +928,12 @@ pub const RenderStructure = struct {
for(storageListsSwap) |storageList| { for(storageListsSwap) |storageList| {
main.globalAllocator.free(storageList); main.globalAllocator.free(storageList);
} }
updatableList.deinit(); for(updatableList.items) |mesh| {
for(clearList.items) |chunkMesh| { mesh.deinit();
chunkMesh.mesh.deinit(); main.globalAllocator.destroy(mesh);
main.globalAllocator.destroy(chunkMesh);
} }
updatableList.deinit();
blockUpdateList.deinit(); blockUpdateList.deinit();
clearList.deinit();
meshList.deinit(); meshList.deinit();
} }
@ -966,12 +965,13 @@ pub const RenderStructure = struct {
pub fn getChunk(x: i32, y: i32, z: i32) ?*chunk.Chunk { pub fn getChunk(x: i32, y: i32, z: i32) ?*chunk.Chunk {
const node = RenderStructure._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}) orelse return null; const node = RenderStructure._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}) orelse return null;
return node.mesh.chunk.load(.Monotonic); return &node.mesh.chunk;
} }
pub fn getBlock(x: i32, y: i32, z: i32) ?blocks.Block { pub fn getBlock(x: i32, y: i32, z: i32) ?blocks.Block {
const node = RenderStructure._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}) orelse return null; const node = RenderStructure._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=1}) orelse return null;
const block = (node.mesh.chunk.load(.Monotonic) orelse return null).getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask); const mesh = node.mesh orelse return null;
const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
return block; return block;
} }
@ -979,7 +979,8 @@ 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._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod}) orelse continue; const node = RenderStructure._getNode(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod}) orelse continue;
const block = (node.mesh.chunk.load(.Monotonic) orelse continue).getBlock(x & chunk.chunkMask<<lod, y & chunk.chunkMask<<lod, z & chunk.chunkMask<<lod); const mesh = node.mesh orelse continue;
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};
@ -992,7 +993,7 @@ 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 = _getNode(pos) orelse return null; const node = _getNode(pos) orelse return null;
return &node.mesh; return node.mesh;
} }
pub fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: Vec3d, renderDistance: i32, LODFactor: f32) ![]*chunk.meshing.ChunkMesh { pub fn updateAndGetRenderChunks(conn: *network.Connection, playerPos: Vec3d, renderDistance: i32, LODFactor: f32) ![]*chunk.meshing.ChunkMesh {
@ -1056,13 +1057,13 @@ 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};
var node = getNodeFromRenderThread(pos); var node = getNodeFromRenderThread(pos);
if(node) |_node| { if(node) |_node| {
if(_node.mesh.generated) { if(_node.mesh) |mesh| {
_node.mesh.visibilityMask = 0xff; mesh.visibilityMask = 0xff;
} }
_node.shouldBeRemoved = false; _node.shouldBeRemoved = false;
} else { } else {
node = try main.globalAllocator.create(ChunkMeshNode); node = try main.globalAllocator.create(ChunkMeshNode);
node.?.mesh = chunk.meshing.ChunkMesh.init(main.globalAllocator, pos); node.?.mesh = null;
node.?.shouldBeRemoved = true; // Might be removed in the next iteration. node.?.shouldBeRemoved = true; // Might be removed in the next iteration.
try meshRequests.append(pos); try meshRequests.append(pos);
} }
@ -1084,27 +1085,23 @@ pub const RenderStructure = struct {
storageListsSwap[lod] = oldList; storageListsSwap[lod] = oldList;
} }
for(oldList) |nullMesh| { for(oldList) |nullMesh| {
if(nullMesh) |mesh| { if(nullMesh) |node| {
if(mesh.shouldBeRemoved) { if(node.shouldBeRemoved) {
// 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:
for(chunk.Neighbors.iterable) |neighbor| { for(chunk.Neighbors.iterable) |neighbor| {
if(getNeighbor(mesh.mesh.pos, mesh.mesh.pos.voxelSize, neighbor)) |neighborMesh| { if(node.mesh) |mesh| {
if(neighborMesh.generated) { if(getNeighbor(mesh.pos, mesh.pos.voxelSize, neighbor)) |neighborMesh| {
neighborMesh.mutex.lock();
defer neighborMesh.mutex.unlock();
try neighborMesh.uploadDataAndFinishNeighbors(); try neighborMesh.uploadDataAndFinishNeighbors();
} }
} }
} }
if(mesh.mesh.mutex.tryLock()) { // Make sure there is no task currently running on the thing. if(node.mesh) |mesh| {
mesh.mesh.mutex.unlock(); mesh.deinit();
mesh.mesh.deinit();
main.globalAllocator.destroy(mesh); main.globalAllocator.destroy(mesh);
} else {
try clearList.append(mesh);
} }
main.globalAllocator.destroy(node);
} else { } else {
mesh.shouldBeRemoved = true; node.shouldBeRemoved = true;
} }
} }
} }
@ -1138,7 +1135,7 @@ pub const RenderStructure = struct {
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) {
if(getNodeFromRenderThread(firstPos)) |node| if(node.mesh.generated) { if(getNodeFromRenderThread(firstPos)) |node| if(node.mesh != null) {
node.lod = lod; node.lod = lod;
node.min = @splat(-1); node.min = @splat(-1);
node.max = @splat(1); node.max = @splat(1);
@ -1158,12 +1155,14 @@ pub const RenderStructure = struct {
const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix); const projRotMat = game.projectionMatrix.mul(game.camera.viewMatrix);
while(searchList.removeOrNull()) |data| { while(searchList.removeOrNull()) |data| {
data.node.active = false; data.node.active = false;
const mesh = &data.node.mesh; const mesh = data.node.mesh.?;
if(data.node.lod+1 != storageLists.len) { if(data.node.lod+1 != storageLists.len) {
if(getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1})) |parent| { if(getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1})) |parent| {
if(parent.mesh) |parentMesh| {
const sizeShift = chunk.chunkShift + data.node.lod; const sizeShift = chunk.chunkShift + data.node.lod;
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);
parent.mesh.visibilityMask &= ~(@as(u8, 1) << octantIndex); parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex);
}
} }
} }
try meshList.append(mesh); try meshList.append(mesh);
@ -1319,7 +1318,7 @@ pub const RenderStructure = struct {
}; };
var lod: u3 = data.node.lod; var lod: u3 = data.node.lod;
while(lod <= settings.highestLOD) : (lod += 1) { while(lod <= settings.highestLOD) : (lod += 1) {
if(getNodeFromRenderThread(neighborPos)) |node| if(node.mesh.generated) { if(getNodeFromRenderThread(neighborPos)) |node| if(node.mesh != null) {
if(node.active) { if(node.active) {
node.min = @min(node.min, min); node.min = @min(node.min, min);
node.max = @max(node.max, max); node.max = @max(node.max, max);
@ -1330,7 +1329,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 = node.mesh.?.pos.getMaxDistanceSquared(playerPos),
}); });
} }
break :continueNeighborLoop; break :continueNeighborLoop;
@ -1343,19 +1342,6 @@ pub const RenderStructure = struct {
} }
} }
var i: usize = 0;
while(i < clearList.items.len) {
const mesh = clearList.items[i];
if(mesh.mesh.mutex.tryLock()) { // Make sure there is no task currently running on the thing.
mesh.mesh.mutex.unlock();
mesh.mesh.deinit();
main.globalAllocator.destroy(mesh);
_ = clearList.swapRemove(i);
} else {
i += 1;
}
}
lastRD = renderDistance; lastRD = renderDistance;
lastFactor = LODFactor; lastFactor = LODFactor;
// Make requests after updating the, to avoid concurrency issues and reduce the number of requests: // Make requests after updating the, to avoid concurrency issues and reduce the number of requests:
@ -1370,7 +1356,9 @@ 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};
if(_getNode(pos)) |node| { if(_getNode(pos)) |node| {
try node.mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock); if(node.mesh) |mesh| {
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.
} }
} }
blockUpdateList.clearRetainingCapacity(); blockUpdateList.clearRetainingCapacity();
@ -1382,29 +1370,27 @@ pub const RenderStructure = struct {
var closestPriority: f32 = -std.math.floatMax(f32); var closestPriority: f32 = -std.math.floatMax(f32);
var closestIndex: usize = 0; var closestIndex: usize = 0;
const playerPos = game.Player.getPosBlocking(); const playerPos = game.Player.getPosBlocking();
for(updatableList.items, 0..) |pos, i| { for(updatableList.items, 0..) |mesh, i| {
const priority = pos.getPriority(playerPos); const priority = mesh.pos.getPriority(playerPos);
if(priority > closestPriority) { if(priority > closestPriority) {
closestPriority = priority; closestPriority = priority;
closestIndex = i; closestIndex = i;
} }
} }
const pos = updatableList.orderedRemove(closestIndex); const mesh = updatableList.orderedRemove(closestIndex);
mutex.unlock(); mutex.unlock();
defer mutex.lock(); defer mutex.lock();
const nullNode = _getNode(pos); const nullNode = _getNode(mesh.pos);
if(nullNode) |node| { if(nullNode) |node| {
node.mesh.mutex.lock(); try mesh.uploadDataAndFinishNeighbors();
defer node.mesh.mutex.unlock(); if(node.mesh) |oldMesh| {
node.mesh.uploadDataAndFinishNeighbors() catch |err| { oldMesh.deinit();
if(err == error.LODMissing) { main.globalAllocator.destroy(oldMesh);
mutex.lock();
defer mutex.unlock();
try updatableList.append(pos);
} else {
return err;
} }
}; node.mesh = mesh;
} else {
mesh.deinit();
main.globalAllocator.destroy(mesh);
} }
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh. if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
} }
@ -1442,25 +1428,18 @@ pub const RenderStructure = struct {
pub fn run(self: *MeshGenerationTask) Allocator.Error!void { pub fn run(self: *MeshGenerationTask) Allocator.Error!void {
const pos = self.mesh.pos; const pos = self.mesh.pos;
const nullNode = _getNode(pos); const mesh = try main.globalAllocator.create(chunk.meshing.ChunkMesh);
if(nullNode) |node| { mesh.* = chunk.meshing.ChunkMesh.init(main.globalAllocator, pos, self.mesh);
{ try mesh.regenerateMainMesh();
node.mesh.mutex.lock();
defer node.mesh.mutex.unlock();
try node.mesh.regenerateMainMesh(self.mesh);
}
mutex.lock(); mutex.lock();
defer mutex.unlock(); defer mutex.unlock();
updatableList.append(pos) catch |err| { updatableList.append(mesh) catch |err| {
std.log.err("Error while regenerating mesh: {s}", .{@errorName(err)}); std.log.err("Error while regenerating mesh: {s}", .{@errorName(err)});
if(@errorReturnTrace()) |trace| { if(@errorReturnTrace()) |trace| {
std.log.err("Trace: {}", .{trace}); std.log.err("Trace: {}", .{trace});
} }
main.globalAllocator.destroy(self.mesh); main.globalAllocator.destroy(self.mesh);
}; };
} else {
main.globalAllocator.destroy(self.mesh);
}
main.globalAllocator.destroy(self); main.globalAllocator.destroy(self);
} }