Debug mode is now a slide show.

Mesh generation is much faster though, especially in release.
Additionally a few concurrency bugs, a deadlock and some other minor issues were fixed.
This commit is contained in:
IntegratedQuantum 2023-11-28 21:30:58 +01:00
parent a61fe26fab
commit 00f8b79ec4
2 changed files with 140 additions and 55 deletions

View File

@ -527,6 +527,7 @@ pub const meshing = struct {
neighborFacesSameLod: [6]std.ArrayListUnmanaged(FaceData) = [_]std.ArrayListUnmanaged(FaceData){.{}} ** 6,
neighborFacesHigherLod: [6]std.ArrayListUnmanaged(FaceData) = [_]std.ArrayListUnmanaged(FaceData){.{}} ** 6,
completeList: []FaceData = &.{},
mutex: std.Thread.Mutex = .{},
bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0},
vertexCount: u31 = 0,
wasChanged: bool = false,
@ -586,23 +587,22 @@ pub const meshing = struct {
for(neighborFaceLists) |neighborFaces| {
len += neighborFaces.len;
}
if(main.globalAllocator.resize(self.completeList, len)) {
self.completeList.len = len;
} else {
main.globalAllocator.free(self.completeList);
self.completeList = try main.globalAllocator.alloc(FaceData, len);
}
const completeList = try main.globalAllocator.alloc(FaceData, len);
var i: usize = 0;
@memcpy(self.completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items);
@memcpy(completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items);
i += self.coreFaces.items.len;
for(neighborFaceLists) |neighborFaces| {
@memcpy(self.completeList[i..][0..neighborFaces.len], neighborFaces);
@memcpy(completeList[i..][0..neighborFaces.len], neighborFaces);
i += neighborFaces.len;
}
for(self.completeList) |*face| {
for(completeList) |*face| {
face.light = getLight(parent, face.position.x, face.position.y, face.position.z, face.position.normal);
}
try self.uploadData();
self.mutex.lock();
const oldList = self.completeList;
self.completeList = completeList;
self.mutex.unlock();
main.globalAllocator.free(oldList);
}
fn getValues(mesh: *ChunkMesh, wx: i32, wy: i32, wz: i32) [6]u8 {
@ -627,7 +627,8 @@ pub const meshing = struct {
if(x == x & chunkMask and y == y & chunkMask and z == z & chunkMask) {
return getValues(parent, wx, wy, wz);
}
const neighborMesh = renderer.RenderStructure.getMeshFromAnyLodFromRenderThread(wx, wy, wz, parent.pos.voxelSize) orelse return .{0, 0, 0, 0, 0, 0};
const neighborMesh = renderer.RenderStructure.getMeshFromAnyLodAndIncreaseRefCount(wx, wy, wz, parent.pos.voxelSize) orelse return .{0, 0, 0, 0, 0, 0};
defer neighborMesh.decreaseRefCount();
// TODO: If the neighbor mesh has a higher lod the transition isn't seamless.
return getValues(neighborMesh, wx, wy, wz);
}
@ -673,6 +674,8 @@ pub const meshing = struct {
}
fn uploadData(self: *PrimitiveMesh) !void {
self.mutex.lock();
defer self.mutex.unlock();
self.vertexCount = @intCast(6*self.completeList.len);
try faceBuffer.uploadData(self.completeList, &self.bufferAllocation);
self.wasChanged = true;
@ -780,6 +783,7 @@ pub const meshing = struct {
refCount: std.atomic.Value(u32) = std.atomic.Value(u32).init(1),
needsNeighborUpdate: bool = false,
needsMeshUpdate: bool = false,
finishedMeshing: bool = false,
mutex: std.Thread.Mutex = .{},
chunkBorders: [6]BoundingRectToNeighborChunk = [1]BoundingRectToNeighborChunk{.{}} ** 6,
@ -858,6 +862,8 @@ pub const meshing = struct {
}
pub fn regenerateMainMesh(self: *ChunkMesh) !void {
renderer.RenderStructure.addMeshToStorage(self);
self.mutex.lock();
self.opaqueMesh.reset();
self.voxelMesh.reset();
self.transparentMesh.reset();
@ -909,6 +915,7 @@ pub const meshing = struct {
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.
}
}
self.mutex.unlock();
try self.finishNeighbors(false);
}
@ -937,12 +944,19 @@ pub const meshing = struct {
}
pub fn updateBlock(self: *ChunkMesh, _x: i32, _y: i32, _z: i32, newBlock: Block) !void {
self.mutex.lock();
defer self.mutex.unlock();
const x = _x & chunkMask;
const y = _y & chunkMask;
const z = _z & chunkMask;
const oldBlock = self.chunk.blocks[getIndex(x, y, z)];
for(Neighbors.iterable) |neighbor| {
var neighborMesh = self;
if(neighborMesh != self) {
self.mutex.unlock();
deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex);
}
defer if(neighborMesh != self) neighborMesh.mutex.unlock();
var nx = x + Neighbors.relX[neighbor];
var ny = y + Neighbors.relY[neighbor];
var nz = z + Neighbors.relZ[neighbor];
@ -1028,10 +1042,12 @@ pub const meshing = struct {
}
}
if(neighborMesh != self) {
try neighborMesh.finishData();
try neighborMesh.uploadData();
}
}
self.chunk.blocks[getIndex(x, y, z)] = newBlock;
try self.finishData();
try self.uploadData();
}
@ -1049,7 +1065,8 @@ pub const meshing = struct {
self.transparentMesh.clearNeighbor(neighbor, isLod);
}
pub fn uploadData(self: *ChunkMesh) !void {
fn finishData(self: *ChunkMesh) !void {
std.debug.assert(!self.mutex.tryLock());
const isNeighborLod: [6]bool = .{
self.lastNeighborsSameLod[0] == null or self.forceHigherLod[0],
self.lastNeighborsSameLod[1] == null or self.forceHigherLod[1],
@ -1063,10 +1080,19 @@ pub const meshing = struct {
try self.transparentMesh.finish(self, isNeighborLod);
}
pub fn uploadData(self: *ChunkMesh) !void {
try self.opaqueMesh.uploadData();
try self.voxelMesh.uploadData();
try self.transparentMesh.uploadData();
}
pub fn changeLodBorders(self: *ChunkMesh, forceHigherLod: [6]bool) !void {
if(!std.meta.eql(forceHigherLod, self.forceHigherLod)) {
self.mutex.lock();
self.forceHigherLod = forceHigherLod;
try self.finishData();
try self.uploadData();
self.mutex.unlock();
}
}
@ -1084,13 +1110,13 @@ pub const meshing = struct {
const getNeighborMesh: fn(ChunkPosition, u31, u3) ?*ChunkMesh = if(inRenderThread) renderer.RenderStructure.getNeighborFromRenderThread else renderer.RenderStructure.getNeighborAndIncreaseRefCount;
for(Neighbors.iterable) |neighbor| {
const nullNeighborMesh = getNeighborMesh(self.pos, self.pos.voxelSize, neighbor);
if(nullNeighborMesh) |neighborMesh| {
if(nullNeighborMesh) |neighborMesh| sameLodBlock: {
defer if(!inRenderThread) neighborMesh.decreaseRefCount();
std.debug.assert(neighborMesh != self);
deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex);
defer self.mutex.unlock();
defer neighborMesh.mutex.unlock();
if(self.lastNeighborsSameLod[neighbor] == neighborMesh) continue;
if(self.lastNeighborsSameLod[neighbor] == neighborMesh) break :sameLodBlock;
self.lastNeighborsSameLod[neighbor] = neighborMesh;
neighborMesh.lastNeighborsSameLod[neighbor ^ 1] = self;
self.clearNeighbor(neighbor, false);
@ -1151,6 +1177,7 @@ pub const meshing = struct {
}
}
}
try neighborMesh.finishData();
if(inRenderThread) {
try neighborMesh.uploadData();
} else {
@ -1158,6 +1185,8 @@ pub const meshing = struct {
try renderer.RenderStructure.addToUpdateList(neighborMesh);
}
} else {
self.mutex.lock();
defer self.mutex.unlock();
if(self.lastNeighborsSameLod[neighbor] != null) {
self.clearNeighbor(neighbor, false);
self.lastNeighborsSameLod[neighbor] = null;
@ -1166,6 +1195,8 @@ pub const meshing = struct {
// lod border:
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
const neighborMesh = getNeighborMesh(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
self.mutex.lock();
defer self.mutex.unlock();
if(self.lastNeighborsHigherLod[neighbor] != null) {
self.clearNeighbor(neighbor, true);
self.lastNeighborsHigherLod[neighbor] = null;
@ -1227,10 +1258,13 @@ pub const meshing = struct {
}
}
}
self.mutex.lock();
defer self.mutex.unlock();
try self.finishData();
}
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
try self.finishNeighbors(true);
self.finishedMeshing = true;
try self.uploadData();
}

View File

@ -912,7 +912,15 @@ pub const RenderStructure = struct {
}
pub fn deinit() void {
freeOldMeshes(0, 0, 0, 0) catch |err| {
const olderPx = lastPx;
const olderPy = lastPy;
const olderPz = lastPz;
const olderRD = lastRD;
lastPx = 0;
lastPy = 0;
lastPz = 0;
lastRD = 0;
freeOldMeshes(olderPx, olderPy, olderPz, olderRD) catch |err| {
std.log.err("Error while freeing remaining meshes: {s}", .{@errorName(err)});
};
for(storageLists) |storageList| {
@ -953,7 +961,7 @@ pub const RenderStructure = struct {
fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block {
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1});
const mesh = node.mesh.load(.Unordered) orelse return null;
const mesh = node.mesh.load(.Acquire) orelse return null;
const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
return block;
}
@ -962,7 +970,7 @@ pub const RenderStructure = struct {
var lod: u5 = 0;
while(lod < settings.highestLOD) : (lod += 1) {
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod});
const mesh = node.mesh.load(.Unordered) orelse continue;
const mesh = node.mesh.load(.Acquire) orelse continue;
const block = mesh.chunk.getBlock(x & chunk.chunkMask<<lod, y & chunk.chunkMask<<lod, z & chunk.chunkMask<<lod);
return block;
}
@ -973,7 +981,7 @@ pub const RenderStructure = struct {
var lod: u5 = @ctz(voxelSize);
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});
return node.mesh.load(.Unordered) orelse continue;
return node.mesh.load(.Acquire) orelse continue;
}
return null;
}
@ -985,18 +993,30 @@ pub const RenderStructure = struct {
pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
pos.voxelSize = resolution;
const node = getNodeFromRenderThread(pos);
return node.mesh.load(.Unordered);
return node.mesh.load(.Acquire);
}
pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk.meshing.ChunkMesh {
const node = RenderStructure.getNodeFromRenderThread(pos);
const mesh = node.mesh.load(.Unordered) orelse return null;
const mesh = node.mesh.load(.Acquire) orelse return null;
const lod = std.math.log2_int(u31, pos.voxelSize);
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;
if(mesh.tryIncreaseRefCount()) {
return mesh;
}
return null;
}
pub fn getMeshFromAnyLodAndIncreaseRefCount(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk.meshing.ChunkMesh {
var lod: u5 = @ctz(voxelSize);
while(lod < settings.highestLOD) : (lod += 1) {
const mesh = RenderStructure.getMeshAndIncreaseRefCount(.{.wx = wx & ~chunk.chunkMask<<lod, .wy = wy & ~chunk.chunkMask<<lod, .wz = wz & ~chunk.chunkMask<<lod, .voxelSize=@as(u31, 1) << lod});
return mesh orelse continue;
}
return null;
}
pub fn getNeighborAndIncreaseRefCount(_pos: chunk.ChunkPosition, resolution: u31, neighbor: u3) ?*chunk.meshing.ChunkMesh {
var pos = _pos;
pos.wx += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relX[neighbor];
@ -1042,47 +1062,47 @@ pub const RenderStructure = struct {
return true;
}
fn freeOldMeshes(px: i32, py: i32, pz: i32, renderDistance: i32) !void {
fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: i32) !void {
for(0..storageLists.len) |_lod| {
const lod: u5 = @intCast(_lod);
const maxRenderDistanceNew = renderDistance*chunk.chunkSize << lod;
const maxRenderDistanceOld = lastRD*chunk.chunkSize << lod;
const maxRenderDistanceNew = lastRD*chunk.chunkSize << lod;
const maxRenderDistanceOld = olderRD*chunk.chunkSize << lod;
const size: u31 = chunk.chunkSize << lod;
const mask: i32 = size - 1;
const invMask: i32 = ~mask;
std.debug.assert(@divFloor(2*maxRenderDistanceNew + size-1, size) + 2 <= storageSize);
const minX = lastPx-%maxRenderDistanceOld & invMask;
const maxX = lastPx+%maxRenderDistanceOld+%size & invMask;
const minX = olderPx-%maxRenderDistanceOld & invMask;
const maxX = olderPx+%maxRenderDistanceOld+%size & invMask;
var x = minX;
while(x != maxX): (x +%= size) {
const xIndex = @divExact(x, size) & storageMask;
var deltaXNew: i64 = @abs(x +% size/2 -% px);
var deltaXNew: i64 = @abs(x +% size/2 -% lastPx);
deltaXNew = @max(0, deltaXNew - size/2);
var deltaXOld: i64 = @abs(x +% size/2 -% lastPx);
var deltaXOld: i64 = @abs(x +% size/2 -% olderPx);
deltaXOld = @max(0, deltaXOld - size/2);
const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew);
const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld);
const minY = lastPy-%maxYRenderDistanceOld & invMask;
const maxY = lastPy+%maxYRenderDistanceOld+%size & invMask;
const minY = olderPy-%maxYRenderDistanceOld & invMask;
const maxY = olderPy+%maxYRenderDistanceOld+%size & invMask;
var y = minY;
while(y != maxY): (y +%= size) {
const yIndex = @divExact(y, size) & storageMask;
var deltaYOld: i64 = @abs(y +% size/2 -% lastPy);
var deltaYOld: i64 = @abs(y +% size/2 -% olderPy);
deltaYOld = @max(0, deltaYOld - size/2);
var deltaYNew: i64 = @abs(y +% size/2 -% py);
var deltaYNew: i64 = @abs(y +% size/2 -% lastPy);
deltaYNew = @max(0, deltaYNew - size/2);
var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld);
if(maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size/2;
var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew);
if(maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size/2;
const minZOld = lastPz-%maxZRenderDistanceOld & invMask;
const maxZOld = lastPz+%maxZRenderDistanceOld+%size & invMask;
const minZNew = pz-%maxZRenderDistanceNew & invMask;
const maxZNew = pz+%maxZRenderDistanceNew+%size & invMask;
const minZOld = olderPz-%maxZRenderDistanceOld & invMask;
const maxZOld = olderPz+%maxZRenderDistanceOld+%size & invMask;
const minZNew = lastPz-%maxZRenderDistanceNew & invMask;
const maxZNew = lastPz+%maxZRenderDistanceNew+%size & invMask;
var zValues: [storageSize]i32 = undefined;
var zValuesLen: usize = 0;
@ -1107,13 +1127,14 @@ pub const RenderStructure = struct {
const node = &storageLists[_lod][@intCast(index)];
// Update the neighbors, so we don't get cracks when we look back:
if(node.mesh.load(.Unordered)) |mesh| {
if(node.mesh.load(.Acquire)) |mesh| {
const pos = mesh.pos;
mesh.decreaseRefCount();
node.mesh.store(null, .Unordered);
if(renderDistance != 0) {
node.mesh.store(null, .Release);
if(lastRD != 0) {
for(chunk.Neighbors.iterable) |neighbor| {
if(getNeighborFromRenderThread(pos, pos.voxelSize, neighbor)) |neighborMesh| {
if(neighborMesh.finishedMeshing) {
neighborMesh.needsNeighborUpdate = true;
neighborMesh.increaseRefCount();
try priorityNeighborUpdateList.append(neighborMesh);
@ -1126,6 +1147,7 @@ pub const RenderStructure = struct {
}
}
}
}
fn createNewMeshes(px: i32, py: i32, pz: i32, renderDistance: i32, meshRequests: *std.ArrayList(chunk.ChunkPosition)) !void {
for(0..storageLists.len) |_lod| {
@ -1192,7 +1214,7 @@ pub const RenderStructure = struct {
const pos = chunk.ChunkPosition{.wx=x, .wy=y, .wz=z, .voxelSize=@as(u31, 1)<<lod};
const node = &storageLists[_lod][@intCast(index)];
std.debug.assert(node.mesh.load(.Unordered) == null);
std.debug.assert(node.mesh.load(.Acquire) == null);
try meshRequests.append(pos);
}
}
@ -1212,7 +1234,6 @@ pub const RenderStructure = struct {
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.globalAllocator);
defer meshRequests.deinit();
try freeOldMeshes(px, py, pz, renderDistance);
try createNewMeshes(px, py, pz, renderDistance, &meshRequests);
// Does occlusion using a breadth-first search that caches an on-screen visibility rectangle.
@ -1244,7 +1265,7 @@ pub const RenderStructure = struct {
var lod: u3 = 0;
while(lod <= settings.highestLOD) : (lod += 1) {
const node = getNodeFromRenderThread(firstPos);
if(node.mesh.load(.Unordered) != null) {
if(node.mesh.load(.Acquire) != null and node.mesh.load(.Acquire).?.finishedMeshing) {
node.lod = lod;
node.min = @splat(-1);
node.max = @splat(1);
@ -1268,7 +1289,8 @@ pub const RenderStructure = struct {
while(searchList.removeOrNull()) |data| {
try nodeList.append(data.node);
data.node.active = false;
const mesh = data.node.mesh.load(.Unordered).?;
const mesh = data.node.mesh.load(.Acquire).?;
std.debug.assert(mesh.finishedMeshing);
mesh.visibilityMask = 0xff;
const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos;
const relPosFloat: Vec3f = @floatCast(relPos);
@ -1425,7 +1447,8 @@ pub const RenderStructure = struct {
neighborPos.voxelSize *= 2;
}
const node = getNodeFromRenderThread(neighborPos);
if(node.mesh.load(.Unordered)) |neighborMesh| {
if(node.mesh.load(.Acquire)) |neighborMesh| {
if(!neighborMesh.finishedMeshing) continue;
// 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) {
var isValid: bool = true;
@ -1490,10 +1513,10 @@ pub const RenderStructure = struct {
}
for(nodeList.items) |node| {
node.rendered = false;
const mesh = node.mesh.load(.Unordered).?;
const mesh = node.mesh.load(.Acquire).?;
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});
if(parent.mesh.load(.Unordered)) |parentMesh| {
if(parent.mesh.load(.Acquire)) |parentMesh| {
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);
parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex);
@ -1503,20 +1526,29 @@ pub const RenderStructure = struct {
try mesh.uploadDataAndFinishNeighbors();
mesh.needsNeighborUpdate = false;
}
mutex.lock();
if(mesh.needsMeshUpdate) {
try mesh.uploadData();
mesh.needsMeshUpdate = false;
}
mutex.unlock();
// Remove empty meshes.
if(mesh.opaqueMesh.vertexCount != 0 or mesh.voxelMesh.vertexCount != 0 or mesh.transparentMesh.vertexCount != 0) {
try meshList.append(mesh);
}
}
const olderPx = lastPx;
const olderPy = lastPy;
const olderPz = lastPz;
const olderRD = lastRD;
mutex.lock();
lastPx = px;
lastPy = py;
lastPz = pz;
lastRD = renderDistance;
mutex.unlock();
try freeOldMeshes(olderPx, olderPy, olderPz, olderRD);
// Make requests after updating the, to avoid concurrency issues and reduce the number of requests:
try network.Protocols.chunkRequest.sendRequest(conn, meshRequests.items);
return meshList.items;
@ -1529,7 +1561,7 @@ pub const RenderStructure = struct {
for(blockUpdateList.items) |blockUpdate| {
const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1};
const node = getNodeFromRenderThread(pos);
if(node.mesh.load(.Unordered)) |mesh| {
if(node.mesh.load(.Acquire)) |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.
}
@ -1547,7 +1579,7 @@ pub const RenderStructure = struct {
mutex.unlock();
defer mutex.lock();
mesh.decreaseRefCount();
if(getNodeFromRenderThread(mesh.pos).mesh.load(.Unordered) != mesh) continue; // This mesh isn't used for rendering anymore.
if(getNodeFromRenderThread(mesh.pos).mesh.load(.Acquire) != mesh) continue; // This mesh isn't used for rendering anymore.
if(!mesh.needsNeighborUpdate) continue;
try mesh.uploadDataAndFinishNeighbors();
mesh.needsNeighborUpdate = false;
@ -1555,12 +1587,17 @@ pub const RenderStructure = struct {
}
while (priorityMeshUpdateList.items.len != 0) {
const mesh = priorityMeshUpdateList.orderedRemove(0);
if(!mesh.needsMeshUpdate) {
mutex.unlock();
defer mutex.lock();
mesh.decreaseRefCount();
if(!mesh.needsMeshUpdate) continue;
continue;
}
mesh.needsMeshUpdate = false;
mutex.unlock();
defer mutex.lock();
if(getNodeFromRenderThread(mesh.pos).mesh.load(.Unordered) != mesh) continue; // This mesh isn't used for rendering anymore.
mesh.decreaseRefCount();
if(getNodeFromRenderThread(mesh.pos).mesh.load(.Acquire) != mesh) continue; // This mesh isn't used for rendering anymore.
try mesh.uploadData();
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
}
@ -1595,7 +1632,7 @@ pub const RenderStructure = struct {
if(isInRenderDistance(mesh.pos)) {
const node = getNodeFromRenderThread(mesh.pos);
try mesh.uploadDataAndFinishNeighbors();
if(node.mesh.swap(mesh, .Monotonic)) |oldMesh| {
if(node.mesh.swap(mesh, .AcqRel)) |oldMesh| {
oldMesh.decreaseRefCount();
}
} else {
@ -1620,6 +1657,20 @@ pub const RenderStructure = struct {
mesh.needsMeshUpdate = true;
}
pub fn addMeshToStorage(mesh: *chunk.meshing.ChunkMesh) void {
mutex.lock();
if(isInRenderDistance(mesh.pos)) {
mesh.increaseRefCount();
const node = getNodeFromRenderThread(mesh.pos);
if(node.mesh.swap(mesh, .AcqRel)) |oldMesh| {
mutex.unlock();
oldMesh.decreaseRefCount();
return;
}
}
mutex.unlock();
}
pub const MeshGenerationTask = struct {
mesh: *chunk.Chunk,