diff --git a/src/main.zig b/src/main.zig index 038acb70..dd46fd2d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -31,6 +31,7 @@ pub const heap = @import("utils/heap.zig"); pub const List = @import("utils/list.zig").List; pub const ListUnmanaged = @import("utils/list.zig").ListUnmanaged; +pub const MultiArray = @import("utils/list.zig").MultiArray; pub const VirtualList = @import("utils/list.zig").VirtualList; const file_monitor = utils.file_monitor; diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 530e1df4..6606dd6b 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -286,16 +286,32 @@ pub const IndirectData = extern struct { }; const PrimitiveMesh = struct { // MARK: PrimitiveMesh - coreFaces: main.ListUnmanaged(FaceData) = .{}, - neighborFacesSameLod: [6]main.ListUnmanaged(FaceData) = @splat(.{}), - neighborFacesHigherLod: [6]main.ListUnmanaged(FaceData) = @splat(.{}), - optionalFaces: main.ListUnmanaged(FaceData) = .{}, - completeList: []FaceData = &.{}, - coreLen: u32 = 0, - sameLodLens: [6]u32 = @splat(0), - higherLodLens: [6]u32 = @splat(0), - optionalLen: u32 = 0, - mutex: std.Thread.Mutex = .{}, + const FaceGroups = enum(u32) { + core, + neighbor0, + neighbor1, + neighbor2, + neighbor3, + neighbor4, + neighbor5, + neighborLod0, + neighborLod1, + neighborLod2, + neighborLod3, + neighborLod4, + neighborLod5, + optional, + + pub fn neighbor(n: main.chunk.Neighbor) FaceGroups { + return @enumFromInt(@intFromEnum(FaceGroups.neighbor0) + @intFromEnum(n)); + } + + pub fn neighborLod(n: main.chunk.Neighbor) FaceGroups { + return @enumFromInt(@intFromEnum(FaceGroups.neighborLod0) + @intFromEnum(n)); + } + }; + completeList: main.MultiArray(FaceData, FaceGroups) = .{}, + lock: main.utils.ReadWriteLock = .{}, bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0}, vertexCount: u31 = 0, byNormalCount: [14]u32 = @splat(0), @@ -304,86 +320,23 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh max: Vec3f = undefined, fn deinit(self: *PrimitiveMesh) void { - faceBuffer.free(self.bufferAllocation); - self.coreFaces.deinit(main.globalAllocator); - self.optionalFaces.deinit(main.globalAllocator); - for(&self.neighborFacesSameLod) |*neighborFaces| { - neighborFaces.deinit(main.globalAllocator); - } - for(&self.neighborFacesHigherLod) |*neighborFaces| { - neighborFaces.deinit(main.globalAllocator); - } - main.globalAllocator.free(self.completeList); + self.completeList.deinit(main.globalAllocator); } - fn reset(self: *PrimitiveMesh) void { - self.coreFaces.clearRetainingCapacity(); - for(&self.neighborFacesSameLod) |*neighborFaces| { - neighborFaces.clearRetainingCapacity(); - } - for(&self.neighborFacesHigherLod) |*neighborFaces| { - neighborFaces.clearRetainingCapacity(); - } - self.optionalFaces.clearRetainingCapacity(); - } - - fn appendInternalQuadsToCore(self: *PrimitiveMesh, block: Block, x: i32, y: i32, z: i32, comptime backFace: bool, optional: bool) void { - const model = blocks.meshes.model(block); - models.models.items[model].appendInternalQuadsToList(if(optional) &self.optionalFaces else &self.coreFaces, main.globalAllocator, block, x, y, z, backFace); - } - - fn appendNeighborFacingQuadsToCore(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, optional: bool) void { - const model = blocks.meshes.model(block); - models.models.items[model].appendNeighborFacingQuadsToList(if(optional) &self.optionalFaces else &self.coreFaces, main.globalAllocator, block, neighbor, x, y, z, backFace); - } - - fn appendNeighborFacingQuadsToNeighbor(self: *PrimitiveMesh, block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, comptime isLod: bool) void { - const model = blocks.meshes.model(block); - if(isLod) { - models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesHigherLod[neighbor.reverse().toInt()], main.globalAllocator, block, neighbor, x, y, z, backFace); - } else { - models.models.items[model].appendNeighborFacingQuadsToList(&self.neighborFacesSameLod[neighbor.reverse().toInt()], main.globalAllocator, block, neighbor, x, y, z, backFace); - } - } - - fn clearNeighbor(self: *PrimitiveMesh, neighbor: chunk.Neighbor, comptime isLod: bool) void { - if(isLod) { - self.neighborFacesHigherLod[neighbor.toInt()].clearRetainingCapacity(); - } else { - self.neighborFacesSameLod[neighbor.toInt()].clearRetainingCapacity(); - } + fn replaceRange(self: *PrimitiveMesh, group: FaceGroups, items: []const FaceData) void { + self.lock.lockWrite(); + self.completeList.replaceRange(main.globalAllocator, group, items); + self.lock.unlockWrite(); } fn finish(self: *PrimitiveMesh, parent: *ChunkMesh, lightList: *main.List(u32), lightMap: *std.AutoHashMap([4]u32, u16)) void { - var len: usize = self.coreFaces.items.len; - for(self.neighborFacesSameLod) |neighborFaces| { - len += neighborFaces.items.len; - } - for(self.neighborFacesHigherLod) |neighborFaces| { - len += neighborFaces.items.len; - } - len += self.optionalFaces.items.len; - const completeList = main.globalAllocator.alloc(FaceData, len); - var i: usize = 0; - @memcpy(completeList[i..][0..self.coreFaces.items.len], self.coreFaces.items); - i += self.coreFaces.items.len; - for(self.neighborFacesSameLod) |neighborFaces| { - @memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items); - i += neighborFaces.items.len; - } - for(self.neighborFacesHigherLod) |neighborFaces| { - @memcpy(completeList[i..][0..neighborFaces.items.len], neighborFaces.items); - i += neighborFaces.items.len; - } - @memcpy(completeList[i..][0..self.optionalFaces.items.len], self.optionalFaces.items); - i += self.optionalFaces.items.len; - self.min = @splat(std.math.floatMax(f32)); self.max = @splat(-std.math.floatMax(f32)); + self.lock.lockRead(); parent.lightingData[0].lock.lockRead(); parent.lightingData[1].lock.lockRead(); - for(completeList) |*face| { + for(self.completeList.getEverything()) |*face| { const light = getLight(parent, .{face.position.x, face.position.y, face.position.z}, face.blockAndQuad.texture, face.blockAndQuad.quadIndex); const result = lightMap.getOrPut(light) catch unreachable; if(!result.found_existing) { @@ -403,20 +356,7 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } parent.lightingData[0].lock.unlockRead(); parent.lightingData[1].lock.unlockRead(); - - self.mutex.lock(); - const oldList = self.completeList; - self.completeList = completeList; - self.coreLen = @intCast(self.coreFaces.items.len); - for(self.neighborFacesSameLod, 0..) |neighborFaces, j| { - self.sameLodLens[j] = @intCast(neighborFaces.items.len); - } - for(self.neighborFacesHigherLod, 0..) |neighborFaces, j| { - self.higherLodLens[j] = @intCast(neighborFaces.items.len); - } - self.optionalLen = @intCast(self.optionalFaces.items.len); - self.mutex.unlock(); - main.globalAllocator.free(oldList); + self.lock.unlockRead(); } fn getValues(mesh: *ChunkMesh, wx: i32, wy: i32, wz: i32) [6]u8 { @@ -584,36 +524,28 @@ const PrimitiveMesh = struct { // MARK: PrimitiveMesh } fn uploadData(self: *PrimitiveMesh, isNeighborLod: [6]bool) void { - self.mutex.lock(); - defer self.mutex.unlock(); - var len: u32 = self.coreLen; - var offset: u32 = self.coreLen; + self.lock.lockRead(); + defer self.lock.unlockRead(); + var len: usize = 0; + const coreList = self.completeList.getRange(.core); + len += coreList.len; + const optionalList = self.completeList.getRange(.optional); + len += optionalList.len; var list: [6][]FaceData = undefined; for(0..6) |i| { - const neighborLen = self.sameLodLens[i]; if(!isNeighborLod[i]) { - list[i] = self.completeList[offset..][0..neighborLen]; - len += neighborLen; + list[i] = self.completeList.getRange(.neighbor(@enumFromInt(i))); + } else { + list[i] = self.completeList.getRange(.neighborLod(@enumFromInt(i))); } - offset += neighborLen; + len += list[i].len; } - for(0..6) |i| { - const neighborLen = self.higherLodLens[i]; - if(isNeighborLod[i]) { - list[i] = self.completeList[offset..][0..neighborLen]; - len += neighborLen; - } - offset += neighborLen; - } - const optionalList = self.completeList[offset..][0..self.optionalLen]; - offset += self.optionalLen; - len += self.optionalLen; + const fullBuffer = faceBuffer.allocateAndMapRange(len, &self.bufferAllocation); defer faceBuffer.unmapRange(fullBuffer); // Sort the faces by normal to allow for backface culling on the GPU: var i: u32 = 0; var iStart = i; - const coreList = self.completeList[0..self.coreLen]; for(0..7) |normal| { for(coreList) |face| { if(main.models.extraQuadInfos.items[face.blockAndQuad.quadIndex].alignedNormalDirection) |normalDir| { @@ -870,10 +802,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } pub fn generateLightingData(self: *ChunkMesh) error{AlreadyStored}!void { - self.mutex.lock(); - self.opaqueMesh.reset(); - self.transparentMesh.reset(); - self.mutex.unlock(); try mesh_storage.addMeshToStorage(self); var lightRefreshList = main.List(*ChunkMesh).init(main.stackAllocator); @@ -922,6 +850,16 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } } + fn appendInternalQuads(block: Block, x: i32, y: i32, z: i32, comptime backFace: bool, list: *main.ListUnmanaged(FaceData), allocator: main.heap.NeverFailingAllocator) void { + const model = blocks.meshes.model(block); + models.models.items[model].appendInternalQuadsToList(list, allocator, block, x, y, z, backFace); + } + + fn appendNeighborFacingQuads(block: Block, neighbor: chunk.Neighbor, x: i32, y: i32, z: i32, comptime backFace: bool, list: *main.ListUnmanaged(FaceData), allocator: main.heap.NeverFailingAllocator) void { + const model = blocks.meshes.model(block); + models.models.items[model].appendNeighborFacingQuadsToList(list, allocator, block, neighbor, x, y, z, backFace); + } + pub fn generateMesh(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void { var alwaysViewThroughMask: [chunk.chunkSize][chunk.chunkSize]u32 = undefined; @memset(std.mem.asBytes(&alwaysViewThroughMask), 0); @@ -933,6 +871,16 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var hasFaces: [chunk.chunkSize][chunk.chunkSize]u32 = undefined; @memset(std.mem.asBytes(&hasFaces), 0); self.mutex.lock(); + + var transparentCore: main.ListUnmanaged(FaceData) = .{}; + defer transparentCore.deinit(main.stackAllocator); + var opaqueCore: main.ListUnmanaged(FaceData) = .{}; + defer opaqueCore.deinit(main.stackAllocator); + var transparentOptional: main.ListUnmanaged(FaceData) = .{}; + defer transparentOptional.deinit(main.stackAllocator); + var opaqueOptional: main.ListUnmanaged(FaceData) = .{}; + defer opaqueOptional.deinit(main.stackAllocator); + const OcclusionInfo = packed struct { canSeeNeighbor: u6 = 0, canSeeAllNeighbors: bool = false, @@ -1029,9 +977,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh if(occlusionInfo.hasInternalQuads) { const block = self.chunk.data.palette[paletteId]; if(block.transparent()) { - self.transparentMesh.appendInternalQuadsToCore(block, x, y, z, false, false); + appendInternalQuads(block, x, y, z, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendInternalQuadsToCore(block, x, y, z, false, false); + appendInternalQuads(block, x, y, z, false, &opaqueCore, main.stackAllocator); } } } @@ -1055,11 +1003,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false, false); + appendNeighborFacingQuads(block, neighbor, @intCast(x - 1), @intCast(y), z, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x - 1), @intCast(y), z, false, initialAlwaysViewThroughMask[x - 1][y] & setBit != 0); + appendNeighborFacingQuads(block, neighbor, @intCast(x - 1), @intCast(y), z, false, if(initialAlwaysViewThroughMask[x - 1][y] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator); } } } @@ -1082,11 +1030,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false, false); + appendNeighborFacingQuads(block, neighbor, @intCast(x + 1), @intCast(y), z, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x + 1), @intCast(y), z, false, initialAlwaysViewThroughMask[x + 1][y] & setBit != 0); + appendNeighborFacingQuads(block, neighbor, @intCast(x + 1), @intCast(y), z, false, if(initialAlwaysViewThroughMask[x + 1][y] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator); } } } @@ -1109,11 +1057,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false, false); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y - 1), z, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y - 1), z, false, initialAlwaysViewThroughMask[x][y - 1] & setBit != 0); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y - 1), z, false, if(initialAlwaysViewThroughMask[x][y - 1] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator); } } } @@ -1136,11 +1084,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false, false); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y + 1), z, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y + 1), z, false, initialAlwaysViewThroughMask[x][y + 1] & setBit != 0); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y + 1), z, false, if(initialAlwaysViewThroughMask[x][y + 1] & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator); } } } @@ -1163,11 +1111,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false, false); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z - 1, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z - 1, false, initialAlwaysViewThroughMask[x][y] << 1 & setBit != 0); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z - 1, false, if(initialAlwaysViewThroughMask[x][y] << 1 & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator); } } } @@ -1190,11 +1138,11 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh } if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), @intCast(x), @intCast(y), z, true, &transparentCore, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false, false); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z + 1, false, &transparentCore, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToCore(block, neighbor, @intCast(x), @intCast(y), z + 1, false, initialAlwaysViewThroughMask[x][y] >> 1 & setBit != 0); + appendNeighborFacingQuads(block, neighbor, @intCast(x), @intCast(y), z + 1, false, if(initialAlwaysViewThroughMask[x][y] >> 1 & setBit != 0) &opaqueOptional else &opaqueCore, main.stackAllocator); } } } @@ -1203,6 +1151,12 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh self.mutex.unlock(); + self.opaqueMesh.replaceRange(.core, opaqueCore.items); + self.opaqueMesh.replaceRange(.optional, opaqueOptional.items); + + self.transparentMesh.replaceRange(.core, transparentCore.items); + self.transparentMesh.replaceRange(.optional, transparentOptional.items); + self.finishNeighbors(lightRefreshList); } @@ -1243,10 +1197,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh if(neighborBlock.mode().dependsOnNeighbors) { if(neighborBlock.mode().updateData(&neighborBlock, neighbor.reverse(), newBlock)) { neighborChunkMesh.chunk.data.setValue(index, neighborBlock); - neighborChunkMesh.opaqueMesh.coreFaces.clearRetainingCapacity(); - neighborChunkMesh.transparentMesh.coreFaces.clearRetainingCapacity(); - neighborChunkMesh.opaqueMesh.optionalFaces.clearRetainingCapacity(); - neighborChunkMesh.transparentMesh.optionalFaces.clearRetainingCapacity(); neighborChunkMesh.mutex.unlock(); neighborChunkMesh.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock, &lightRefreshList); neighborChunkMesh.generateMesh(&lightRefreshList); @@ -1302,10 +1252,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh self.lastNeighborsHigherLod[chunk.Neighbor.dirUp.toInt()] = null; self.lastNeighborsSameLod[chunk.Neighbor.dirUp.toInt()] = null; } - self.opaqueMesh.coreFaces.clearRetainingCapacity(); - self.transparentMesh.coreFaces.clearRetainingCapacity(); - self.opaqueMesh.optionalFaces.clearRetainingCapacity(); - self.transparentMesh.optionalFaces.clearRetainingCapacity(); self.mutex.unlock(); self.generateMesh(&lightRefreshList); // TODO: Batch mesh updates instead of applying them for each block changes. self.mutex.lock(); @@ -1319,7 +1265,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh self.uploadData(); } - fn clearNeighbor(self: *ChunkMesh, neighbor: chunk.Neighbor, comptime isLod: bool) void { + fn clearNeighborA(self: *ChunkMesh, neighbor: chunk.Neighbor, comptime isLod: bool) void { self.opaqueMesh.clearNeighbor(neighbor, isLod); self.transparentMesh.clearNeighbor(neighbor, isLod); } @@ -1377,8 +1323,16 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh if(self.lastNeighborsSameLod[neighbor.toInt()] == neighborMesh) break :sameLodBlock; self.lastNeighborsSameLod[neighbor.toInt()] = neighborMesh; neighborMesh.lastNeighborsSameLod[neighbor.reverse().toInt()] = self; - self.clearNeighbor(neighbor, false); - neighborMesh.clearNeighbor(neighbor.reverse(), false); + + var transparentSelf: main.ListUnmanaged(FaceData) = .{}; + defer transparentSelf.deinit(main.stackAllocator); + var opaqueSelf: main.ListUnmanaged(FaceData) = .{}; + defer opaqueSelf.deinit(main.stackAllocator); + var transparentNeighbor: main.ListUnmanaged(FaceData) = .{}; + defer transparentNeighbor.deinit(main.stackAllocator); + var opaqueNeighbor: main.ListUnmanaged(FaceData) = .{}; + defer opaqueNeighbor.deinit(main.stackAllocator); + const x3: i32 = if(neighbor.isPositive()) chunk.chunkMask else 0; var x1: i32 = 0; while(x1 < chunk.chunkSize) : (x1 += 1) { @@ -1410,25 +1364,30 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { if(block.transparent()) { if(block.hasBackFace()) { - self.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor.reverse(), x, y, z, true, false); + appendNeighborFacingQuads(block, neighbor.reverse(), x, y, z, true, &transparentSelf, main.stackAllocator); } - neighborMesh.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor, otherX, otherY, otherZ, false, false); + appendNeighborFacingQuads(block, neighbor, otherX, otherY, otherZ, false, &transparentNeighbor, main.stackAllocator); } else { - neighborMesh.opaqueMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor, otherX, otherY, otherZ, false, false); + appendNeighborFacingQuads(block, neighbor, otherX, otherY, otherZ, false, &opaqueNeighbor, main.stackAllocator); } } if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor.reverse())) { if(otherBlock.transparent()) { if(otherBlock.hasBackFace()) { - neighborMesh.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor, otherX, otherY, otherZ, true, false); + appendNeighborFacingQuads(otherBlock, neighbor, otherX, otherY, otherZ, true, &transparentNeighbor, main.stackAllocator); } - self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, false); + appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &transparentSelf, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, false); + appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &opaqueSelf, main.stackAllocator); } } } } + self.opaqueMesh.replaceRange(.neighbor(neighbor), opaqueSelf.items); + self.transparentMesh.replaceRange(.neighbor(neighbor), transparentSelf.items); + neighborMesh.opaqueMesh.replaceRange(.neighbor(neighbor.reverse()), opaqueNeighbor.items); + neighborMesh.transparentMesh.replaceRange(.neighbor(neighbor.reverse()), transparentNeighbor.items); + _ = neighborMesh.needsLightRefresh.store(true, .release); neighborMesh.increaseRefCount(); lightRefreshList.append(neighborMesh); @@ -1436,7 +1395,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh self.mutex.lock(); defer self.mutex.unlock(); if(self.lastNeighborsSameLod[neighbor.toInt()] != null) { - self.clearNeighbor(neighbor, false); + self.opaqueMesh.replaceRange(.neighbor(neighbor), &.{}); + self.transparentMesh.replaceRange(.neighbor(neighbor), &.{}); self.lastNeighborsSameLod[neighbor.toInt()] = null; } } @@ -1446,7 +1406,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh self.mutex.lock(); defer self.mutex.unlock(); if(self.lastNeighborsHigherLod[neighbor.toInt()] != null) { - self.clearNeighbor(neighbor, true); + self.opaqueMesh.replaceRange(.neighborLod(neighbor), &.{}); + self.transparentMesh.replaceRange(.neighborLod(neighbor), &.{}); self.lastNeighborsHigherLod[neighbor.toInt()] = null; } continue; @@ -1457,7 +1418,12 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh defer neighborMesh.mutex.unlock(); if(self.lastNeighborsHigherLod[neighbor.toInt()] == neighborMesh) continue; self.lastNeighborsHigherLod[neighbor.toInt()] = neighborMesh; - self.clearNeighbor(neighbor, true); + + var transparentSelf: main.ListUnmanaged(FaceData) = .{}; + defer transparentSelf.deinit(main.stackAllocator); + var opaqueSelf: main.ListUnmanaged(FaceData) = .{}; + defer opaqueSelf.deinit(main.stackAllocator); + const x3: i32 = if(neighbor.isPositive()) chunk.chunkMask else 0; const offsetX = @divExact(self.pos.wx, self.pos.voxelSize) & chunk.chunkSize; const offsetY = @divExact(self.pos.wy, self.pos.voxelSize) & chunk.chunkSize; @@ -1491,18 +1457,20 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh if(settings.leavesQuality == 0) otherBlock.typ = otherBlock.opaqueVariant(); if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor.reverse())) { if(otherBlock.transparent()) { - self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, true); + appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &transparentSelf, main.stackAllocator); } else { - self.opaqueMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor.reverse(), x, y, z, false, true); + appendNeighborFacingQuads(otherBlock, neighbor.reverse(), x, y, z, false, &opaqueSelf, main.stackAllocator); } } if(block.hasBackFace()) { if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { - self.transparentMesh.appendNeighborFacingQuadsToNeighbor(block, neighbor.reverse(), x, y, z, true, true); + appendNeighborFacingQuads(block, neighbor.reverse(), x, y, z, true, &transparentSelf, main.stackAllocator); } } } } + self.opaqueMesh.replaceRange(.neighborLod(neighbor), opaqueSelf.items); + self.transparentMesh.replaceRange(.neighborLod(neighbor), transparentSelf.items); } self.mutex.lock(); defer self.mutex.unlock(); @@ -1541,33 +1509,26 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh var needsUpdate: bool = false; if(self.transparentMesh.wasChanged) { self.transparentMesh.wasChanged = false; - self.transparentMesh.mutex.lock(); - defer self.transparentMesh.mutex.unlock(); - var len: usize = self.transparentMesh.coreLen; - var offset: usize = self.transparentMesh.coreLen; + self.transparentMesh.lock.lockRead(); + defer self.transparentMesh.lock.unlockRead(); + var len: usize = 0; + const coreList = self.transparentMesh.completeList.getRange(.core); + len += coreList.len; var list: [6][]FaceData = undefined; for(0..6) |i| { - const neighborLen = self.transparentMesh.sameLodLens[i]; if(!self.isNeighborLod[i]) { - list[i] = self.transparentMesh.completeList[offset..][0..neighborLen]; - len += neighborLen; + list[i] = self.transparentMesh.completeList.getRange(.neighbor(@enumFromInt(i))); + } else { + list[i] = self.transparentMesh.completeList.getRange(.neighborLod(@enumFromInt(i))); } - offset += neighborLen; - } - for(0..6) |i| { - const neighborLen = self.transparentMesh.higherLodLens[i]; - if(self.isNeighborLod[i]) { - list[i] = self.transparentMesh.completeList[offset..][0..neighborLen]; - len += neighborLen; - } - offset += neighborLen; + len += list[i].len; } self.currentSorting = main.globalAllocator.realloc(self.currentSorting, len); self.sortingOutputBuffer = main.globalAllocator.realloc(self.sortingOutputBuffer, len + self.blockBreakingFaces.items.len); - for(0..self.transparentMesh.coreLen) |i| { - self.currentSorting[i].face = self.transparentMesh.completeList[i]; + for(0..coreList.len) |i| { + self.currentSorting[i].face = coreList[i]; } - offset = self.transparentMesh.coreLen; + var offset = coreList.len; for(0..6) |n| { for(0..list[n].len) |i| { self.currentSorting[offset + i].face = list[n][i]; diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index 2f2222d2..82c11dc6 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -980,7 +980,9 @@ fn addBreakingAnimationFace(pos: Vec3i, quadIndex: u16, texture: u16, neighbor: defer mesh.mutex.unlock(); const lightIndex = blk: { const meshData = if(isTransparent) &mesh.transparentMesh else &mesh.opaqueMesh; - for(meshData.completeList) |face| { + meshData.lock.lockRead(); + defer meshData.lock.unlockRead(); + for(meshData.completeList.getEverything()) |face| { if(face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) { break :blk face.position.lightIndex; } diff --git a/src/utils/list.zig b/src/utils/list.zig index 1a5ca40b..f0774299 100644 --- a/src/utils/list.zig +++ b/src/utils/list.zig @@ -433,6 +433,85 @@ pub fn ListUnmanaged(comptime T: type) type { }; } +/// Holds multiple arrays sequentially in memory. +/// Allows addressing and remove each subarray individually, as well as iterating through all of them at once. +pub fn MultiArray(T: type, Range: type) type { + const size = @typeInfo(Range).@"enum".fields.len; + std.debug.assert(@typeInfo(Range).@"enum".is_exhaustive); + for(@typeInfo(Range).@"enum".fields) |field| { + std.debug.assert(field.value < size); + } + return struct { + offsets: [size + 1]usize = @splat(0), + items: [*]T = undefined, + capacity: usize = 0, + + pub fn initCapacity(allocator: NeverFailingAllocator, capacity: usize) @This() { + return .{ + .items = allocator.alloc(T, capacity)[0..0], + .capacity = capacity, + }; + } + + pub fn deinit(self: @This(), allocator: NeverFailingAllocator) void { + if(self.capacity != 0) { + allocator.free(self.items[0..self.capacity]); + } + } + + pub fn clearAndFree(self: *@This(), allocator: NeverFailingAllocator) void { + self.deinit(allocator); + self.* = .{}; + } + + pub fn clearRetainingCapacity(self: *@This()) void { + self.offsets = @splat(0); + } + + pub fn ensureCapacity(self: *@This(), allocator: NeverFailingAllocator, newCapacity: usize) void { + if(newCapacity <= self.capacity) return; + const newAllocation = allocator.realloc(self.items[0..self.capacity], newCapacity); + self.items = newAllocation.ptr; + self.capacity = newAllocation.len; + } + + pub fn addMany(self: *@This(), allocator: NeverFailingAllocator, n: usize) []T { + self.ensureFreeCapacity(allocator, n); + return self.addManyAssumeCapacity(n); + } + + pub fn replaceRange(self: *@This(), allocator: NeverFailingAllocator, range: Range, elems: []const T) void { + const i: usize = @intFromEnum(range); + const oldLen = self.offsets[i + 1] - self.offsets[i]; + self.ensureCapacity(allocator, self.offsets[size] - oldLen + elems.len); + const startIndex = self.offsets[i + 1]; + const newStartIndex = self.offsets[i + 1] - oldLen + elems.len; + const endIndex = self.offsets[size]; + const newEndIndex = self.offsets[size] - oldLen + elems.len; + if(newStartIndex > startIndex) { + std.mem.copyBackwards(T, self.items[newStartIndex..newEndIndex], self.items[startIndex..endIndex]); + } else { + std.mem.copyForwards(T, self.items[newStartIndex..newEndIndex], self.items[startIndex..endIndex]); + } + @memcpy(self.items[self.offsets[i]..][0..elems.len], elems); + for(self.offsets[i + 1 ..]) |*offset| { + offset.* = offset.* - oldLen + elems.len; + } + } + + pub fn getRange(self: *@This(), range: Range) []T { + const i: usize = @intFromEnum(range); + const startIndex = self.offsets[i]; + const endIndex = self.offsets[i + 1]; + return self.items[startIndex..endIndex]; + } + + pub fn getEverything(self: *@This()) []T { + return self.items[0..self.offsets[size]]; + } + }; +} + const page_size_min = std.heap.page_size_min; const page_size_max = std.heap.page_size_max; const pageSize = std.heap.pageSize;