Remove redundant light refreshes.

helps with #277
This commit is contained in:
IntegratedQuantum 2024-06-27 22:49:22 +02:00
parent d40da846a0
commit 04f9bae91c
2 changed files with 81 additions and 46 deletions

View File

@ -717,12 +717,8 @@ pub const ChunkMesh = struct {
} }
} }
pub fn scheduleLightRefreshAndDecreaseRefCount(self: *ChunkMesh) void { pub fn scheduleLightRefreshAndDecreaseRefCount1(self: *ChunkMesh) void {
if(!self.needsLightRefresh.swap(true, .acq_rel)) { LightRefreshTask.scheduleAndDecreaseRefCount(self);
LightRefreshTask.scheduleAndDecreaseRefCount(self);
} else {
self.decreaseRefCount();
}
} }
const LightRefreshTask = struct { const LightRefreshTask = struct {
mesh: *ChunkMesh, mesh: *ChunkMesh,
@ -783,7 +779,7 @@ pub const ChunkMesh = struct {
); );
} }
fn initLight(self: *ChunkMesh) void { fn initLight(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
self.mutex.lock(); self.mutex.lock();
var lightEmittingBlocks = main.List([3]u8).init(main.stackAllocator); var lightEmittingBlocks = main.List([3]u8).init(main.stackAllocator);
defer lightEmittingBlocks.deinit(); defer lightEmittingBlocks.deinit();
@ -799,7 +795,7 @@ pub const ChunkMesh = struct {
} }
} }
self.mutex.unlock(); self.mutex.unlock();
self.lightingData[0].propagateLights(lightEmittingBlocks.items, true); self.lightingData[0].propagateLights(lightEmittingBlocks.items, true, lightRefreshList);
sunLight: { sunLight: {
var allSun: bool = self.chunk.data.paletteLength == 1 and self.chunk.data.palette[0].typ == 0; var allSun: bool = self.chunk.data.paletteLength == 1 and self.chunk.data.palette[0].typ == 0;
var sunStarters: [chunk.chunkSize*chunk.chunkSize][3]u8 = undefined; var sunStarters: [chunk.chunkSize*chunk.chunkSize][3]u8 = undefined;
@ -821,9 +817,9 @@ pub const ChunkMesh = struct {
} }
} }
if(allSun) { if(allSun) {
self.lightingData[1].propagateUniformSun(); self.lightingData[1].propagateUniformSun(lightRefreshList);
} else { } else {
self.lightingData[1].propagateLights(sunStarters[0..index], true); self.lightingData[1].propagateLights(sunStarters[0..index], true, lightRefreshList);
} }
} }
} }
@ -835,7 +831,9 @@ pub const ChunkMesh = struct {
self.mutex.unlock(); self.mutex.unlock();
try mesh_storage.addMeshToStorage(self); try mesh_storage.addMeshToStorage(self);
self.initLight(); var lightRefreshList = main.List(*ChunkMesh).init(main.stackAllocator);
defer lightRefreshList.deinit();
self.initLight(&lightRefreshList);
self.mutex.lock(); self.mutex.lock();
self.finishedLighting = true; self.finishedLighting = true;
@ -858,20 +856,28 @@ pub const ChunkMesh = struct {
const shiftSelf: u5 = @intCast(((dx + 1)*3 + dy + 1)*3 + dz + 1); const shiftSelf: u5 = @intCast(((dx + 1)*3 + dy + 1)*3 + dz + 1);
const shiftOther: u5 = @intCast(((-dx + 1)*3 + -dy + 1)*3 + -dz + 1); const shiftOther: u5 = @intCast(((-dx + 1)*3 + -dy + 1)*3 + -dz + 1);
if(neighborMesh.litNeighbors.fetchOr(@as(u27, 1) << shiftOther, .monotonic) ^ @as(u27, 1) << shiftOther == ~@as(u27, 0)) { // Trigger mesh creation for neighbor if(neighborMesh.litNeighbors.fetchOr(@as(u27, 1) << shiftOther, .monotonic) ^ @as(u27, 1) << shiftOther == ~@as(u27, 0)) { // Trigger mesh creation for neighbor
neighborMesh.generateMesh(); neighborMesh.generateMesh(&lightRefreshList);
} }
neighborMesh.mutex.lock(); neighborMesh.mutex.lock();
const neighborFinishedLighting = neighborMesh.finishedLighting; const neighborFinishedLighting = neighborMesh.finishedLighting;
neighborMesh.mutex.unlock(); neighborMesh.mutex.unlock();
if(neighborFinishedLighting and self.litNeighbors.fetchOr(@as(u27, 1) << shiftSelf, .monotonic) ^ @as(u27, 1) << shiftSelf == ~@as(u27, 0)) { if(neighborFinishedLighting and self.litNeighbors.fetchOr(@as(u27, 1) << shiftSelf, .monotonic) ^ @as(u27, 1) << shiftSelf == ~@as(u27, 0)) {
self.generateMesh(); self.generateMesh(&lightRefreshList);
} }
} }
} }
} }
for(lightRefreshList.items) |other| {
if(other.needsLightRefresh.load(.unordered)) {
other.scheduleLightRefreshAndDecreaseRefCount1();
} else {
other.decreaseRefCount();
}
}
} }
pub fn generateMesh(self: *ChunkMesh) void { pub fn generateMesh(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
var canSeeNeighbor: [6][chunk.chunkSize][chunk.chunkSize]u32 = undefined; var canSeeNeighbor: [6][chunk.chunkSize][chunk.chunkSize]u32 = undefined;
@memset(std.mem.asBytes(&canSeeNeighbor), 0); @memset(std.mem.asBytes(&canSeeNeighbor), 0);
var canSeeAllNeighbors: [chunk.chunkSize][chunk.chunkSize]u32 = undefined; var canSeeAllNeighbors: [chunk.chunkSize][chunk.chunkSize]u32 = undefined;
@ -1108,23 +1114,32 @@ pub const ChunkMesh = struct {
} }
self.mutex.unlock(); self.mutex.unlock();
self.finishNeighbors(); self.finishNeighbors(lightRefreshList);
} }
fn updateBlockLight(self: *ChunkMesh, x: u5, y: u5, z: u5, newBlock: Block) void { fn updateBlockLight(self: *ChunkMesh, x: u5, y: u5, z: u5, newBlock: Block, lightRefreshList: *main.List(*ChunkMesh)) void {
for(self.lightingData[0..]) |lightingData| { for(self.lightingData[0..]) |lightingData| {
lightingData.propagateLightsDestructive(&.{.{x, y, z}}); lightingData.propagateLightsDestructive(&.{.{x, y, z}}, lightRefreshList);
} }
if(newBlock.light() != 0) { if(newBlock.light() != 0) {
self.lightingData[0].propagateLights(&.{.{x, y, z}}, false); self.lightingData[0].propagateLights(&.{.{x, y, z}}, false, lightRefreshList);
} }
} }
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 {
std.log.err("Block: {} {} {}", .{_x, _y, _z});
var lightRefreshList = main.List(*ChunkMesh).init(main.stackAllocator);
defer lightRefreshList.deinit();
const x: u5 = @intCast(_x & chunk.chunkMask); const x: u5 = @intCast(_x & chunk.chunkMask);
const y: u5 = @intCast(_y & chunk.chunkMask); const y: u5 = @intCast(_y & chunk.chunkMask);
const z: u5 = @intCast(_z & chunk.chunkMask); const z: u5 = @intCast(_z & chunk.chunkMask);
var newBlock = _newBlock; var newBlock = _newBlock;
self.mutex.lock();
if(std.meta.eql(self.chunk.data.getValue(chunk.getIndex(x, y, z)), newBlock)) {
self.mutex.unlock();
return;
}
self.mutex.unlock();
var neighborBlocks: [6]Block = undefined; var neighborBlocks: [6]Block = undefined;
@memset(&neighborBlocks, .{.typ = 0, .data = 0}); @memset(&neighborBlocks, .{.typ = 0, .data = 0});
for(chunk.Neighbors.iterable) |neighbor| { for(chunk.Neighbors.iterable) |neighbor| {
@ -1143,8 +1158,8 @@ pub const ChunkMesh = struct {
neighborChunkMesh.opaqueMesh.coreFaces.clearRetainingCapacity(); neighborChunkMesh.opaqueMesh.coreFaces.clearRetainingCapacity();
neighborChunkMesh.transparentMesh.coreFaces.clearRetainingCapacity(); neighborChunkMesh.transparentMesh.coreFaces.clearRetainingCapacity();
neighborChunkMesh.mutex.unlock(); neighborChunkMesh.mutex.unlock();
neighborChunkMesh.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock); neighborChunkMesh.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock, &lightRefreshList);
neighborChunkMesh.generateMesh(); neighborChunkMesh.generateMesh(&lightRefreshList);
neighborChunkMesh.mutex.lock(); neighborChunkMesh.mutex.lock();
} }
} }
@ -1157,7 +1172,7 @@ pub const ChunkMesh = struct {
if(neighborBlock.mode().dependsOnNeighbors) { if(neighborBlock.mode().dependsOnNeighbors) {
if(neighborBlock.mode().updateData(&neighborBlock, neighbor ^ 1, newBlock)) { if(neighborBlock.mode().updateData(&neighborBlock, neighbor ^ 1, newBlock)) {
self.chunk.data.setValue(index, neighborBlock); self.chunk.data.setValue(index, neighborBlock);
self.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock); self.updateBlockLight(@intCast(nx & chunk.chunkMask), @intCast(ny & chunk.chunkMask), @intCast(nz & chunk.chunkMask), neighborBlock, &lightRefreshList);
} }
} }
self.mutex.unlock(); self.mutex.unlock();
@ -1172,7 +1187,7 @@ pub const ChunkMesh = struct {
self.mutex.lock(); self.mutex.lock();
self.chunk.data.setValue(chunk.getIndex(x, y, z), newBlock); self.chunk.data.setValue(chunk.getIndex(x, y, z), newBlock);
self.mutex.unlock(); self.mutex.unlock();
self.updateBlockLight(x, y, z, newBlock); self.updateBlockLight(x, y, z, newBlock, &lightRefreshList);
self.mutex.lock(); self.mutex.lock();
defer self.mutex.unlock(); defer self.mutex.unlock();
// Update neighbor chunks: // Update neighbor chunks:
@ -1200,8 +1215,15 @@ pub const ChunkMesh = struct {
self.opaqueMesh.coreFaces.clearRetainingCapacity(); self.opaqueMesh.coreFaces.clearRetainingCapacity();
self.transparentMesh.coreFaces.clearRetainingCapacity(); self.transparentMesh.coreFaces.clearRetainingCapacity();
self.mutex.unlock(); self.mutex.unlock();
self.generateMesh(); // TODO: Batch mesh updates instead of applying them for each block changes. self.generateMesh(&lightRefreshList); // TODO: Batch mesh updates instead of applying them for each block changes.
self.mutex.lock(); self.mutex.lock();
for(lightRefreshList.items) |other| {
if(other.needsLightRefresh.load(.unordered)) {
other.scheduleLightRefreshAndDecreaseRefCount1();
} else {
other.decreaseRefCount();
}
}
self.uploadData(); self.uploadData();
} }
@ -1241,7 +1263,7 @@ pub const ChunkMesh = struct {
} }
} }
fn finishNeighbors(self: *ChunkMesh) void { fn finishNeighbors(self: *ChunkMesh, lightRefreshList: *main.List(*ChunkMesh)) void {
for(chunk.Neighbors.iterable) |neighbor| { for(chunk.Neighbors.iterable) |neighbor| {
const nullNeighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.pos, self.pos.voxelSize, neighbor); const nullNeighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.pos, self.pos.voxelSize, neighbor);
if(nullNeighborMesh) |neighborMesh| sameLodBlock: { if(nullNeighborMesh) |neighborMesh| sameLodBlock: {
@ -1303,10 +1325,9 @@ pub const ChunkMesh = struct {
} }
} }
} }
_ = neighborMesh.needsLightRefresh.swap(false, .acq_rel); _ = neighborMesh.needsLightRefresh.store(true, .release);
neighborMesh.finishData();
neighborMesh.increaseRefCount(); neighborMesh.increaseRefCount();
mesh_storage.addToUpdateListAndDecreaseRefCount(neighborMesh); lightRefreshList.append(neighborMesh);
} else { } else {
self.mutex.lock(); self.mutex.lock();
defer self.mutex.unlock(); defer self.mutex.unlock();

View File

@ -103,7 +103,7 @@ pub const ChannelChunk = struct {
} }
} }
fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry)) void { fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6; var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6;
defer { defer {
for(&neighborLists) |*list| { for(&neighborLists) |*list| {
@ -146,19 +146,26 @@ pub const ChannelChunk = struct {
} }
self.data.optimizeLayout(); self.data.optimizeLayout();
self.lock.unlockWrite(); self.lock.unlockWrite();
if(mesh_storage.getMeshAndIncreaseRefCount(self.ch.pos)) |mesh| { if(mesh_storage.getMeshAndIncreaseRefCount(self.ch.pos)) |mesh| outer: {
mesh.scheduleLightRefreshAndDecreaseRefCount(); for(lightRefreshList.items) |other| {
if(mesh == other) {
mesh.decreaseRefCount();
break :outer;
}
}
mesh.needsLightRefresh.store(true, .release);
lightRefreshList.append(mesh);
} }
for(0..6) |neighbor| { for(0..6) |neighbor| {
if(neighborLists[neighbor].items.len == 0) continue; if(neighborLists[neighbor].items.len == 0) continue;
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, @intCast(neighbor)) orelse continue; const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, @intCast(neighbor)) orelse continue;
defer neighborMesh.decreaseRefCount(); defer neighborMesh.decreaseRefCount();
neighborMesh.lightingData[@intFromBool(self.isSun)].propagateFromNeighbor(lightQueue, neighborLists[neighbor].items); neighborMesh.lightingData[@intFromBool(self.isSun)].propagateFromNeighbor(lightQueue, neighborLists[neighbor].items, lightRefreshList);
} }
} }
fn propagateDestructive(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), constructiveEntries: *main.ListUnmanaged(ChunkEntries), isFirstBlock: bool) main.ListUnmanaged(PositionEntry) { fn propagateDestructive(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), constructiveEntries: *main.ListUnmanaged(ChunkEntries), isFirstBlock: bool, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) main.ListUnmanaged(PositionEntry) {
var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6; var neighborLists: [6]main.ListUnmanaged(Entry) = .{.{}} ** 6;
var constructiveList: main.ListUnmanaged(PositionEntry) = .{}; var constructiveList: main.ListUnmanaged(PositionEntry) = .{};
defer { defer {
@ -224,8 +231,15 @@ pub const ChannelChunk = struct {
} }
} }
self.lock.unlockWrite(); self.lock.unlockWrite();
if(mesh_storage.getMeshAndIncreaseRefCount(self.ch.pos)) |mesh| { if(mesh_storage.getMeshAndIncreaseRefCount(self.ch.pos)) |mesh| outer: {
mesh.scheduleLightRefreshAndDecreaseRefCount(); for(lightRefreshList.items) |other| {
if(mesh == other) {
mesh.decreaseRefCount();
break :outer;
}
}
mesh.needsLightRefresh.store(true, .release);
lightRefreshList.append(mesh);
} }
for(0..6) |neighbor| { for(0..6) |neighbor| {
@ -233,14 +247,14 @@ pub const ChannelChunk = struct {
const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, @intCast(neighbor)) orelse continue; const neighborMesh = mesh_storage.getNeighborAndIncreaseRefCount(self.ch.pos, self.ch.pos.voxelSize, @intCast(neighbor)) orelse continue;
constructiveEntries.append(main.stackAllocator, .{ constructiveEntries.append(main.stackAllocator, .{
.mesh = neighborMesh, .mesh = neighborMesh,
.entries = neighborMesh.lightingData[@intFromBool(self.isSun)].propagateDestructiveFromNeighbor(lightQueue, neighborLists[neighbor].items, constructiveEntries), .entries = neighborMesh.lightingData[@intFromBool(self.isSun)].propagateDestructiveFromNeighbor(lightQueue, neighborLists[neighbor].items, constructiveEntries, lightRefreshList),
}); });
} }
return constructiveList; return constructiveList;
} }
fn propagateFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry) void { fn propagateFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
std.debug.assert(lightQueue.startIndex == lightQueue.endIndex); std.debug.assert(lightQueue.startIndex == lightQueue.endIndex);
for(lights) |entry| { for(lights) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z); const index = chunk.getIndex(entry.x, entry.y, entry.z);
@ -248,10 +262,10 @@ pub const ChannelChunk = struct {
calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, entry.sourceDir); calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, entry.sourceDir);
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result); if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result);
} }
self.propagateDirect(lightQueue); self.propagateDirect(lightQueue, lightRefreshList);
} }
fn propagateDestructiveFromNeighbor(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry), lights: []const Entry, constructiveEntries: *main.ListUnmanaged(ChunkEntries)) main.ListUnmanaged(PositionEntry) { 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) {
std.debug.assert(lightQueue.startIndex == lightQueue.endIndex); std.debug.assert(lightQueue.startIndex == lightQueue.endIndex);
for(lights) |entry| { for(lights) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z); const index = chunk.getIndex(entry.x, entry.y, entry.z);
@ -259,10 +273,10 @@ pub const ChannelChunk = struct {
calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, entry.sourceDir); calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, entry.sourceDir);
lightQueue.enqueue(result); lightQueue.enqueue(result);
} }
return self.propagateDestructive(lightQueue, constructiveEntries, false); return self.propagateDestructive(lightQueue, constructiveEntries, false, lightRefreshList);
} }
pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool) void { pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12); var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
defer lightQueue.deinit(); defer lightQueue.deinit();
for(lights) |pos| { for(lights) |pos| {
@ -320,10 +334,10 @@ pub const ChannelChunk = struct {
} }
} }
} }
self.propagateDirect(&lightQueue); self.propagateDirect(&lightQueue, lightRefreshList);
} }
pub fn propagateUniformSun(self: *ChannelChunk) void { pub fn propagateUniformSun(self: *ChannelChunk, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
std.debug.assert(self.isSun); std.debug.assert(self.isSun);
self.lock.lockWrite(); self.lock.lockWrite();
if(self.data.paletteLength != 1) { if(self.data.paletteLength != 1) {
@ -367,11 +381,11 @@ pub const ChannelChunk = struct {
entry.sourceDir = neighbor ^ 1; entry.sourceDir = neighbor ^ 1;
} }
} }
neighborMesh.lightingData[1].propagateFromNeighbor(&lightQueue, &list); neighborMesh.lightingData[1].propagateFromNeighbor(&lightQueue, &list, lightRefreshList);
} }
} }
pub fn propagateLightsDestructive(self: *ChannelChunk, lights: []const [3]u8) void { pub fn propagateLightsDestructive(self: *ChannelChunk, lights: []const [3]u8, lightRefreshList: *main.List(*chunk_meshing.ChunkMesh)) void {
var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12); var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
defer lightQueue.deinit(); defer lightQueue.deinit();
self.lock.lockRead(); self.lock.lockRead();
@ -384,7 +398,7 @@ pub const ChannelChunk = struct {
defer constructiveEntries.deinit(main.stackAllocator); defer constructiveEntries.deinit(main.stackAllocator);
constructiveEntries.append(main.stackAllocator, .{ constructiveEntries.append(main.stackAllocator, .{
.mesh = null, .mesh = null,
.entries = self.propagateDestructive(&lightQueue, &constructiveEntries, true), .entries = self.propagateDestructive(&lightQueue, &constructiveEntries, true, lightRefreshList),
}); });
for(constructiveEntries.items) |entries| { for(constructiveEntries.items) |entries| {
const mesh = entries.mesh; const mesh = entries.mesh;
@ -401,7 +415,7 @@ pub const ChannelChunk = struct {
lightQueue.enqueue(.{.x = entry.x, .y = entry.y, .z = entry.z, .value = value, .sourceDir = 6, .activeValue = 0b111}); lightQueue.enqueue(.{.x = entry.x, .y = entry.y, .z = entry.z, .value = value, .sourceDir = 6, .activeValue = 0b111});
} }
channelChunk.lock.unlockWrite(); channelChunk.lock.unlockWrite();
channelChunk.propagateDirect(&lightQueue); channelChunk.propagateDirect(&lightQueue, lightRefreshList);
} }
} }
}; };