mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-02 18:57:10 -04:00
Garbage Collection - The Great Return (#1680)
This is a first attempt to get rid of of reference counting #1413 Basically all threads execute a global sync point, this allows determining a time when all temporary resources (such as meshes on the client side used in lighting on other threads) have been freed with 100% certainty. Remaining work: - [x] Implement the sync point - [x] Implement free lists and free the resources - [x] Determine if this is worth it performance wise - [x] Use this for chunk meshes - [x] Remove reference counting and the locks on the chunk storage data structure - [x] Measure performance of gathering many light samples and compare it with master → around 15% reduction in time - [x] Cleanup some unused things (mesh free list)
This commit is contained in:
parent
a8470ea78a
commit
6baf7fc067
@ -212,7 +212,7 @@ pub fn generateData(
|
||||
}
|
||||
|
||||
const sidePos = pos + side.relPos();
|
||||
const sideBlock = main.renderer.mesh_storage.getBlock(sidePos[0], sidePos[1], sidePos[2]) orelse continue;
|
||||
const sideBlock = main.renderer.mesh_storage.getBlockFromRenderThread(sidePos[0], sidePos[1], sidePos[2]) orelse continue;
|
||||
const canConnectToSide = currentBlock.mode() == sideBlock.mode() and currentBlock.modeData() == sideBlock.modeData();
|
||||
|
||||
if(canConnectToSide) {
|
||||
|
@ -401,8 +401,7 @@ pub const BlockEntityTypes = struct {
|
||||
|
||||
pub fn updateTextFromClient(pos: Vec3i, newText: []const u8) void {
|
||||
{
|
||||
const mesh = main.renderer.mesh_storage.getMeshAndIncreaseRefCount(.initFromWorldPos(pos, 1)) orelse return;
|
||||
defer mesh.decreaseRefCount();
|
||||
const mesh = main.renderer.mesh_storage.getMesh(.initFromWorldPos(pos, 1)) orelse return;
|
||||
mesh.mutex.lock();
|
||||
defer mesh.mutex.unlock();
|
||||
const index = mesh.chunk.getLocalBlockIndex(pos);
|
||||
@ -478,8 +477,7 @@ pub const BlockEntityTypes = struct {
|
||||
signData.renderedTexture.?.bindTo(0);
|
||||
|
||||
c.glUniform1i(uniforms.quadIndex, @intFromEnum(quad));
|
||||
const mesh = main.renderer.mesh_storage.getMeshAndIncreaseRefCount(main.chunk.ChunkPosition.initFromWorldPos(signData.blockPos, 1)) orelse continue :outer;
|
||||
defer mesh.decreaseRefCount();
|
||||
const mesh = main.renderer.mesh_storage.getMesh(main.chunk.ChunkPosition.initFromWorldPos(signData.blockPos, 1)) orelse continue :outer;
|
||||
mesh.lightingData[0].lock.lockRead();
|
||||
defer mesh.lightingData[0].lock.unlockRead();
|
||||
mesh.lightingData[1].lock.lockRead();
|
||||
|
@ -190,6 +190,8 @@ pub const ChunkPosition = struct { // MARK: ChunkPosition
|
||||
return self.equals(notNull);
|
||||
}
|
||||
return false;
|
||||
} else if(@TypeOf(other) == ChunkPosition) {
|
||||
return self.wx == other.wx and self.wy == other.wy and self.wz == other.wz and self.voxelSize == other.voxelSize;
|
||||
} else if(@TypeOf(other.*) == ServerChunk) {
|
||||
return self.wx == other.super.pos.wx and self.wy == other.super.pos.wy and self.wz == other.super.pos.wz and self.voxelSize == other.super.pos.voxelSize;
|
||||
} else if(@typeInfo(@TypeOf(other)) == .pointer) {
|
||||
|
16
src/game.zig
16
src/game.zig
@ -260,7 +260,7 @@ pub const collision = struct {
|
||||
while(y <= maxY) : (y += 1) {
|
||||
var z: i32 = maxZ;
|
||||
while(z >= minZ) : (z -= 1) {
|
||||
const _block = if(side == .client) main.renderer.mesh_storage.getBlock(x, y, z) else main.server.world.?.getBlock(x, y, z);
|
||||
const _block = if(side == .client) main.renderer.mesh_storage.getBlockFromRenderThread(x, y, z) else main.server.world.?.getBlock(x, y, z);
|
||||
if(_block) |block| {
|
||||
if(collideWithBlock(block, x, y, z, boundingBoxCenter, fullBoundingBoxExtent, directionVector)) |res| {
|
||||
if(res.dist < minDistance) {
|
||||
@ -298,7 +298,7 @@ pub const collision = struct {
|
||||
while(x <= maxX) : (x += 1) {
|
||||
var y = minY;
|
||||
while(y <= maxY) : (y += 1) {
|
||||
const _block = if(side == .client) main.renderer.mesh_storage.getBlock(x, y, z) else main.server.world.?.getBlock(x, y, z);
|
||||
const _block = if(side == .client) main.renderer.mesh_storage.getBlockFromRenderThread(x, y, z) else main.server.world.?.getBlock(x, y, z);
|
||||
|
||||
if(_block) |block| {
|
||||
const blockPos: Vec3d = .{@floatFromInt(x), @floatFromInt(y), @floatFromInt(z)};
|
||||
@ -368,7 +368,7 @@ pub const collision = struct {
|
||||
while(y <= maxY) : (y += 1) {
|
||||
var z: i32 = maxZ;
|
||||
while(z >= minZ) : (z -= 1) {
|
||||
const _block = if(side == .client) main.renderer.mesh_storage.getBlock(x, y, z) else main.server.world.?.getBlock(x, y, z);
|
||||
const _block = if(side == .client) main.renderer.mesh_storage.getBlockFromRenderThread(x, y, z) else main.server.world.?.getBlock(x, y, z);
|
||||
const totalBox: Box = .{
|
||||
.min = @floatFromInt(Vec3i{x, y, z}),
|
||||
.max = @floatFromInt(Vec3i{x + 1, y + 1, z + 1}),
|
||||
@ -480,7 +480,7 @@ pub const collision = struct {
|
||||
var posZ: i32 = minZ;
|
||||
while(posZ <= maxZ) : (posZ += 1) {
|
||||
const block: ?Block =
|
||||
if(side == .client) main.renderer.mesh_storage.getBlock(posX, posY, posZ) else main.server.world.?.getBlock(posX, posY, posZ);
|
||||
if(side == .client) main.renderer.mesh_storage.getBlockFromRenderThread(posX, posY, posZ) else main.server.world.?.getBlock(posX, posY, posZ);
|
||||
if(block == null or block.?.touchFunction() == null)
|
||||
continue;
|
||||
const touchX: bool = isBlockIntersecting(block.?, posX, posY, posZ, center, extentX);
|
||||
@ -643,9 +643,9 @@ pub const Player = struct { // MARK: Player
|
||||
pub fn placeBlock() void {
|
||||
if(main.renderer.MeshSelection.selectedBlockPos) |blockPos| {
|
||||
if(!main.KeyBoard.key("shift").pressed) {
|
||||
if(main.renderer.mesh_storage.triggerOnInteractBlock(blockPos[0], blockPos[1], blockPos[2]) == .handled) return;
|
||||
if(main.renderer.mesh_storage.triggerOnInteractBlockFromRenderThread(blockPos[0], blockPos[1], blockPos[2]) == .handled) return;
|
||||
}
|
||||
const block = main.renderer.mesh_storage.getBlock(blockPos[0], blockPos[1], blockPos[2]) orelse main.blocks.Block{.typ = 0, .data = 0};
|
||||
const block = main.renderer.mesh_storage.getBlockFromRenderThread(blockPos[0], blockPos[1], blockPos[2]) orelse main.blocks.Block{.typ = 0, .data = 0};
|
||||
const gui = block.gui();
|
||||
if(gui.len != 0 and !main.KeyBoard.key("shift").pressed) {
|
||||
main.gui.openWindow(gui);
|
||||
@ -677,7 +677,7 @@ pub const Player = struct { // MARK: Player
|
||||
|
||||
pub fn acquireSelectedBlock() void {
|
||||
if(main.renderer.MeshSelection.selectedBlockPos) |selectedPos| {
|
||||
const block = main.renderer.mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
const block = main.renderer.mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
|
||||
const item: items.Item = for(0..items.itemListSize) |idx| {
|
||||
const baseItem: main.items.BaseItemIndex = @enumFromInt(idx);
|
||||
@ -919,7 +919,7 @@ pub fn update(deltaTime: f64) void { // MARK: update()
|
||||
const airFrictionCoefficient = gravity/airTerminalVelocity; // λ = a/v in equillibrium
|
||||
const playerDensity = 1.2;
|
||||
var move: Vec3d = .{0, 0, 0};
|
||||
if(main.renderer.mesh_storage.getBlock(@intFromFloat(@floor(Player.super.pos[0])), @intFromFloat(@floor(Player.super.pos[1])), @intFromFloat(@floor(Player.super.pos[2]))) != null) {
|
||||
if(main.renderer.mesh_storage.getBlockFromRenderThread(@intFromFloat(@floor(Player.super.pos[0])), @intFromFloat(@floor(Player.super.pos[1])), @intFromFloat(@floor(Player.super.pos[2]))) != null) {
|
||||
const volumeProperties = collision.calculateVolumeProperties(.client, Player.super.pos, Player.outerBoundingBox, .{.density = 0.001, .terminalVelocity = airTerminalVelocity, .mobility = 1.0});
|
||||
const effectiveGravity = gravity*(playerDensity - volumeProperties.density)/playerDensity;
|
||||
const volumeFrictionCoeffecient: f32 = @floatCast(gravity/volumeProperties.terminalVelocity);
|
||||
|
@ -55,10 +55,12 @@ pub fn initThreadLocals() void {
|
||||
seed = @bitCast(@as(i64, @truncate(std.time.nanoTimestamp())));
|
||||
stackAllocatorBase = heap.StackAllocator.init(globalAllocator, 1 << 23);
|
||||
stackAllocator = stackAllocatorBase.allocator();
|
||||
heap.GarbageCollection.addThread();
|
||||
}
|
||||
|
||||
pub fn deinitThreadLocals() void {
|
||||
stackAllocatorBase.deinit();
|
||||
heap.GarbageCollection.removeThread();
|
||||
}
|
||||
|
||||
fn cacheStringImpl(comptime len: usize, comptime str: [len]u8) []const u8 {
|
||||
@ -549,6 +551,7 @@ pub fn main() void { // MARK: main()
|
||||
defer if(global_gpa.deinit() == .leak) {
|
||||
std.log.err("Memory leak", .{});
|
||||
};
|
||||
defer heap.GarbageCollection.assertAllThreadsStopped();
|
||||
initThreadLocals();
|
||||
defer deinitThreadLocals();
|
||||
|
||||
@ -672,6 +675,7 @@ pub fn main() void { // MARK: main()
|
||||
audio.setMusic("cubyz:cubyz");
|
||||
|
||||
while(c.glfwWindowShouldClose(Window.window) == 0) {
|
||||
heap.GarbageCollection.syncPoint();
|
||||
const isHidden = c.glfwGetWindowAttrib(Window.window, c.GLFW_ICONIFIED) == c.GLFW_TRUE;
|
||||
if(!isHidden) {
|
||||
c.glfwSwapBuffers(Window.window);
|
||||
|
@ -587,6 +587,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager
|
||||
|
||||
var lastTime: i64 = networkTimestamp();
|
||||
while(self.running.load(.monotonic)) {
|
||||
main.heap.GarbageCollection.syncPoint();
|
||||
self.waitingToFinishReceive.broadcast();
|
||||
var source: Address = undefined;
|
||||
if(self.socket.receive(&self.receiveBuffer, 1, &source)) |data| {
|
||||
@ -1308,8 +1309,7 @@ pub const Protocols = struct {
|
||||
}
|
||||
|
||||
pub fn sendClientDataUpdateToServer(conn: *Connection, pos: Vec3i) void {
|
||||
const mesh = main.renderer.mesh_storage.getMeshAndIncreaseRefCount(.initFromWorldPos(pos, 1)) orelse return;
|
||||
defer mesh.decreaseRefCount();
|
||||
const mesh = main.renderer.mesh_storage.getMesh(.initFromWorldPos(pos, 1)) orelse return;
|
||||
mesh.mutex.lock();
|
||||
defer mesh.mutex.unlock();
|
||||
const index = mesh.chunk.getLocalBlockIndex(pos);
|
||||
|
@ -287,7 +287,7 @@ pub fn renderWorld(world: *World, ambientLight: Vec3f, skyColor: Vec3f, playerPo
|
||||
|
||||
worldFrameBuffer.bindTexture(c.GL_TEXTURE3);
|
||||
|
||||
const playerBlock = mesh_storage.getBlockFromAnyLod(@intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2])));
|
||||
const playerBlock = mesh_storage.getBlockFromAnyLodFromRenderThread(@intFromFloat(@floor(playerPos[0])), @intFromFloat(@floor(playerPos[1])), @intFromFloat(@floor(playerPos[2])));
|
||||
|
||||
if(settings.bloom) {
|
||||
Bloom.render(lastWidth, lastHeight, playerBlock, playerPos, game.camera.viewMatrix);
|
||||
@ -915,7 +915,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
selectedBlockPos = null;
|
||||
|
||||
while(total_tMax < closestDistance) {
|
||||
const block = mesh_storage.getBlock(voxelPos[0], voxelPos[1], voxelPos[2]) orelse break;
|
||||
const block = mesh_storage.getBlockFromRenderThread(voxelPos[0], voxelPos[1], voxelPos[2]) orelse break;
|
||||
if(block.typ != 0) blk: {
|
||||
const fluidPlaceable = item != null and item.? == .baseItem and item.?.baseItem.hasTag(.fluidPlaceable);
|
||||
for(block.blockTags()) |tag| {
|
||||
@ -971,7 +971,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
|
||||
pub fn placeBlock(inventory: main.items.Inventory, slot: u32) void {
|
||||
if(selectedBlockPos) |selectedPos| {
|
||||
var oldBlock = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
var oldBlock = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
var block = oldBlock;
|
||||
if(inventory.getItem(slot)) |item| {
|
||||
switch(item) {
|
||||
@ -999,7 +999,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
neighborDir = selectedPos - posBeforeBlock;
|
||||
const relPos: Vec3f = @floatCast(lastPos - @as(Vec3d, @floatFromInt(neighborPos)));
|
||||
const neighborBlock = block;
|
||||
oldBlock = mesh_storage.getBlock(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return;
|
||||
oldBlock = mesh_storage.getBlockFromRenderThread(neighborPos[0], neighborPos[1], neighborPos[2]) orelse return;
|
||||
block = oldBlock;
|
||||
if(block.typ == itemBlock) {
|
||||
if(rotationMode.generateData(main.game.world.?, neighborPos, relPos, lastDir, neighborDir, neighborOfSelection, &block, neighborBlock, false)) {
|
||||
@ -1047,7 +1047,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection
|
||||
lastSelectedBlockPos = selectedPos;
|
||||
currentBlockProgress = 0;
|
||||
}
|
||||
const block = mesh_storage.getBlock(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
const block = mesh_storage.getBlockFromRenderThread(selectedPos[0], selectedPos[1], selectedPos[2]) orelse return;
|
||||
if(block.hasTag(.fluid) or block.hasTag(.air)) {
|
||||
return;
|
||||
}
|
||||
|
@ -420,8 +420,7 @@ pub const PrimitiveMesh = struct { // MARK: PrimitiveMesh
|
||||
if(x == x & chunk.chunkMask and y == y & chunk.chunkMask and z == z & chunk.chunkMask) {
|
||||
return getValues(parent, wx, wy, wz);
|
||||
}
|
||||
const neighborMesh = mesh_storage.getMeshAndIncreaseRefCount(.{.wx = wx, .wy = wy, .wz = wz, .voxelSize = parent.pos.voxelSize}) orelse return .{0, 0, 0, 0, 0, 0};
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
const neighborMesh = mesh_storage.getMesh(.{.wx = wx, .wy = wy, .wz = wz, .voxelSize = parent.pos.voxelSize}) orelse return .{0, 0, 0, 0, 0, 0};
|
||||
neighborMesh.lightingData[0].lock.lockRead();
|
||||
neighborMesh.lightingData[1].lock.lockRead();
|
||||
defer neighborMesh.lightingData[0].lock.unlockRead();
|
||||
@ -692,7 +691,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
blockBreakingFacesSortingData: []SortingData = &.{},
|
||||
blockBreakingFacesChanged: bool = false,
|
||||
|
||||
pub fn init(self: *ChunkMesh, pos: chunk.ChunkPosition, ch: *chunk.Chunk) void {
|
||||
pub fn init(pos: chunk.ChunkPosition, ch: *chunk.Chunk) *ChunkMesh {
|
||||
const self = mesh_storage.meshMemoryPool.create();
|
||||
self.* = ChunkMesh{
|
||||
.pos = pos,
|
||||
.size = chunk.chunkSize*pos.voxelSize,
|
||||
@ -709,10 +709,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
},
|
||||
.blockBreakingFaces = .init(main.globalAllocator),
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ChunkMesh) void {
|
||||
std.debug.assert(self.refCount.load(.monotonic) == 0);
|
||||
pub fn deinit(self: *ChunkMesh, _: usize) void {
|
||||
chunkBuffer.free(self.chunkAllocation);
|
||||
self.opaqueMesh.deinit();
|
||||
self.transparentMesh.deinit();
|
||||
@ -727,35 +727,14 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
main.globalAllocator.free(self.blockBreakingFacesSortingData);
|
||||
main.globalAllocator.free(self.lightList);
|
||||
lightBuffers[std.math.log2_int(u32, self.pos.voxelSize)].free(self.lightAllocation);
|
||||
mesh_storage.meshMemoryPool.destroy(self);
|
||||
}
|
||||
|
||||
pub fn increaseRefCount(self: *ChunkMesh) void {
|
||||
const prevVal = self.refCount.fetchAdd(1, .monotonic);
|
||||
std.debug.assert(prevVal != 0);
|
||||
}
|
||||
|
||||
/// In cases where it's not certain whether the thing was cleared already.
|
||||
pub fn tryIncreaseRefCount(self: *ChunkMesh) bool {
|
||||
var prevVal = self.refCount.load(.monotonic);
|
||||
while(prevVal != 0) {
|
||||
prevVal = self.refCount.cmpxchgWeak(prevVal, prevVal + 1, .monotonic, .monotonic) orelse return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn decreaseRefCount(self: *ChunkMesh) void {
|
||||
const prevVal = self.refCount.fetchSub(1, .monotonic);
|
||||
std.debug.assert(prevVal != 0);
|
||||
if(prevVal == 1) {
|
||||
mesh_storage.addMeshToClearListAndDecreaseRefCount(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scheduleLightRefreshAndDecreaseRefCount(self: *ChunkMesh) void {
|
||||
LightRefreshTask.scheduleAndDecreaseRefCount(self);
|
||||
pub fn scheduleLightRefresh(pos: chunk.ChunkPosition) void {
|
||||
LightRefreshTask.schedule(pos);
|
||||
}
|
||||
const LightRefreshTask = struct {
|
||||
mesh: *ChunkMesh,
|
||||
pos: chunk.ChunkPosition,
|
||||
|
||||
pub const vtable = main.utils.ThreadPool.VTable{
|
||||
.getPriority = main.utils.castFunctionSelfToAnyopaque(getPriority),
|
||||
@ -765,10 +744,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
.taskType = .misc,
|
||||
};
|
||||
|
||||
pub fn scheduleAndDecreaseRefCount(mesh: *ChunkMesh) void {
|
||||
pub fn schedule(pos: chunk.ChunkPosition) void {
|
||||
const task = main.globalAllocator.create(LightRefreshTask);
|
||||
task.* = .{
|
||||
.mesh = mesh,
|
||||
.pos = pos,
|
||||
};
|
||||
main.threadPool.addTask(task, &vtable);
|
||||
}
|
||||
@ -778,23 +757,21 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
|
||||
pub fn isStillNeeded(_: *LightRefreshTask) bool {
|
||||
return true; // TODO: Is it worth checking for this?
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn run(self: *LightRefreshTask) void {
|
||||
if(self.mesh.needsLightRefresh.swap(false, .acq_rel)) {
|
||||
self.mesh.mutex.lock();
|
||||
self.mesh.finishData();
|
||||
self.mesh.mutex.unlock();
|
||||
mesh_storage.addToUpdateListAndDecreaseRefCount(self.mesh);
|
||||
} else {
|
||||
self.mesh.decreaseRefCount();
|
||||
defer main.globalAllocator.destroy(self);
|
||||
const mesh = mesh_storage.getMesh(self.pos) orelse return;
|
||||
if(mesh.needsLightRefresh.swap(false, .acq_rel)) {
|
||||
mesh.mutex.lock();
|
||||
mesh.finishData();
|
||||
mesh.mutex.unlock();
|
||||
mesh_storage.addToUpdateList(mesh);
|
||||
}
|
||||
main.globalAllocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn clean(self: *LightRefreshTask) void {
|
||||
self.mesh.decreaseRefCount();
|
||||
main.globalAllocator.destroy(self);
|
||||
}
|
||||
};
|
||||
@ -809,7 +786,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
return block.typ != 0 and (other.typ == 0 or (block != other and other.viewThrough()) or other.alwaysViewThrough() or !blocks.meshes.model(other).model().isNeighborOccluded[neighbor.reverse().toInt()]);
|
||||
}
|
||||
|
||||
fn initLight(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
|
||||
fn initLight(self: *ChunkMesh, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
self.mutex.lock();
|
||||
var lightEmittingBlocks = main.List([3]u8).init(main.stackAllocator);
|
||||
defer lightEmittingBlocks.deinit();
|
||||
@ -854,10 +831,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generateLightingData(self: *ChunkMesh) error{AlreadyStored}!void {
|
||||
pub fn generateLightingData(self: *ChunkMesh) error{AlreadyStored, NoLongerNeeded}!void {
|
||||
try mesh_storage.addMeshToStorage(self);
|
||||
|
||||
var lightRefreshList = main.List(*ChunkMesh).init(main.stackAllocator);
|
||||
var lightRefreshList = main.List(chunk.ChunkPosition).init(main.stackAllocator);
|
||||
defer lightRefreshList.deinit();
|
||||
self.initLight(&lightRefreshList);
|
||||
|
||||
@ -876,8 +853,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
pos.wx +%= pos.voxelSize*chunk.chunkSize*dx;
|
||||
pos.wy +%= pos.voxelSize*chunk.chunkSize*dy;
|
||||
pos.wz +%= pos.voxelSize*chunk.chunkSize*dz;
|
||||
const neighborMesh = mesh_storage.getMeshAndIncreaseRefCount(pos) orelse continue;
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
const neighborMesh = mesh_storage.getMesh(pos) orelse continue;
|
||||
|
||||
const shiftSelf: u5 = @intCast(((dx + 1)*3 + dy + 1)*3 + dz + 1);
|
||||
const shiftOther: u5 = @intCast(((-dx + 1)*3 + -dy + 1)*3 + -dz + 1);
|
||||
@ -894,12 +870,8 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
}
|
||||
|
||||
for(lightRefreshList.items) |other| {
|
||||
if(other.needsLightRefresh.load(.unordered)) {
|
||||
other.scheduleLightRefreshAndDecreaseRefCount();
|
||||
} else {
|
||||
other.decreaseRefCount();
|
||||
}
|
||||
for(lightRefreshList.items) |pos| {
|
||||
scheduleLightRefresh(pos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -913,7 +885,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
model.appendNeighborFacingQuadsToList(list, allocator, block, neighbor, x, y, z, backFace);
|
||||
}
|
||||
|
||||
pub fn generateMesh(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
|
||||
pub fn generateMesh(self: *ChunkMesh, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
var alwaysViewThroughMask: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||
@memset(std.mem.asBytes(&alwaysViewThroughMask), 0);
|
||||
var alwaysViewThroughMask2: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
|
||||
@ -1213,7 +1185,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
self.finishNeighbors(lightRefreshList);
|
||||
}
|
||||
|
||||
fn updateBlockLight(self: *ChunkMesh, x: u5, y: u5, z: u5, newBlock: Block, lightRefreshList: *main.List(*ChunkMesh)) void {
|
||||
fn updateBlockLight(self: *ChunkMesh, x: u5, y: u5, z: u5, newBlock: Block, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
for(self.lightingData[0..]) |lightingData| {
|
||||
lightingData.propagateLightsDestructive(&.{.{x, y, z}}, lightRefreshList);
|
||||
}
|
||||
@ -1222,7 +1194,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateBlock(self: *ChunkMesh, _x: i32, _y: i32, _z: i32, _newBlock: Block, blockEntityData: []const u8, lightRefreshList: *main.List(*ChunkMesh), regenerateMeshList: *main.List(*ChunkMesh)) void {
|
||||
pub fn updateBlock(self: *ChunkMesh, _x: i32, _y: i32, _z: i32, _newBlock: Block, blockEntityData: []const u8, lightRefreshList: *main.List(chunk.ChunkPosition), regenerateMeshList: *main.List(*ChunkMesh)) void {
|
||||
const x: u5 = @intCast(_x & chunk.chunkMask);
|
||||
const y: u5 = @intCast(_y & chunk.chunkMask);
|
||||
const z: u5 = @intCast(_z & chunk.chunkMask);
|
||||
@ -1261,8 +1233,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
const nny: u5 = @intCast(ny & chunk.chunkMask);
|
||||
const nnz: u5 = @intCast(nz & chunk.chunkMask);
|
||||
|
||||
const neighborChunkMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.pos, self.pos.voxelSize, neighbor) orelse continue;
|
||||
defer neighborChunkMesh.decreaseRefCount();
|
||||
const neighborChunkMesh = mesh_storage.getNeighbor(self.pos, self.pos.voxelSize, neighbor) orelse continue;
|
||||
|
||||
const index = chunk.getIndex(nnx, nny, nnz);
|
||||
|
||||
@ -1342,7 +1313,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
return;
|
||||
}
|
||||
}
|
||||
mesh.increaseRefCount();
|
||||
list.append(mesh);
|
||||
}
|
||||
|
||||
@ -1392,11 +1362,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
}
|
||||
|
||||
fn finishNeighbors(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
|
||||
fn finishNeighbors(self: *ChunkMesh, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
for(chunk.Neighbor.iterable) |neighbor| {
|
||||
const nullNeighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.pos, self.pos.voxelSize, neighbor);
|
||||
const nullNeighborMesh = mesh_storage.getNeighbor(self.pos, self.pos.voxelSize, neighbor);
|
||||
if(nullNeighborMesh) |neighborMesh| sameLodBlock: {
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
std.debug.assert(neighborMesh != self);
|
||||
deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex);
|
||||
defer self.mutex.unlock();
|
||||
@ -1470,8 +1439,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
neighborMesh.transparentMesh.replaceRange(.neighbor(neighbor.reverse()), transparentNeighbor.items);
|
||||
|
||||
_ = neighborMesh.needsLightRefresh.store(true, .release);
|
||||
neighborMesh.increaseRefCount();
|
||||
lightRefreshList.append(neighborMesh);
|
||||
lightRefreshList.append(neighborMesh.pos);
|
||||
} else {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
@ -1483,7 +1451,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
// lod border:
|
||||
if(self.pos.voxelSize == @as(u31, 1) << settings.highestLod) continue;
|
||||
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
|
||||
const neighborMesh = mesh_storage.getNeighbor(self.pos, 2*self.pos.voxelSize, neighbor) orelse {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
if(self.lastNeighborsHigherLod[neighbor.toInt()] != null) {
|
||||
@ -1493,7 +1461,6 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
}
|
||||
continue;
|
||||
};
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
deadlockFreeDoubleLock(&self.mutex, &neighborMesh.mutex);
|
||||
defer self.mutex.unlock();
|
||||
defer neighborMesh.mutex.unlock();
|
||||
@ -1557,7 +1524,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
|
||||
defer self.mutex.unlock();
|
||||
_ = self.needsLightRefresh.swap(false, .acq_rel);
|
||||
self.finishData();
|
||||
mesh_storage.finishMesh(self);
|
||||
mesh_storage.finishMesh(self.pos);
|
||||
}
|
||||
|
||||
fn uploadChunkPosition(self: *ChunkMesh) void {
|
||||
|
@ -98,7 +98,7 @@ pub const ChannelChunk = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
|
||||
fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
var neighborLists: [6]main.ListUnmanaged(Entry) = @splat(.{});
|
||||
defer {
|
||||
for(&neighborLists) |*list| {
|
||||
@ -145,26 +145,24 @@ pub const ChannelChunk = struct {
|
||||
|
||||
for(chunk.Neighbor.iterable) |neighbor| {
|
||||
if(neighborLists[neighbor.toInt()].items.len == 0) continue;
|
||||
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
const neighborMesh = mesh_storage.getNeighbor(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
neighborMesh.lightingData[@intFromBool(self.isSun)].propagateFromNeighbor(lightQueue, neighborLists[neighbor.toInt()].items, lightRefreshList);
|
||||
}
|
||||
}
|
||||
|
||||
fn addSelfToLightRefreshList(self: *ChannelChunk, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
|
||||
if(mesh_storage.getMeshAndIncreaseRefCount(self.ch.pos)) |mesh| {
|
||||
for(lightRefreshList.items) |other| {
|
||||
if(mesh == other) {
|
||||
mesh.decreaseRefCount();
|
||||
return;
|
||||
}
|
||||
fn addSelfToLightRefreshList(self: *ChannelChunk, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
for(lightRefreshList.items) |other| {
|
||||
if(self.ch.pos.equals(other)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(mesh_storage.getMesh(self.ch.pos)) |mesh| {
|
||||
mesh.needsLightRefresh.store(true, .release);
|
||||
lightRefreshList.append(mesh);
|
||||
lightRefreshList.append(self.ch.pos);
|
||||
}
|
||||
}
|
||||
|
||||
fn propagateDestructive(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), constructiveEntries: *main.ListUnmanaged(ChunkEntries), isFirstBlock: bool, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) main.ListUnmanaged(PositionEntry) {
|
||||
fn propagateDestructive(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), constructiveEntries: *main.ListUnmanaged(ChunkEntries), isFirstBlock: bool, lightRefreshList: *main.List(chunk.ChunkPosition)) main.ListUnmanaged(PositionEntry) {
|
||||
var neighborLists: [6]main.ListUnmanaged(Entry) = @splat(.{});
|
||||
var constructiveList: main.ListUnmanaged(PositionEntry) = .{};
|
||||
defer {
|
||||
@ -238,7 +236,7 @@ pub const ChannelChunk = struct {
|
||||
|
||||
for(chunk.Neighbor.iterable) |neighbor| {
|
||||
if(neighborLists[neighbor.toInt()].items.len == 0) continue;
|
||||
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
const neighborMesh = mesh_storage.getNeighbor(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
constructiveEntries.append(main.stackAllocator, .{
|
||||
.mesh = neighborMesh,
|
||||
.entries = neighborMesh.lightingData[@intFromBool(self.isSun)].propagateDestructiveFromNeighbor(lightQueue, neighborLists[neighbor.toInt()].items, constructiveEntries, lightRefreshList),
|
||||
@ -248,7 +246,7 @@ pub const ChannelChunk = struct {
|
||||
return constructiveList;
|
||||
}
|
||||
|
||||
fn propagateFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
|
||||
fn propagateFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
std.debug.assert(lightQueue.empty());
|
||||
for(lights) |entry| {
|
||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||
@ -259,7 +257,7 @@ pub const ChannelChunk = struct {
|
||||
self.propagateDirect(lightQueue, lightRefreshList);
|
||||
}
|
||||
|
||||
fn propagateDestructiveFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry, constructiveEntries: *main.ListUnmanaged(ChunkEntries), lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) main.ListUnmanaged(PositionEntry) {
|
||||
fn propagateDestructiveFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry, constructiveEntries: *main.ListUnmanaged(ChunkEntries), lightRefreshList: *main.List(chunk.ChunkPosition)) main.ListUnmanaged(PositionEntry) {
|
||||
std.debug.assert(lightQueue.empty());
|
||||
for(lights) |entry| {
|
||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||
@ -270,7 +268,7 @@ pub const ChannelChunk = struct {
|
||||
return self.propagateDestructive(lightQueue, constructiveEntries, false, lightRefreshList);
|
||||
}
|
||||
|
||||
pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
|
||||
pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
|
||||
defer lightQueue.deinit();
|
||||
for(lights) |pos| {
|
||||
@ -307,8 +305,7 @@ pub const ChannelChunk = struct {
|
||||
const otherX = x +% neighbor.relX() & chunk.chunkMask;
|
||||
const otherY = y +% neighbor.relY() & chunk.chunkMask;
|
||||
const otherZ = z +% neighbor.relZ() & chunk.chunkMask;
|
||||
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
const neighborMesh = mesh_storage.getNeighbor(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
const neighborLightChunk = neighborMesh.lightingData[@intFromBool(self.isSun)];
|
||||
neighborLightChunk.lock.lockRead();
|
||||
defer neighborLightChunk.lock.unlockRead();
|
||||
@ -331,7 +328,7 @@ pub const ChannelChunk = struct {
|
||||
self.propagateDirect(&lightQueue, lightRefreshList);
|
||||
}
|
||||
|
||||
pub fn propagateUniformSun(self: *ChannelChunk, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
|
||||
pub fn propagateUniformSun(self: *ChannelChunk, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
std.debug.assert(self.isSun);
|
||||
self.lock.lockWrite();
|
||||
if(self.data.paletteLength != 1) {
|
||||
@ -345,8 +342,7 @@ pub const ChannelChunk = struct {
|
||||
defer lightQueue.deinit();
|
||||
for(chunk.Neighbor.iterable) |neighbor| {
|
||||
if(neighbor == .dirUp) continue;
|
||||
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
defer neighborMesh.decreaseRefCount();
|
||||
const neighborMesh = mesh_storage.getNeighbor(self.ch.pos, self.ch.pos.voxelSize, neighbor) orelse continue;
|
||||
var list: [chunk.chunkSize*chunk.chunkSize]Entry = undefined;
|
||||
for(0..chunk.chunkSize) |x| {
|
||||
for(0..chunk.chunkSize) |y| {
|
||||
@ -379,7 +375,7 @@ pub const ChannelChunk = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn propagateLightsDestructive(self: *ChannelChunk, lights: []const [3]u8, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
|
||||
pub fn propagateLightsDestructive(self: *ChannelChunk, lights: []const [3]u8, lightRefreshList: *main.List(chunk.ChunkPosition)) void {
|
||||
var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
|
||||
defer lightQueue.deinit();
|
||||
self.lock.lockRead();
|
||||
@ -396,7 +392,6 @@ pub const ChannelChunk = struct {
|
||||
});
|
||||
for(constructiveEntries.items) |entries| {
|
||||
const mesh = entries.mesh;
|
||||
defer if(mesh) |_mesh| _mesh.decreaseRefCount();
|
||||
var entryList = entries.entries;
|
||||
defer entryList.deinit(main.stackAllocator);
|
||||
const channelChunk = if(mesh) |_mesh| _mesh.lightingData[@intFromBool(self.isSun)] else self;
|
||||
|
@ -22,24 +22,22 @@ const chunk_meshing = @import("chunk_meshing.zig");
|
||||
const ChunkMesh = chunk_meshing.ChunkMesh;
|
||||
|
||||
const ChunkMeshNode = struct {
|
||||
mesh: ?*chunk_meshing.ChunkMesh = null,
|
||||
mesh: Atomic(?*chunk_meshing.ChunkMesh) = .init(null),
|
||||
active: bool = false,
|
||||
rendered: bool = false,
|
||||
finishedMeshing: bool = false, // Must be synced with mesh.finishedMeshing
|
||||
finishedMeshingHigherResolution: u8 = 0, // Must be synced with finishedMeshing of the 8 higher resolution chunks.
|
||||
pos: chunk.ChunkPosition = undefined,
|
||||
isNeighborLod: [6]bool = @splat(false), // Must be synced with mesh.isNeighborLod
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
};
|
||||
const storageSize = 64;
|
||||
const storageMask = storageSize - 1;
|
||||
var storageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined;
|
||||
var mapStorageLists: [settings.highestSupportedLod + 1]*[storageSize*storageSize]?*LightMap.LightMapFragment = undefined;
|
||||
var meshList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var priorityMeshUpdateList: main.utils.ConcurrentQueue(*chunk_meshing.ChunkMesh) = undefined;
|
||||
pub var updatableList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var priorityMeshUpdateList: main.utils.ConcurrentQueue(chunk.ChunkPosition) = undefined;
|
||||
pub var updatableList = main.List(chunk.ChunkPosition).init(main.globalAllocator);
|
||||
var mapUpdatableList: main.utils.ConcurrentQueue(*LightMap.LightMapFragment) = undefined;
|
||||
var clearList = main.List(*chunk_meshing.ChunkMesh).init(main.globalAllocator);
|
||||
var lastPx: i32 = 0;
|
||||
var lastPy: i32 = 0;
|
||||
var lastPz: i32 = 0;
|
||||
@ -74,7 +72,7 @@ pub const BlockUpdate = struct {
|
||||
|
||||
var blockUpdateList: main.utils.ConcurrentQueue(BlockUpdate) = undefined;
|
||||
|
||||
var meshMemoryPool: main.heap.MemoryPool(chunk_meshing.ChunkMesh) = undefined;
|
||||
pub var meshMemoryPool: main.heap.MemoryPool(chunk_meshing.ChunkMesh) = undefined;
|
||||
|
||||
pub fn init() void { // MARK: init()
|
||||
lastRD = 0;
|
||||
@ -104,6 +102,7 @@ pub fn deinit() void {
|
||||
lastPz = 0;
|
||||
lastRD = 0;
|
||||
freeOldMeshes(olderPx, olderPy, olderPz, olderRD);
|
||||
main.heap.GarbageCollection.waitForFreeCompletion();
|
||||
for(storageLists) |storageList| {
|
||||
main.globalAllocator.destroy(storageList);
|
||||
}
|
||||
@ -111,28 +110,17 @@ pub fn deinit() void {
|
||||
main.globalAllocator.destroy(mapStorageList);
|
||||
}
|
||||
|
||||
for(updatableList.items) |mesh| {
|
||||
mesh.decreaseRefCount();
|
||||
}
|
||||
updatableList.clearAndFree();
|
||||
while(mapUpdatableList.dequeue()) |map| {
|
||||
map.decreaseRefCount();
|
||||
}
|
||||
mapUpdatableList.deinit();
|
||||
while(priorityMeshUpdateList.dequeue()) |mesh| {
|
||||
mesh.decreaseRefCount();
|
||||
}
|
||||
priorityMeshUpdateList.deinit();
|
||||
while(blockUpdateList.dequeue()) |blockUpdate| {
|
||||
blockUpdate.deinitManaged(main.globalAllocator);
|
||||
}
|
||||
blockUpdateList.deinit();
|
||||
meshList.clearAndFree();
|
||||
for(clearList.items) |mesh| {
|
||||
mesh.deinit();
|
||||
meshMemoryPool.destroy(mesh);
|
||||
}
|
||||
clearList.clearAndFree();
|
||||
meshMemoryPool.deinit();
|
||||
}
|
||||
|
||||
@ -191,20 +179,16 @@ pub fn getLightMapPieceAndIncreaseRefCount(x: i32, y: i32, voxelSize: u31) ?*Lig
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn getBlock(x: i32, y: i32, z: i32) ?blocks.Block {
|
||||
pub fn getBlockFromRenderThread(x: i32, y: i32, z: i32) ?blocks.Block {
|
||||
const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize = 1});
|
||||
node.mutex.lock();
|
||||
defer node.mutex.unlock();
|
||||
const mesh = node.mesh 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;
|
||||
}
|
||||
|
||||
pub fn triggerOnInteractBlock(x: i32, y: i32, z: i32) EventStatus {
|
||||
pub fn triggerOnInteractBlockFromRenderThread(x: i32, y: i32, z: i32) EventStatus {
|
||||
const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize = 1});
|
||||
node.mutex.lock();
|
||||
defer node.mutex.unlock();
|
||||
const mesh = node.mesh orelse return .ignored;
|
||||
const mesh = node.mesh.load(.acquire) orelse return .ignored;
|
||||
const block = mesh.chunk.getBlock(x & chunk.chunkMask, y & chunk.chunkMask, z & chunk.chunkMask);
|
||||
if(block.blockEntity()) |blockEntity| {
|
||||
return blockEntity.onInteract(.{x, y, z}, mesh.chunk);
|
||||
@ -215,9 +199,7 @@ pub fn triggerOnInteractBlock(x: i32, y: i32, z: i32) EventStatus {
|
||||
|
||||
pub fn getLight(wx: i32, wy: i32, wz: i32) ?[6]u8 {
|
||||
const node = getNodePointer(.{.wx = wx, .wy = wy, .wz = wz, .voxelSize = 1});
|
||||
node.mutex.lock();
|
||||
defer node.mutex.unlock();
|
||||
const mesh = node.mesh orelse return null;
|
||||
const mesh = node.mesh.load(.acquire) orelse return null;
|
||||
const x = (wx >> mesh.chunk.voxelSizeShift) & chunk.chunkMask;
|
||||
const y = (wy >> mesh.chunk.voxelSizeShift) & chunk.chunkMask;
|
||||
const z = (wz >> mesh.chunk.voxelSizeShift) & chunk.chunkMask;
|
||||
@ -228,53 +210,44 @@ pub fn getLight(wx: i32, wy: i32, wz: i32) ?[6]u8 {
|
||||
return mesh.lightingData[1].getValue(x, y, z) ++ mesh.lightingData[0].getValue(x, y, z);
|
||||
}
|
||||
|
||||
pub fn getBlockFromAnyLod(x: i32, y: i32, z: i32) blocks.Block {
|
||||
pub fn getBlockFromAnyLodFromRenderThread(x: i32, y: i32, z: i32) blocks.Block {
|
||||
var lod: u5 = 0;
|
||||
while(lod < settings.highestLod) : (lod += 1) {
|
||||
const node = getNodePointer(.{.wx = x, .wy = y, .wz = z, .voxelSize = @as(u31, 1) << lod});
|
||||
node.mutex.lock();
|
||||
defer node.mutex.unlock();
|
||||
const mesh = node.mesh 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;
|
||||
}
|
||||
return blocks.Block{.typ = 0, .data = 0};
|
||||
}
|
||||
|
||||
pub fn getMeshAndIncreaseRefCount(pos: chunk.ChunkPosition) ?*chunk_meshing.ChunkMesh {
|
||||
pub fn getMesh(pos: chunk.ChunkPosition) ?*chunk_meshing.ChunkMesh {
|
||||
const lod = std.math.log2_int(u31, pos.voxelSize);
|
||||
const mask = ~((@as(i32, 1) << lod + chunk.chunkShift) - 1);
|
||||
const node = getNodePointer(pos);
|
||||
node.mutex.lock();
|
||||
const mesh = node.mesh orelse {
|
||||
node.mutex.unlock();
|
||||
return null;
|
||||
};
|
||||
mesh.increaseRefCount();
|
||||
node.mutex.unlock();
|
||||
const mesh = node.mesh.load(.acquire) orelse return null;
|
||||
if(pos.wx & mask != mesh.pos.wx or pos.wy & mask != mesh.pos.wy or pos.wz & mask != mesh.pos.wz) {
|
||||
mesh.decreaseRefCount();
|
||||
return null;
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
pub fn getMeshFromAnyLodAndIncreaseRefCount(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk_meshing.ChunkMesh {
|
||||
pub fn getMeshFromAnyLod(wx: i32, wy: i32, wz: i32, voxelSize: u31) ?*chunk_meshing.ChunkMesh {
|
||||
var lod: u5 = @ctz(voxelSize);
|
||||
while(lod < settings.highestLod) : (lod += 1) {
|
||||
const mesh = getMeshAndIncreaseRefCount(.{.wx = wx & ~chunk.chunkMask << lod, .wy = wy & ~chunk.chunkMask << lod, .wz = wz & ~chunk.chunkMask << lod, .voxelSize = @as(u31, 1) << lod});
|
||||
const mesh = getMesh(.{.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: chunk.Neighbor) ?*chunk_meshing.ChunkMesh {
|
||||
pub fn getNeighbor(_pos: chunk.ChunkPosition, resolution: u31, neighbor: chunk.Neighbor) ?*chunk_meshing.ChunkMesh {
|
||||
var pos = _pos;
|
||||
pos.wx +%= pos.voxelSize*chunk.chunkSize*neighbor.relX();
|
||||
pos.wy +%= pos.voxelSize*chunk.chunkSize*neighbor.relY();
|
||||
pos.wz +%= pos.voxelSize*chunk.chunkSize*neighbor.relZ();
|
||||
pos.voxelSize = resolution;
|
||||
return getMeshAndIncreaseRefCount(pos);
|
||||
return getMesh(pos);
|
||||
}
|
||||
|
||||
fn reduceRenderDistance(fullRenderDistance: i64, reduction: i64) i32 {
|
||||
@ -399,15 +372,15 @@ fn freeOldMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16) void {
|
||||
const index = (xIndex*storageSize + yIndex)*storageSize + zIndex;
|
||||
|
||||
const node = &storageLists[_lod][@intCast(index)];
|
||||
node.mutex.lock();
|
||||
const oldMesh = node.mesh;
|
||||
node.mesh = null;
|
||||
node.mutex.unlock();
|
||||
const oldMesh = node.mesh.swap(null, .monotonic);
|
||||
node.pos = undefined;
|
||||
if(oldMesh) |mesh| {
|
||||
node.finishedMeshing = false;
|
||||
updateHigherLodNodeFinishedMeshing(mesh.pos, false);
|
||||
mesh.decreaseRefCount();
|
||||
main.heap.GarbageCollection.deferredFree(.{
|
||||
.ptr = mesh,
|
||||
.freeFunction = main.utils.castFunctionSelfToAnyopaque(ChunkMesh.deinit),
|
||||
});
|
||||
}
|
||||
node.isNeighborLod = @splat(false);
|
||||
}
|
||||
@ -542,14 +515,12 @@ fn createNewMeshes(olderPx: i32, olderPy: i32, olderPz: i32, olderRD: u16, meshR
|
||||
const pos = chunk.ChunkPosition{.wx = x, .wy = y, .wz = z, .voxelSize = @as(u31, 1) << lod};
|
||||
|
||||
const node = &storageLists[_lod][@intCast(index)];
|
||||
node.mutex.lock();
|
||||
node.pos = pos;
|
||||
if(node.mesh) |mesh| {
|
||||
if(node.mesh.load(.acquire)) |mesh| {
|
||||
std.debug.assert(std.meta.eql(pos, mesh.pos));
|
||||
} else {
|
||||
meshRequests.append(pos);
|
||||
}
|
||||
node.mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -756,7 +727,7 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co
|
||||
}
|
||||
}
|
||||
if(!std.meta.eql(node.isNeighborLod, isNeighborLod)) {
|
||||
const mesh = node.mesh.?; // No need to lock the mutex, since no other thread is allowed to overwrite the mesh (unless it's null).
|
||||
const mesh = node.mesh.load(.acquire).?; // no other thread is allowed to overwrite the mesh (unless it's null).
|
||||
mesh.isNeighborLod = isNeighborLod;
|
||||
node.isNeighborLod = isNeighborLod;
|
||||
mesh.uploadData();
|
||||
@ -766,14 +737,12 @@ pub noinline fn updateAndGetRenderChunks(conn: *network.Connection, frustum: *co
|
||||
node.rendered = false;
|
||||
if(!node.finishedMeshing) continue;
|
||||
|
||||
const mesh = node.mesh.?; // No need to lock the mutex, since no other thread is allowed to overwrite the mesh (unless it's null).
|
||||
const mesh = node.mesh.load(.acquire).?; // no other thread is allowed to overwrite the mesh (unless it's null).
|
||||
|
||||
node.mutex.lock();
|
||||
if(mesh.needsMeshUpdate) {
|
||||
mesh.uploadData();
|
||||
mesh.needsMeshUpdate = false;
|
||||
}
|
||||
node.mutex.unlock();
|
||||
// Remove empty meshes.
|
||||
if(!mesh.isEmpty()) {
|
||||
meshList.append(mesh);
|
||||
@ -788,32 +757,14 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()=
|
||||
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
for(clearList.items) |mesh| {
|
||||
mesh.deinit();
|
||||
meshMemoryPool.destroy(mesh);
|
||||
}
|
||||
clearList.clearRetainingCapacity();
|
||||
while(priorityMeshUpdateList.dequeue()) |mesh| {
|
||||
while(priorityMeshUpdateList.dequeue()) |pos| {
|
||||
const mesh = getMesh(pos) orelse continue;
|
||||
if(!mesh.needsMeshUpdate) {
|
||||
mutex.unlock();
|
||||
defer mutex.lock();
|
||||
mesh.decreaseRefCount();
|
||||
continue;
|
||||
}
|
||||
mesh.needsMeshUpdate = false;
|
||||
const node = getNodePointer(mesh.pos);
|
||||
node.mutex.lock();
|
||||
if(node.mesh != mesh) {
|
||||
node.mutex.unlock();
|
||||
mutex.unlock();
|
||||
defer mutex.lock();
|
||||
mesh.decreaseRefCount();
|
||||
continue;
|
||||
}
|
||||
node.mutex.unlock();
|
||||
mutex.unlock();
|
||||
defer mutex.lock();
|
||||
mesh.decreaseRefCount();
|
||||
mesh.uploadData();
|
||||
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
|
||||
}
|
||||
@ -836,15 +787,14 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()=
|
||||
{
|
||||
var i: usize = 0;
|
||||
while(i < updatableList.items.len) {
|
||||
const mesh = updatableList.items[i];
|
||||
if(!isInRenderDistance(mesh.pos)) {
|
||||
const pos = updatableList.items[i];
|
||||
if(!isInRenderDistance(pos)) {
|
||||
_ = updatableList.swapRemove(i);
|
||||
mutex.unlock();
|
||||
defer mutex.lock();
|
||||
mesh.decreaseRefCount();
|
||||
continue;
|
||||
}
|
||||
const priority = mesh.pos.getPriority(playerPos);
|
||||
const priority = pos.getPriority(playerPos);
|
||||
if(priority > closestPriority) {
|
||||
closestPriority = priority;
|
||||
closestIndex = i;
|
||||
@ -853,32 +803,24 @@ pub fn updateMeshes(targetTime: i64) void { // MARK: updateMeshes()=
|
||||
}
|
||||
if(updatableList.items.len == 0) break;
|
||||
}
|
||||
const mesh = updatableList.swapRemove(closestIndex);
|
||||
const pos = updatableList.swapRemove(closestIndex);
|
||||
mutex.unlock();
|
||||
defer mutex.lock();
|
||||
if(isInRenderDistance(mesh.pos)) {
|
||||
const node = getNodePointer(mesh.pos);
|
||||
std.debug.assert(std.meta.eql(node.pos, mesh.pos));
|
||||
if(isInRenderDistance(pos)) {
|
||||
const node = getNodePointer(pos);
|
||||
if(node.finishedMeshing) continue;
|
||||
const mesh = getMesh(pos) orelse continue;
|
||||
node.finishedMeshing = true;
|
||||
mesh.finishedMeshing = true;
|
||||
updateHigherLodNodeFinishedMeshing(mesh.pos, true);
|
||||
updateHigherLodNodeFinishedMeshing(pos, true);
|
||||
mesh.uploadData();
|
||||
node.mutex.lock();
|
||||
const oldMesh = node.mesh;
|
||||
node.mesh = mesh;
|
||||
node.mutex.unlock();
|
||||
if(oldMesh) |_oldMesh| {
|
||||
_oldMesh.decreaseRefCount();
|
||||
}
|
||||
} else {
|
||||
mesh.decreaseRefCount();
|
||||
}
|
||||
if(std.time.milliTimestamp() >= targetTime) break; // Update at least one mesh.
|
||||
}
|
||||
}
|
||||
|
||||
fn batchUpdateBlocks() void {
|
||||
var lightRefreshList = main.List(*ChunkMesh).init(main.stackAllocator);
|
||||
var lightRefreshList = main.List(chunk.ChunkPosition).init(main.stackAllocator);
|
||||
defer lightRefreshList.deinit();
|
||||
|
||||
var regenerateMeshList = main.List(*ChunkMesh).init(main.stackAllocator);
|
||||
@ -888,74 +830,51 @@ fn batchUpdateBlocks() void {
|
||||
while(blockUpdateList.dequeue()) |blockUpdate| {
|
||||
defer blockUpdate.deinitManaged(main.globalAllocator);
|
||||
const pos = chunk.ChunkPosition{.wx = blockUpdate.x, .wy = blockUpdate.y, .wz = blockUpdate.z, .voxelSize = 1};
|
||||
if(getMeshAndIncreaseRefCount(pos)) |mesh| {
|
||||
if(getMesh(pos)) |mesh| {
|
||||
mesh.updateBlock(blockUpdate.x, blockUpdate.y, blockUpdate.z, blockUpdate.newBlock, blockUpdate.blockEntityData, &lightRefreshList, ®enerateMeshList);
|
||||
mesh.decreaseRefCount();
|
||||
} // TODO: It seems like we simply ignore the block update if we don't have the mesh yet.
|
||||
}
|
||||
for(regenerateMeshList.items) |mesh| {
|
||||
mesh.generateMesh(&lightRefreshList);
|
||||
}
|
||||
{
|
||||
for(lightRefreshList.items) |mesh| {
|
||||
if(mesh.needsLightRefresh.load(.unordered)) {
|
||||
mesh.scheduleLightRefreshAndDecreaseRefCount();
|
||||
} else {
|
||||
mesh.decreaseRefCount();
|
||||
}
|
||||
}
|
||||
for(lightRefreshList.items) |pos| {
|
||||
ChunkMesh.scheduleLightRefresh(pos);
|
||||
}
|
||||
for(regenerateMeshList.items) |mesh| {
|
||||
mesh.uploadData();
|
||||
mesh.decreaseRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: adders
|
||||
|
||||
pub fn addMeshToClearListAndDecreaseRefCount(mesh: *chunk_meshing.ChunkMesh) void {
|
||||
std.debug.assert(mesh.refCount.load(.monotonic) == 0);
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
clearList.append(mesh);
|
||||
}
|
||||
|
||||
pub fn addToUpdateListAndDecreaseRefCount(mesh: *chunk_meshing.ChunkMesh) void {
|
||||
pub fn addToUpdateList(mesh: *chunk_meshing.ChunkMesh) void {
|
||||
std.debug.assert(mesh.refCount.load(.monotonic) != 0);
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
if(mesh.finishedMeshing) {
|
||||
priorityMeshUpdateList.enqueue(mesh);
|
||||
priorityMeshUpdateList.enqueue(mesh.pos);
|
||||
mesh.needsMeshUpdate = true;
|
||||
} else {
|
||||
mutex.unlock();
|
||||
defer mutex.lock();
|
||||
mesh.decreaseRefCount();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addMeshToStorage(mesh: *chunk_meshing.ChunkMesh) error{AlreadyStored}!void {
|
||||
pub fn addMeshToStorage(mesh: *chunk_meshing.ChunkMesh) error{AlreadyStored, NoLongerNeeded}!void {
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
if(isInRenderDistance(mesh.pos)) {
|
||||
const node = getNodePointer(mesh.pos);
|
||||
node.mutex.lock();
|
||||
defer node.mutex.unlock();
|
||||
if(node.mesh != null) {
|
||||
return error.AlreadyStored;
|
||||
}
|
||||
node.mesh = mesh;
|
||||
node.finishedMeshing = mesh.finishedMeshing;
|
||||
updateHigherLodNodeFinishedMeshing(mesh.pos, mesh.finishedMeshing);
|
||||
mesh.increaseRefCount();
|
||||
if(!isInRenderDistance(mesh.pos)) {
|
||||
return error.NoLongerNeeded;
|
||||
}
|
||||
const node = getNodePointer(mesh.pos);
|
||||
if(node.mesh.cmpxchgStrong(null, mesh, .release, .monotonic) != null) {
|
||||
return error.AlreadyStored;
|
||||
}
|
||||
node.finishedMeshing = mesh.finishedMeshing;
|
||||
updateHigherLodNodeFinishedMeshing(mesh.pos, mesh.finishedMeshing);
|
||||
}
|
||||
|
||||
pub fn finishMesh(mesh: *chunk_meshing.ChunkMesh) void {
|
||||
pub fn finishMesh(pos: chunk.ChunkPosition) void {
|
||||
mutex.lock();
|
||||
defer mutex.unlock();
|
||||
mesh.increaseRefCount();
|
||||
updatableList.append(mesh);
|
||||
updatableList.append(pos);
|
||||
}
|
||||
|
||||
pub const MeshGenerationTask = struct { // MARK: MeshGenerationTask
|
||||
@ -991,10 +910,8 @@ pub const MeshGenerationTask = struct { // MARK: MeshGenerationTask
|
||||
pub fn run(self: *MeshGenerationTask) void {
|
||||
defer main.globalAllocator.destroy(self);
|
||||
const pos = self.mesh.pos;
|
||||
const mesh = meshMemoryPool.create();
|
||||
mesh.init(pos, self.mesh);
|
||||
defer mesh.decreaseRefCount();
|
||||
mesh.generateLightingData() catch return;
|
||||
const mesh = ChunkMesh.init(pos, self.mesh);
|
||||
mesh.generateLightingData() catch mesh.deinit(undefined);
|
||||
}
|
||||
|
||||
pub fn clean(self: *MeshGenerationTask) void {
|
||||
@ -1023,7 +940,7 @@ pub fn addBreakingAnimation(pos: Vec3i, breakingProgress: f32) void {
|
||||
const animationFrame: usize = @intFromFloat(breakingProgress*@as(f32, @floatFromInt(main.blocks.meshes.blockBreakingTextures.items.len)));
|
||||
const texture = main.blocks.meshes.blockBreakingTextures.items[animationFrame];
|
||||
|
||||
const block = getBlock(pos[0], pos[1], pos[2]) orelse return;
|
||||
const block = getBlockFromRenderThread(pos[0], pos[1], pos[2]) orelse return;
|
||||
const model = main.blocks.meshes.model(block).model();
|
||||
|
||||
for(model.internalQuads) |quadIndex| {
|
||||
@ -1039,8 +956,7 @@ pub fn addBreakingAnimation(pos: Vec3i, breakingProgress: f32) void {
|
||||
fn addBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, texture: u16, neighbor: ?chunk.Neighbor, isTransparent: bool) void {
|
||||
const worldPos = pos +% if(neighbor) |n| n.relPos() else Vec3i{0, 0, 0};
|
||||
const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask));
|
||||
const mesh = getMeshAndIncreaseRefCount(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return;
|
||||
defer mesh.decreaseRefCount();
|
||||
const mesh = getMesh(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return;
|
||||
mesh.mutex.lock();
|
||||
defer mesh.mutex.unlock();
|
||||
const lightIndex = blk: {
|
||||
@ -1074,8 +990,7 @@ fn addBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, textur
|
||||
fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, neighbor: ?chunk.Neighbor) void {
|
||||
const worldPos = pos +% if(neighbor) |n| n.relPos() else Vec3i{0, 0, 0};
|
||||
const relPos = worldPos & @as(Vec3i, @splat(main.chunk.chunkMask));
|
||||
const mesh = getMeshAndIncreaseRefCount(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return;
|
||||
defer mesh.decreaseRefCount();
|
||||
const mesh = getMesh(.{.wx = worldPos[0], .wy = worldPos[1], .wz = worldPos[2], .voxelSize = 1}) orelse return;
|
||||
for(mesh.blockBreakingFaces.items, 0..) |face, i| {
|
||||
if(face.position.x == relPos[0] and face.position.y == relPos[1] and face.position.z == relPos[2] and face.blockAndQuad.quadIndex == quadIndex) {
|
||||
_ = mesh.blockBreakingFaces.swapRemove(i);
|
||||
@ -1086,7 +1001,7 @@ fn removeBreakingAnimationFace(pos: Vec3i, quadIndex: main.models.QuadIndex, nei
|
||||
}
|
||||
|
||||
pub fn removeBreakingAnimation(pos: Vec3i) void {
|
||||
const block = getBlock(pos[0], pos[1], pos[2]) orelse return;
|
||||
const block = getBlockFromRenderThread(pos[0], pos[1], pos[2]) orelse return;
|
||||
const model = main.blocks.meshes.model(block).model();
|
||||
|
||||
for(model.internalQuads) |quadIndex| {
|
||||
|
@ -442,6 +442,7 @@ pub fn start(name: []const u8, port: ?u16) void {
|
||||
defer deinit();
|
||||
running.store(true, .release);
|
||||
while(running.load(.monotonic)) {
|
||||
main.heap.GarbageCollection.syncPoint();
|
||||
const newTime = std.time.nanoTimestamp();
|
||||
if(newTime -% lastTime < updateNanoTime) {
|
||||
std.Thread.sleep(@intCast(lastTime +% updateNanoTime -% newTime));
|
||||
|
@ -749,15 +749,17 @@ pub fn BlockingMaxHeap(comptime T: type) type { // MARK: BlockingMaxHeap
|
||||
|
||||
/// Returns the biggest element and removes it from the heap.
|
||||
/// If empty blocks until a new object is added or the datastructure is closed.
|
||||
pub fn extractMax(self: *@This()) !T {
|
||||
pub fn extractMax(self: *@This()) error{Timeout, Closed}!T {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
|
||||
const startTime = std.time.nanoTimestamp();
|
||||
|
||||
while(true) {
|
||||
if(self.size == 0) {
|
||||
self.waitingThreadCount += 1;
|
||||
self.waitingThreads.wait(&self.mutex);
|
||||
self.waitingThreadCount -= 1;
|
||||
defer self.waitingThreadCount -= 1;
|
||||
try self.waitingThreads.timedWait(&self.mutex, 10_000_000);
|
||||
} else {
|
||||
const ret = self.array[0];
|
||||
self.removeIndex(0);
|
||||
@ -766,6 +768,7 @@ pub fn BlockingMaxHeap(comptime T: type) type { // MARK: BlockingMaxHeap
|
||||
if(self.closed) {
|
||||
return error.Closed;
|
||||
}
|
||||
if(std.time.nanoTimestamp() -% startTime > 10_000_000) return error.Timeout;
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,9 +912,13 @@ pub const ThreadPool = struct { // MARK: ThreadPool
|
||||
defer main.deinitThreadLocals();
|
||||
|
||||
var lastUpdate = std.time.milliTimestamp();
|
||||
while(true) {
|
||||
outer: while(true) {
|
||||
main.heap.GarbageCollection.syncPoint();
|
||||
{
|
||||
const task = self.loadList.extractMax() catch break;
|
||||
const task = self.loadList.extractMax() catch |err| switch(err) {
|
||||
error.Timeout => continue :outer,
|
||||
error.Closed => break :outer,
|
||||
};
|
||||
self.currentTasks[id].store(task.vtable, .monotonic);
|
||||
const start = std.time.microTimestamp();
|
||||
task.vtable.run(task.self);
|
||||
|
@ -577,6 +577,113 @@ pub fn MemoryPool(Item: type) type { // MARK: MemoryPool
|
||||
};
|
||||
}
|
||||
|
||||
pub const GarbageCollection = struct { // MARK: GarbageCollection
|
||||
var sharedState: std.atomic.Value(u32) = .init(0);
|
||||
threadlocal var threadCycle: u2 = undefined;
|
||||
threadlocal var lastSyncPointTime: i64 = undefined;
|
||||
const FreeItem = struct {
|
||||
ptr: *anyopaque,
|
||||
extraData: usize = 0,
|
||||
freeFunction: *const fn(*anyopaque, usize) void,
|
||||
};
|
||||
threadlocal var lists: [4]main.ListUnmanaged(FreeItem) = undefined;
|
||||
|
||||
const State = packed struct {
|
||||
waitingThreads: u15 = 0,
|
||||
totalThreads: u15 = 0,
|
||||
cycle: u2 = 0,
|
||||
};
|
||||
|
||||
pub fn addThread() void {
|
||||
const old: State = @bitCast(sharedState.fetchAdd(@bitCast(State{.totalThreads = 1}), .monotonic));
|
||||
_ = old.totalThreads + 1; // Assert no overflow
|
||||
threadCycle = old.cycle;
|
||||
lastSyncPointTime = std.time.milliTimestamp();
|
||||
for(&lists) |*list| {
|
||||
list.* = .initCapacity(main.globalAllocator, 1024);
|
||||
}
|
||||
if(old.waitingThreads == 0) {
|
||||
startNewCycle();
|
||||
}
|
||||
}
|
||||
|
||||
fn freeItemsFromList(list: *main.ListUnmanaged(FreeItem)) void {
|
||||
while(list.popOrNull()) |item| {
|
||||
item.freeFunction(item.ptr, item.extraData);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn removeThread() void {
|
||||
const old: State = @bitCast(sharedState.fetchSub(@bitCast(State{.totalThreads = 1}), .monotonic));
|
||||
_ = old.totalThreads - 1; // Assert no overflow
|
||||
if(old.cycle != threadCycle) removeThreadFromWaiting();
|
||||
const newTime = std.time.milliTimestamp();
|
||||
if(newTime -% lastSyncPointTime > 10_000) {
|
||||
std.log.err("No sync point executed in {} ms for thread. Did you forget to add a sync point in the thread's main loop?", .{newTime -% lastSyncPointTime});
|
||||
std.debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
for(&lists) |*list| {
|
||||
freeItemsFromList(list);
|
||||
list.deinit(main.globalAllocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assertAllThreadsStopped() void {
|
||||
std.debug.assert(sharedState.load(.unordered) & 0x3fffffff == 0);
|
||||
}
|
||||
|
||||
fn startNewCycle() void {
|
||||
var cur = sharedState.load(.unordered);
|
||||
while(true) {
|
||||
var new: State = @bitCast(cur);
|
||||
new.waitingThreads = new.totalThreads;
|
||||
new.cycle +%= 1;
|
||||
cur = sharedState.cmpxchgWeak(cur, @bitCast(new), .monotonic, .monotonic) orelse break;
|
||||
}
|
||||
}
|
||||
|
||||
fn removeThreadFromWaiting() void {
|
||||
const old: State = @bitCast(sharedState.fetchSub(@bitCast(State{.waitingThreads = 1}), .acq_rel));
|
||||
_ = old.waitingThreads - 1; // Assert no overflow
|
||||
threadCycle = old.cycle;
|
||||
|
||||
if(old.waitingThreads == 1) startNewCycle();
|
||||
}
|
||||
|
||||
/// Must be called when no objects originating from other threads are held on the current function stack
|
||||
pub fn syncPoint() void {
|
||||
const newTime = std.time.milliTimestamp();
|
||||
if(newTime -% lastSyncPointTime > 10_000) {
|
||||
std.log.err("No sync point executed in {} ms. Did you forget to add a sync point in the thread's main loop", .{newTime -% lastSyncPointTime});
|
||||
std.debug.dumpCurrentStackTrace(null);
|
||||
}
|
||||
lastSyncPointTime = newTime;
|
||||
|
||||
const old: State = @bitCast(sharedState.load(.unordered));
|
||||
if(old.cycle == threadCycle) return;
|
||||
removeThreadFromWaiting();
|
||||
freeItemsFromList(&lists[threadCycle]);
|
||||
// TODO: Free all the data here and swap lists
|
||||
}
|
||||
|
||||
pub fn deferredFree(item: FreeItem) void {
|
||||
lists[threadCycle].append(main.globalAllocator, item);
|
||||
}
|
||||
|
||||
/// Waits until all deferred frees have been completed.
|
||||
pub fn waitForFreeCompletion() void {
|
||||
const startCycle = threadCycle;
|
||||
while(threadCycle == startCycle) {
|
||||
syncPoint();
|
||||
std.Thread.sleep(1_000_000);
|
||||
}
|
||||
while(threadCycle != startCycle) {
|
||||
syncPoint();
|
||||
std.Thread.sleep(1_000_000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn PowerOfTwoPoolAllocator(minSize: comptime_int, maxSize: comptime_int, maxAlignment: comptime_int) type { // MARK: PowerOfTwoPoolAllocator
|
||||
std.debug.assert(std.math.isPowerOfTwo(minSize));
|
||||
std.debug.assert(std.math.isPowerOfTwo(maxSize));
|
||||
|
Loading…
x
Reference in New Issue
Block a user