mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-04 03:37:59 -04:00
Use a CircularBufferQueue for lighting instead of a PriorityQueue.
A CircularBufferQueue means that we might revisit some points, for example when there is a dim and a bright light source. However in practice the performance impact of using a PriorityQueue is huge, so this edge case doesn't matter really. This makes light propagation about 20 times faster and should help with #223, but it doesn't seem to actually make a difference. There is likely something else going on here.
This commit is contained in:
parent
048b48e5c7
commit
451cac2923
@ -40,15 +40,9 @@ pub const ChannelChunk = struct {
|
|||||||
y: u5,
|
y: u5,
|
||||||
z: u5,
|
z: u5,
|
||||||
value: u8,
|
value: u8,
|
||||||
|
|
||||||
fn compare(_: void, a: @This(), b: @This()) std.math.Order {
|
|
||||||
if (a.value > b.value) return .gt;
|
|
||||||
if (a.value < b.value) return .lt;
|
|
||||||
return .eq;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn propagateDirect(self: *ChannelChunk, lightQueue: *std.PriorityQueue(Entry, void, Entry.compare)) std.mem.Allocator.Error!void {
|
noinline fn propagateDirect(self: *ChannelChunk, lightQueue: *main.utils.CircularBufferQueue(Entry)) std.mem.Allocator.Error!void {
|
||||||
var neighborLists: [6]std.ArrayListUnmanaged(Entry) = .{.{}} ** 6;
|
var neighborLists: [6]std.ArrayListUnmanaged(Entry) = .{.{}} ** 6;
|
||||||
defer {
|
defer {
|
||||||
for(&neighborLists) |*list| {
|
for(&neighborLists) |*list| {
|
||||||
@ -58,7 +52,7 @@ pub const ChannelChunk = struct {
|
|||||||
|
|
||||||
self.mutex.lock();
|
self.mutex.lock();
|
||||||
errdefer self.mutex.unlock();
|
errdefer self.mutex.unlock();
|
||||||
while(lightQueue.removeOrNull()) |entry| {
|
while(lightQueue.dequeue()) |entry| {
|
||||||
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
const index = chunk.getIndex(entry.x, entry.y, entry.z);
|
||||||
if(entry.value <= self.data[index].load(.Unordered)) continue;
|
if(entry.value <= self.data[index].load(.Unordered)) continue;
|
||||||
self.data[index].store(entry.value, .Unordered);
|
self.data[index].store(entry.value, .Unordered);
|
||||||
@ -77,7 +71,7 @@ pub const ChannelChunk = struct {
|
|||||||
var absorption: u8 = @intCast(self.ch.blocks[neighborIndex].absorption() >> self.channel.shift() & 255);
|
var absorption: u8 = @intCast(self.ch.blocks[neighborIndex].absorption() >> self.channel.shift() & 255);
|
||||||
absorption *|= @intCast(self.ch.pos.voxelSize);
|
absorption *|= @intCast(self.ch.pos.voxelSize);
|
||||||
result.value -|= absorption;
|
result.value -|= absorption;
|
||||||
if(result.value != 0) try lightQueue.add(result);
|
if(result.value != 0) try lightQueue.enqueue(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.mutex.unlock();
|
self.mutex.unlock();
|
||||||
@ -97,7 +91,7 @@ pub const ChannelChunk = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn propagateFromNeighbor(self: *ChannelChunk, lights: []const Entry) std.mem.Allocator.Error!void {
|
fn propagateFromNeighbor(self: *ChannelChunk, lights: []const Entry) std.mem.Allocator.Error!void {
|
||||||
var lightQueue = std.PriorityQueue(Entry, void, Entry.compare).init(main.globalAllocator, {});
|
var lightQueue = try main.utils.CircularBufferQueue(Entry).init(main.globalAllocator, 1 << 8);
|
||||||
defer lightQueue.deinit();
|
defer lightQueue.deinit();
|
||||||
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);
|
||||||
@ -105,17 +99,17 @@ pub const ChannelChunk = struct {
|
|||||||
var absorption: u8 = @intCast(self.ch.blocks[index].absorption() >> self.channel.shift() & 255);
|
var absorption: u8 = @intCast(self.ch.blocks[index].absorption() >> self.channel.shift() & 255);
|
||||||
absorption *|= @intCast(self.ch.pos.voxelSize);
|
absorption *|= @intCast(self.ch.pos.voxelSize);
|
||||||
result.value -|= absorption;
|
result.value -|= absorption;
|
||||||
if(result.value != 0) try lightQueue.add(result);
|
if(result.value != 0) try lightQueue.enqueue(result);
|
||||||
}
|
}
|
||||||
try self.propagateDirect(&lightQueue);
|
try self.propagateDirect(&lightQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool) std.mem.Allocator.Error!void {
|
pub fn propagateLights(self: *ChannelChunk, lights: []const [3]u8, comptime checkNeighbors: bool) std.mem.Allocator.Error!void {
|
||||||
var lightQueue = std.PriorityQueue(Entry, void, Entry.compare).init(main.globalAllocator, {});
|
var lightQueue = try main.utils.CircularBufferQueue(Entry).init(main.globalAllocator, 1 << 8);
|
||||||
defer lightQueue.deinit();
|
defer lightQueue.deinit();
|
||||||
for(lights) |pos| {
|
for(lights) |pos| {
|
||||||
const index = chunk.getIndex(pos[0], pos[1], pos[2]);
|
const index = chunk.getIndex(pos[0], pos[1], pos[2]);
|
||||||
try lightQueue.add(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = @intCast(self.ch.blocks[index].light() >> self.channel.shift() & 255)});
|
try lightQueue.enqueue(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = @intCast(self.ch.blocks[index].light() >> self.channel.shift() & 255)});
|
||||||
}
|
}
|
||||||
if(checkNeighbors) {
|
if(checkNeighbors) {
|
||||||
for(0..6) |neighbor| {
|
for(0..6) |neighbor| {
|
||||||
@ -154,7 +148,7 @@ pub const ChannelChunk = struct {
|
|||||||
var absorption: u8 = @intCast(self.ch.blocks[index].absorption() >> self.channel.shift() & 255);
|
var absorption: u8 = @intCast(self.ch.blocks[index].absorption() >> self.channel.shift() & 255);
|
||||||
absorption *|= @intCast(self.ch.pos.voxelSize);
|
absorption *|= @intCast(self.ch.pos.voxelSize);
|
||||||
value -|= absorption;
|
value -|= absorption;
|
||||||
if(value != 0) try lightQueue.add(.{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .value = value});
|
if(value != 0) try lightQueue.enqueue(.{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .value = value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,6 +317,59 @@ pub fn Array3D(comptime T: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn CircularBufferQueue(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
mem: []T,
|
||||||
|
mask: usize,
|
||||||
|
startIndex: usize,
|
||||||
|
endIndex: usize,
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, initialCapacity: usize) !Self {
|
||||||
|
comptime std.debug.assert(@sizeOf(Self) <= 64);
|
||||||
|
std.debug.assert(initialCapacity-1 & initialCapacity == 0 and initialCapacity > 0);
|
||||||
|
return .{
|
||||||
|
.mem = try allocator.alloc(T, initialCapacity),
|
||||||
|
.mask = initialCapacity-1,
|
||||||
|
.startIndex = 0,
|
||||||
|
.endIndex = 0,
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Self) void {
|
||||||
|
self.allocator.free(self.mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn increaseCapacity(self: *Self) !void {
|
||||||
|
const newMem = try self.allocator.alloc(T, self.mem.len*2);
|
||||||
|
@memcpy(newMem[0..(self.mem.len - self.startIndex)], self.mem[self.startIndex..]);
|
||||||
|
@memcpy(newMem[(self.mem.len - self.startIndex)..][0..self.endIndex], self.mem[0..self.endIndex]);
|
||||||
|
self.startIndex = 0;
|
||||||
|
self.endIndex = self.mem.len;
|
||||||
|
self.allocator.free(self.mem);
|
||||||
|
self.mem = newMem;
|
||||||
|
self.mask = self.mem.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enqueue(self: *Self, elem: T) !void {
|
||||||
|
self.mem[self.endIndex] = elem;
|
||||||
|
self.endIndex = (self.endIndex + 1) & self.mask;
|
||||||
|
if(self.endIndex == self.startIndex) {
|
||||||
|
try self.increaseCapacity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dequeue(self: *Self) ?T {
|
||||||
|
if(self.startIndex == self.endIndex) return null;
|
||||||
|
const result = self.mem[self.startIndex];
|
||||||
|
self.startIndex = (self.startIndex + 1) & self.mask;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Allows for stack-like allocations in a fast and safe way.
|
/// Allows for stack-like allocations in a fast and safe way.
|
||||||
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
|
/// It is safe in the sense that a regular allocator will be used when the buffer is full.
|
||||||
pub const StackAllocator = struct {
|
pub const StackAllocator = struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user