mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-08 03:29:48 -04:00
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:
parent
a61fe26fab
commit
00f8b79ec4
@ -527,6 +527,7 @@ pub const meshing = struct {
|
|||||||
neighborFacesSameLod: [6]std.ArrayListUnmanaged(FaceData) = [_]std.ArrayListUnmanaged(FaceData){.{}} ** 6,
|
neighborFacesSameLod: [6]std.ArrayListUnmanaged(FaceData) = [_]std.ArrayListUnmanaged(FaceData){.{}} ** 6,
|
||||||
neighborFacesHigherLod: [6]std.ArrayListUnmanaged(FaceData) = [_]std.ArrayListUnmanaged(FaceData){.{}} ** 6,
|
neighborFacesHigherLod: [6]std.ArrayListUnmanaged(FaceData) = [_]std.ArrayListUnmanaged(FaceData){.{}} ** 6,
|
||||||
completeList: []FaceData = &.{},
|
completeList: []FaceData = &.{},
|
||||||
|
mutex: std.Thread.Mutex = .{},
|
||||||
bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0},
|
bufferAllocation: graphics.SubAllocation = .{.start = 0, .len = 0},
|
||||||
vertexCount: u31 = 0,
|
vertexCount: u31 = 0,
|
||||||
wasChanged: bool = false,
|
wasChanged: bool = false,
|
||||||
@ -586,23 +587,22 @@ pub const meshing = struct {
|
|||||||
for(neighborFaceLists) |neighborFaces| {
|
for(neighborFaceLists) |neighborFaces| {
|
||||||
len += neighborFaces.len;
|
len += neighborFaces.len;
|
||||||
}
|
}
|
||||||
if(main.globalAllocator.resize(self.completeList, len)) {
|
const completeList = try main.globalAllocator.alloc(FaceData, len);
|
||||||
self.completeList.len = len;
|
|
||||||
} else {
|
|
||||||
main.globalAllocator.free(self.completeList);
|
|
||||||
self.completeList = try main.globalAllocator.alloc(FaceData, len);
|
|
||||||
}
|
|
||||||
var i: usize = 0;
|
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;
|
i += self.coreFaces.items.len;
|
||||||
for(neighborFaceLists) |neighborFaces| {
|
for(neighborFaceLists) |neighborFaces| {
|
||||||
@memcpy(self.completeList[i..][0..neighborFaces.len], neighborFaces);
|
@memcpy(completeList[i..][0..neighborFaces.len], neighborFaces);
|
||||||
i += neighborFaces.len;
|
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);
|
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 {
|
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) {
|
if(x == x & chunkMask and y == y & chunkMask and z == z & chunkMask) {
|
||||||
return getValues(parent, wx, wy, wz);
|
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.
|
// TODO: If the neighbor mesh has a higher lod the transition isn't seamless.
|
||||||
return getValues(neighborMesh, wx, wy, wz);
|
return getValues(neighborMesh, wx, wy, wz);
|
||||||
}
|
}
|
||||||
@ -673,6 +674,8 @@ pub const meshing = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn uploadData(self: *PrimitiveMesh) !void {
|
fn uploadData(self: *PrimitiveMesh) !void {
|
||||||
|
self.mutex.lock();
|
||||||
|
defer self.mutex.unlock();
|
||||||
self.vertexCount = @intCast(6*self.completeList.len);
|
self.vertexCount = @intCast(6*self.completeList.len);
|
||||||
try faceBuffer.uploadData(self.completeList, &self.bufferAllocation);
|
try faceBuffer.uploadData(self.completeList, &self.bufferAllocation);
|
||||||
self.wasChanged = true;
|
self.wasChanged = true;
|
||||||
@ -780,6 +783,7 @@ pub const meshing = struct {
|
|||||||
refCount: std.atomic.Value(u32) = std.atomic.Value(u32).init(1),
|
refCount: std.atomic.Value(u32) = std.atomic.Value(u32).init(1),
|
||||||
needsNeighborUpdate: bool = false,
|
needsNeighborUpdate: bool = false,
|
||||||
needsMeshUpdate: bool = false,
|
needsMeshUpdate: bool = false,
|
||||||
|
finishedMeshing: bool = false,
|
||||||
mutex: std.Thread.Mutex = .{},
|
mutex: std.Thread.Mutex = .{},
|
||||||
|
|
||||||
chunkBorders: [6]BoundingRectToNeighborChunk = [1]BoundingRectToNeighborChunk{.{}} ** 6,
|
chunkBorders: [6]BoundingRectToNeighborChunk = [1]BoundingRectToNeighborChunk{.{}} ** 6,
|
||||||
@ -858,6 +862,8 @@ pub const meshing = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn regenerateMainMesh(self: *ChunkMesh) !void {
|
pub fn regenerateMainMesh(self: *ChunkMesh) !void {
|
||||||
|
renderer.RenderStructure.addMeshToStorage(self);
|
||||||
|
self.mutex.lock();
|
||||||
self.opaqueMesh.reset();
|
self.opaqueMesh.reset();
|
||||||
self.voxelMesh.reset();
|
self.voxelMesh.reset();
|
||||||
self.transparentMesh.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.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);
|
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 {
|
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 x = _x & chunkMask;
|
||||||
const y = _y & chunkMask;
|
const y = _y & chunkMask;
|
||||||
const z = _z & chunkMask;
|
const z = _z & chunkMask;
|
||||||
const oldBlock = self.chunk.blocks[getIndex(x, y, z)];
|
const oldBlock = self.chunk.blocks[getIndex(x, y, z)];
|
||||||
for(Neighbors.iterable) |neighbor| {
|
for(Neighbors.iterable) |neighbor| {
|
||||||
var neighborMesh = self;
|
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 nx = x + Neighbors.relX[neighbor];
|
||||||
var ny = y + Neighbors.relY[neighbor];
|
var ny = y + Neighbors.relY[neighbor];
|
||||||
var nz = z + Neighbors.relZ[neighbor];
|
var nz = z + Neighbors.relZ[neighbor];
|
||||||
@ -1028,10 +1042,12 @@ pub const meshing = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(neighborMesh != self) {
|
if(neighborMesh != self) {
|
||||||
|
try neighborMesh.finishData();
|
||||||
try neighborMesh.uploadData();
|
try neighborMesh.uploadData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.chunk.blocks[getIndex(x, y, z)] = newBlock;
|
self.chunk.blocks[getIndex(x, y, z)] = newBlock;
|
||||||
|
try self.finishData();
|
||||||
try self.uploadData();
|
try self.uploadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1049,7 +1065,8 @@ pub const meshing = struct {
|
|||||||
self.transparentMesh.clearNeighbor(neighbor, isLod);
|
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 = .{
|
const isNeighborLod: [6]bool = .{
|
||||||
self.lastNeighborsSameLod[0] == null or self.forceHigherLod[0],
|
self.lastNeighborsSameLod[0] == null or self.forceHigherLod[0],
|
||||||
self.lastNeighborsSameLod[1] == null or self.forceHigherLod[1],
|
self.lastNeighborsSameLod[1] == null or self.forceHigherLod[1],
|
||||||
@ -1063,10 +1080,19 @@ pub const meshing = struct {
|
|||||||
try self.transparentMesh.finish(self, isNeighborLod);
|
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 {
|
pub fn changeLodBorders(self: *ChunkMesh, forceHigherLod: [6]bool) !void {
|
||||||
if(!std.meta.eql(forceHigherLod, self.forceHigherLod)) {
|
if(!std.meta.eql(forceHigherLod, self.forceHigherLod)) {
|
||||||
|
self.mutex.lock();
|
||||||
self.forceHigherLod = forceHigherLod;
|
self.forceHigherLod = forceHigherLod;
|
||||||
|
try self.finishData();
|
||||||
try self.uploadData();
|
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;
|
const getNeighborMesh: fn(ChunkPosition, u31, u3) ?*ChunkMesh = if(inRenderThread) renderer.RenderStructure.getNeighborFromRenderThread else renderer.RenderStructure.getNeighborAndIncreaseRefCount;
|
||||||
for(Neighbors.iterable) |neighbor| {
|
for(Neighbors.iterable) |neighbor| {
|
||||||
const nullNeighborMesh = getNeighborMesh(self.pos, self.pos.voxelSize, neighbor);
|
const nullNeighborMesh = getNeighborMesh(self.pos, self.pos.voxelSize, neighbor);
|
||||||
if(nullNeighborMesh) |neighborMesh| {
|
if(nullNeighborMesh) |neighborMesh| sameLodBlock: {
|
||||||
defer if(!inRenderThread) neighborMesh.decreaseRefCount();
|
defer if(!inRenderThread) neighborMesh.decreaseRefCount();
|
||||||
std.debug.assert(neighborMesh != self);
|
std.debug.assert(neighborMesh != self);
|
||||||
deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex);
|
deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex);
|
||||||
defer self.mutex.unlock();
|
defer self.mutex.unlock();
|
||||||
defer neighborMesh.mutex.unlock();
|
defer neighborMesh.mutex.unlock();
|
||||||
if(self.lastNeighborsSameLod[neighbor] == neighborMesh) continue;
|
if(self.lastNeighborsSameLod[neighbor] == neighborMesh) break :sameLodBlock;
|
||||||
self.lastNeighborsSameLod[neighbor] = neighborMesh;
|
self.lastNeighborsSameLod[neighbor] = neighborMesh;
|
||||||
neighborMesh.lastNeighborsSameLod[neighbor ^ 1] = self;
|
neighborMesh.lastNeighborsSameLod[neighbor ^ 1] = self;
|
||||||
self.clearNeighbor(neighbor, false);
|
self.clearNeighbor(neighbor, false);
|
||||||
@ -1151,6 +1177,7 @@ pub const meshing = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try neighborMesh.finishData();
|
||||||
if(inRenderThread) {
|
if(inRenderThread) {
|
||||||
try neighborMesh.uploadData();
|
try neighborMesh.uploadData();
|
||||||
} else {
|
} else {
|
||||||
@ -1158,6 +1185,8 @@ pub const meshing = struct {
|
|||||||
try renderer.RenderStructure.addToUpdateList(neighborMesh);
|
try renderer.RenderStructure.addToUpdateList(neighborMesh);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
self.mutex.lock();
|
||||||
|
defer self.mutex.unlock();
|
||||||
if(self.lastNeighborsSameLod[neighbor] != null) {
|
if(self.lastNeighborsSameLod[neighbor] != null) {
|
||||||
self.clearNeighbor(neighbor, false);
|
self.clearNeighbor(neighbor, false);
|
||||||
self.lastNeighborsSameLod[neighbor] = null;
|
self.lastNeighborsSameLod[neighbor] = null;
|
||||||
@ -1166,6 +1195,8 @@ pub const meshing = struct {
|
|||||||
// lod border:
|
// lod border:
|
||||||
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
|
if(self.pos.voxelSize == 1 << settings.highestLOD) continue;
|
||||||
const neighborMesh = getNeighborMesh(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
|
const neighborMesh = getNeighborMesh(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
|
||||||
|
self.mutex.lock();
|
||||||
|
defer self.mutex.unlock();
|
||||||
if(self.lastNeighborsHigherLod[neighbor] != null) {
|
if(self.lastNeighborsHigherLod[neighbor] != null) {
|
||||||
self.clearNeighbor(neighbor, true);
|
self.clearNeighbor(neighbor, true);
|
||||||
self.lastNeighborsHigherLod[neighbor] = null;
|
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 {
|
pub fn uploadDataAndFinishNeighbors(self: *ChunkMesh) !void {
|
||||||
try self.finishNeighbors(true);
|
self.finishedMeshing = true;
|
||||||
try self.uploadData();
|
try self.uploadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
131
src/renderer.zig
131
src/renderer.zig
@ -912,7 +912,15 @@ pub const RenderStructure = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit() void {
|
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)});
|
std.log.err("Error while freeing remaining meshes: {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
for(storageLists) |storageList| {
|
for(storageLists) |storageList| {
|
||||||
@ -953,7 +961,7 @@ pub const RenderStructure = struct {
|
|||||||
|
|
||||||
fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block {
|
fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block {
|
||||||
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1});
|
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=1});
|
||||||
const mesh = node.mesh.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);
|
const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@ -962,7 +970,7 @@ pub const RenderStructure = struct {
|
|||||||
var lod: u5 = 0;
|
var lod: u5 = 0;
|
||||||
while(lod < settings.highestLOD) : (lod += 1) {
|
while(lod < settings.highestLOD) : (lod += 1) {
|
||||||
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod});
|
const node = RenderStructure.getNodeFromRenderThread(.{.wx = x, .wy = y, .wz = z, .voxelSize=@as(u31, 1) << lod});
|
||||||
const mesh = node.mesh.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);
|
const block = mesh.chunk.getBlock(x & chunk.chunkMask<<lod, y & chunk.chunkMask<<lod, z & chunk.chunkMask<<lod);
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
@ -973,7 +981,7 @@ pub const RenderStructure = struct {
|
|||||||
var lod: u5 = @ctz(voxelSize);
|
var lod: u5 = @ctz(voxelSize);
|
||||||
while(lod < settings.highestLOD) : (lod += 1) {
|
while(lod < settings.highestLOD) : (lod += 1) {
|
||||||
const node = RenderStructure.getNodeFromRenderThread(.{.wx = wx & ~chunk.chunkMask<<lod, .wy = wy & ~chunk.chunkMask<<lod, .wz = wz & ~chunk.chunkMask<<lod, .voxelSize=@as(u31, 1) << lod});
|
const node = RenderStructure.getNodeFromRenderThread(.{.wx = wx & ~chunk.chunkMask<<lod, .wy = wy & ~chunk.chunkMask<<lod, .wz = wz & ~chunk.chunkMask<<lod, .voxelSize=@as(u31, 1) << lod});
|
||||||
return node.mesh.load(.Unordered) orelse continue;
|
return node.mesh.load(.Acquire) orelse continue;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -985,18 +993,30 @@ pub const RenderStructure = struct {
|
|||||||
pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
|
pos.wz += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relZ[neighbor];
|
||||||
pos.voxelSize = resolution;
|
pos.voxelSize = resolution;
|
||||||
const node = getNodeFromRenderThread(pos);
|
const node = getNodeFromRenderThread(pos);
|
||||||
return node.mesh.load(.Unordered);
|
return node.mesh.load(.Acquire);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk.meshing.ChunkMesh {
|
pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk.meshing.ChunkMesh {
|
||||||
const node = RenderStructure.getNodeFromRenderThread(pos);
|
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()) {
|
if(mesh.tryIncreaseRefCount()) {
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
return null;
|
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 {
|
pub fn getNeighborAndIncreaseRefCount(_pos: chunk.ChunkPosition, resolution: u31, neighbor: u3) ?*chunk.meshing.ChunkMesh {
|
||||||
var pos = _pos;
|
var pos = _pos;
|
||||||
pos.wx += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relX[neighbor];
|
pos.wx += pos.voxelSize*chunk.chunkSize*chunk.Neighbors.relX[neighbor];
|
||||||
@ -1042,47 +1062,47 @@ pub const RenderStructure = struct {
|
|||||||
return true;
|
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| {
|
for(0..storageLists.len) |_lod| {
|
||||||
const lod: u5 = @intCast(_lod);
|
const lod: u5 = @intCast(_lod);
|
||||||
const maxRenderDistanceNew = renderDistance*chunk.chunkSize << lod;
|
const maxRenderDistanceNew = lastRD*chunk.chunkSize << lod;
|
||||||
const maxRenderDistanceOld = lastRD*chunk.chunkSize << lod;
|
const maxRenderDistanceOld = olderRD*chunk.chunkSize << lod;
|
||||||
const size: u31 = chunk.chunkSize << lod;
|
const size: u31 = chunk.chunkSize << lod;
|
||||||
const mask: i32 = size - 1;
|
const mask: i32 = size - 1;
|
||||||
const invMask: i32 = ~mask;
|
const invMask: i32 = ~mask;
|
||||||
|
|
||||||
std.debug.assert(@divFloor(2*maxRenderDistanceNew + size-1, size) + 2 <= storageSize);
|
std.debug.assert(@divFloor(2*maxRenderDistanceNew + size-1, size) + 2 <= storageSize);
|
||||||
|
|
||||||
const minX = lastPx-%maxRenderDistanceOld & invMask;
|
const minX = olderPx-%maxRenderDistanceOld & invMask;
|
||||||
const maxX = lastPx+%maxRenderDistanceOld+%size & invMask;
|
const maxX = olderPx+%maxRenderDistanceOld+%size & invMask;
|
||||||
var x = minX;
|
var x = minX;
|
||||||
while(x != maxX): (x +%= size) {
|
while(x != maxX): (x +%= size) {
|
||||||
const xIndex = @divExact(x, size) & storageMask;
|
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);
|
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);
|
deltaXOld = @max(0, deltaXOld - size/2);
|
||||||
const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew);
|
const maxYRenderDistanceNew: i32 = reduceRenderDistance(maxRenderDistanceNew, deltaXNew);
|
||||||
const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld);
|
const maxYRenderDistanceOld: i32 = reduceRenderDistance(maxRenderDistanceOld, deltaXOld);
|
||||||
|
|
||||||
const minY = lastPy-%maxYRenderDistanceOld & invMask;
|
const minY = olderPy-%maxYRenderDistanceOld & invMask;
|
||||||
const maxY = lastPy+%maxYRenderDistanceOld+%size & invMask;
|
const maxY = olderPy+%maxYRenderDistanceOld+%size & invMask;
|
||||||
var y = minY;
|
var y = minY;
|
||||||
while(y != maxY): (y +%= size) {
|
while(y != maxY): (y +%= size) {
|
||||||
const yIndex = @divExact(y, size) & storageMask;
|
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);
|
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);
|
deltaYNew = @max(0, deltaYNew - size/2);
|
||||||
var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld);
|
var maxZRenderDistanceOld: i32 = reduceRenderDistance(maxYRenderDistanceOld, deltaYOld);
|
||||||
if(maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size/2;
|
if(maxZRenderDistanceOld == 0) maxZRenderDistanceOld -= size/2;
|
||||||
var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew);
|
var maxZRenderDistanceNew: i32 = reduceRenderDistance(maxYRenderDistanceNew, deltaYNew);
|
||||||
if(maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size/2;
|
if(maxZRenderDistanceNew == 0) maxZRenderDistanceNew -= size/2;
|
||||||
|
|
||||||
const minZOld = lastPz-%maxZRenderDistanceOld & invMask;
|
const minZOld = olderPz-%maxZRenderDistanceOld & invMask;
|
||||||
const maxZOld = lastPz+%maxZRenderDistanceOld+%size & invMask;
|
const maxZOld = olderPz+%maxZRenderDistanceOld+%size & invMask;
|
||||||
const minZNew = pz-%maxZRenderDistanceNew & invMask;
|
const minZNew = lastPz-%maxZRenderDistanceNew & invMask;
|
||||||
const maxZNew = pz+%maxZRenderDistanceNew+%size & invMask;
|
const maxZNew = lastPz+%maxZRenderDistanceNew+%size & invMask;
|
||||||
|
|
||||||
var zValues: [storageSize]i32 = undefined;
|
var zValues: [storageSize]i32 = undefined;
|
||||||
var zValuesLen: usize = 0;
|
var zValuesLen: usize = 0;
|
||||||
@ -1107,16 +1127,18 @@ pub const RenderStructure = struct {
|
|||||||
|
|
||||||
const node = &storageLists[_lod][@intCast(index)];
|
const node = &storageLists[_lod][@intCast(index)];
|
||||||
// Update the neighbors, so we don't get cracks when we look back:
|
// Update the neighbors, so we don't get cracks when we look back:
|
||||||
if(node.mesh.load(.Unordered)) |mesh| {
|
if(node.mesh.load(.Acquire)) |mesh| {
|
||||||
const pos = mesh.pos;
|
const pos = mesh.pos;
|
||||||
mesh.decreaseRefCount();
|
mesh.decreaseRefCount();
|
||||||
node.mesh.store(null, .Unordered);
|
node.mesh.store(null, .Release);
|
||||||
if(renderDistance != 0) {
|
if(lastRD != 0) {
|
||||||
for(chunk.Neighbors.iterable) |neighbor| {
|
for(chunk.Neighbors.iterable) |neighbor| {
|
||||||
if(getNeighborFromRenderThread(pos, pos.voxelSize, neighbor)) |neighborMesh| {
|
if(getNeighborFromRenderThread(pos, pos.voxelSize, neighbor)) |neighborMesh| {
|
||||||
neighborMesh.needsNeighborUpdate = true;
|
if(neighborMesh.finishedMeshing) {
|
||||||
neighborMesh.increaseRefCount();
|
neighborMesh.needsNeighborUpdate = true;
|
||||||
try priorityNeighborUpdateList.append(neighborMesh);
|
neighborMesh.increaseRefCount();
|
||||||
|
try priorityNeighborUpdateList.append(neighborMesh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1192,7 +1214,7 @@ pub const RenderStructure = struct {
|
|||||||
const pos = chunk.ChunkPosition{.wx=x, .wy=y, .wz=z, .voxelSize=@as(u31, 1)<<lod};
|
const pos = chunk.ChunkPosition{.wx=x, .wy=y, .wz=z, .voxelSize=@as(u31, 1)<<lod};
|
||||||
|
|
||||||
const node = &storageLists[_lod][@intCast(index)];
|
const node = &storageLists[_lod][@intCast(index)];
|
||||||
std.debug.assert(node.mesh.load(.Unordered) == null);
|
std.debug.assert(node.mesh.load(.Acquire) == null);
|
||||||
try meshRequests.append(pos);
|
try meshRequests.append(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1212,7 +1234,6 @@ pub const RenderStructure = struct {
|
|||||||
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.globalAllocator);
|
var meshRequests = std.ArrayList(chunk.ChunkPosition).init(main.globalAllocator);
|
||||||
defer meshRequests.deinit();
|
defer meshRequests.deinit();
|
||||||
|
|
||||||
try freeOldMeshes(px, py, pz, renderDistance);
|
|
||||||
try createNewMeshes(px, py, pz, renderDistance, &meshRequests);
|
try createNewMeshes(px, py, pz, renderDistance, &meshRequests);
|
||||||
|
|
||||||
// Does occlusion using a breadth-first search that caches an on-screen visibility rectangle.
|
// 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;
|
var lod: u3 = 0;
|
||||||
while(lod <= settings.highestLOD) : (lod += 1) {
|
while(lod <= settings.highestLOD) : (lod += 1) {
|
||||||
const node = getNodeFromRenderThread(firstPos);
|
const node = getNodeFromRenderThread(firstPos);
|
||||||
if(node.mesh.load(.Unordered) != null) {
|
if(node.mesh.load(.Acquire) != null and node.mesh.load(.Acquire).?.finishedMeshing) {
|
||||||
node.lod = lod;
|
node.lod = lod;
|
||||||
node.min = @splat(-1);
|
node.min = @splat(-1);
|
||||||
node.max = @splat(1);
|
node.max = @splat(1);
|
||||||
@ -1268,7 +1289,8 @@ pub const RenderStructure = struct {
|
|||||||
while(searchList.removeOrNull()) |data| {
|
while(searchList.removeOrNull()) |data| {
|
||||||
try nodeList.append(data.node);
|
try nodeList.append(data.node);
|
||||||
data.node.active = false;
|
data.node.active = false;
|
||||||
const mesh = data.node.mesh.load(.Unordered).?;
|
const mesh = data.node.mesh.load(.Acquire).?;
|
||||||
|
std.debug.assert(mesh.finishedMeshing);
|
||||||
mesh.visibilityMask = 0xff;
|
mesh.visibilityMask = 0xff;
|
||||||
const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos;
|
const relPos: Vec3d = @as(Vec3d, @floatFromInt(Vec3i{mesh.pos.wx, mesh.pos.wy, mesh.pos.wz})) - playerPos;
|
||||||
const relPosFloat: Vec3f = @floatCast(relPos);
|
const relPosFloat: Vec3f = @floatCast(relPos);
|
||||||
@ -1425,7 +1447,8 @@ pub const RenderStructure = struct {
|
|||||||
neighborPos.voxelSize *= 2;
|
neighborPos.voxelSize *= 2;
|
||||||
}
|
}
|
||||||
const node = getNodeFromRenderThread(neighborPos);
|
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.
|
// Ensure that there are no high-to-low lod transitions, which would produce cracks.
|
||||||
if(lod == data.node.lod and lod != settings.highestLOD and !node.rendered) {
|
if(lod == data.node.lod and lod != settings.highestLOD and !node.rendered) {
|
||||||
var isValid: bool = true;
|
var isValid: bool = true;
|
||||||
@ -1490,10 +1513,10 @@ pub const RenderStructure = struct {
|
|||||||
}
|
}
|
||||||
for(nodeList.items) |node| {
|
for(nodeList.items) |node| {
|
||||||
node.rendered = false;
|
node.rendered = false;
|
||||||
const mesh = node.mesh.load(.Unordered).?;
|
const mesh = node.mesh.load(.Acquire).?;
|
||||||
if(mesh.pos.voxelSize != @as(u31, 1) << settings.highestLOD) {
|
if(mesh.pos.voxelSize != @as(u31, 1) << settings.highestLOD) {
|
||||||
const parent = getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1});
|
const parent = getNodeFromRenderThread(.{.wx=mesh.pos.wx, .wy=mesh.pos.wy, .wz=mesh.pos.wz, .voxelSize=mesh.pos.voxelSize << 1});
|
||||||
if(parent.mesh.load(.Unordered)) |parentMesh| {
|
if(parent.mesh.load(.Acquire)) |parentMesh| {
|
||||||
const sizeShift = chunk.chunkShift + @ctz(mesh.pos.voxelSize);
|
const sizeShift = chunk.chunkShift + @ctz(mesh.pos.voxelSize);
|
||||||
const octantIndex: u3 = @intCast((mesh.pos.wx>>sizeShift & 1) | (mesh.pos.wy>>sizeShift & 1)<<1 | (mesh.pos.wz>>sizeShift & 1)<<2);
|
const octantIndex: u3 = @intCast((mesh.pos.wx>>sizeShift & 1) | (mesh.pos.wy>>sizeShift & 1)<<1 | (mesh.pos.wz>>sizeShift & 1)<<2);
|
||||||
parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex);
|
parentMesh.visibilityMask &= ~(@as(u8, 1) << octantIndex);
|
||||||
@ -1503,20 +1526,29 @@ pub const RenderStructure = struct {
|
|||||||
try mesh.uploadDataAndFinishNeighbors();
|
try mesh.uploadDataAndFinishNeighbors();
|
||||||
mesh.needsNeighborUpdate = false;
|
mesh.needsNeighborUpdate = false;
|
||||||
}
|
}
|
||||||
|
mutex.lock();
|
||||||
if(mesh.needsMeshUpdate) {
|
if(mesh.needsMeshUpdate) {
|
||||||
try mesh.uploadData();
|
try mesh.uploadData();
|
||||||
mesh.needsMeshUpdate = false;
|
mesh.needsMeshUpdate = false;
|
||||||
}
|
}
|
||||||
|
mutex.unlock();
|
||||||
// Remove empty meshes.
|
// Remove empty meshes.
|
||||||
if(mesh.opaqueMesh.vertexCount != 0 or mesh.voxelMesh.vertexCount != 0 or mesh.transparentMesh.vertexCount != 0) {
|
if(mesh.opaqueMesh.vertexCount != 0 or mesh.voxelMesh.vertexCount != 0 or mesh.transparentMesh.vertexCount != 0) {
|
||||||
try meshList.append(mesh);
|
try meshList.append(mesh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const olderPx = lastPx;
|
||||||
|
const olderPy = lastPy;
|
||||||
|
const olderPz = lastPz;
|
||||||
|
const olderRD = lastRD;
|
||||||
|
mutex.lock();
|
||||||
lastPx = px;
|
lastPx = px;
|
||||||
lastPy = py;
|
lastPy = py;
|
||||||
lastPz = pz;
|
lastPz = pz;
|
||||||
lastRD = renderDistance;
|
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:
|
// Make requests after updating the, to avoid concurrency issues and reduce the number of requests:
|
||||||
try network.Protocols.chunkRequest.sendRequest(conn, meshRequests.items);
|
try network.Protocols.chunkRequest.sendRequest(conn, meshRequests.items);
|
||||||
return meshList.items;
|
return meshList.items;
|
||||||
@ -1529,7 +1561,7 @@ pub const RenderStructure = struct {
|
|||||||
for(blockUpdateList.items) |blockUpdate| {
|
for(blockUpdateList.items) |blockUpdate| {
|
||||||
const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1};
|
const pos = chunk.ChunkPosition{.wx=blockUpdate.x, .wy=blockUpdate.y, .wz=blockUpdate.z, .voxelSize=1};
|
||||||
const node = getNodeFromRenderThread(pos);
|
const node = getNodeFromRenderThread(pos);
|
||||||
if(node.mesh.load(.Unordered)) |mesh| {
|
if(node.mesh.load(.Acquire)) |mesh| {
|
||||||
try mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock);
|
try mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock);
|
||||||
} // TODO: It seems like we simply ignore the block update if we don't have the mesh yet.
|
} // TODO: It seems like we simply ignore the block update if we don't have the mesh yet.
|
||||||
}
|
}
|
||||||
@ -1547,7 +1579,7 @@ pub const RenderStructure = struct {
|
|||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
defer mutex.lock();
|
defer mutex.lock();
|
||||||
mesh.decreaseRefCount();
|
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;
|
if(!mesh.needsNeighborUpdate) continue;
|
||||||
try mesh.uploadDataAndFinishNeighbors();
|
try mesh.uploadDataAndFinishNeighbors();
|
||||||
mesh.needsNeighborUpdate = false;
|
mesh.needsNeighborUpdate = false;
|
||||||
@ -1555,12 +1587,17 @@ pub const RenderStructure = struct {
|
|||||||
}
|
}
|
||||||
while (priorityMeshUpdateList.items.len != 0) {
|
while (priorityMeshUpdateList.items.len != 0) {
|
||||||
const mesh = priorityMeshUpdateList.orderedRemove(0);
|
const mesh = priorityMeshUpdateList.orderedRemove(0);
|
||||||
mesh.decreaseRefCount();
|
if(!mesh.needsMeshUpdate) {
|
||||||
if(!mesh.needsMeshUpdate) continue;
|
mutex.unlock();
|
||||||
|
defer mutex.lock();
|
||||||
|
mesh.decreaseRefCount();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
mesh.needsMeshUpdate = false;
|
mesh.needsMeshUpdate = false;
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
defer mutex.lock();
|
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();
|
try mesh.uploadData();
|
||||||
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
|
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
|
||||||
}
|
}
|
||||||
@ -1595,7 +1632,7 @@ pub const RenderStructure = struct {
|
|||||||
if(isInRenderDistance(mesh.pos)) {
|
if(isInRenderDistance(mesh.pos)) {
|
||||||
const node = getNodeFromRenderThread(mesh.pos);
|
const node = getNodeFromRenderThread(mesh.pos);
|
||||||
try mesh.uploadDataAndFinishNeighbors();
|
try mesh.uploadDataAndFinishNeighbors();
|
||||||
if(node.mesh.swap(mesh, .Monotonic)) |oldMesh| {
|
if(node.mesh.swap(mesh, .AcqRel)) |oldMesh| {
|
||||||
oldMesh.decreaseRefCount();
|
oldMesh.decreaseRefCount();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1620,6 +1657,20 @@ pub const RenderStructure = struct {
|
|||||||
mesh.needsMeshUpdate = true;
|
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 {
|
pub const MeshGenerationTask = struct {
|
||||||
mesh: *chunk.Chunk,
|
mesh: *chunk.Chunk,
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user