Atomicize the palette

This commit is contained in:
IntegratedQuantum 2025-07-28 20:44:26 +02:00
parent 714b0a91d7
commit a85a3ec2d2
10 changed files with 120 additions and 89 deletions

View File

@ -351,7 +351,7 @@ pub const BlockEntityTypes = struct {
}
data.valuePtr.* = .{
.blockPos = pos,
.block = chunk.data.getValue(chunk.getLocalBlockIndex(pos)),
.block = chunk.data.getValueFromOwnerThread(chunk.getLocalBlockIndex(pos)),
.renderedTexture = null,
.text = main.globalAllocator.dupe(u8, event.createOrUpdate.remaining),
};
@ -405,7 +405,7 @@ pub const BlockEntityTypes = struct {
mesh.mutex.lock();
defer mesh.mutex.unlock();
const index = mesh.chunk.getLocalBlockIndex(pos);
const block = mesh.chunk.data.getValue(index);
const block = mesh.chunk.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse return;
if(!std.mem.eql(u8, blockEntity.id, id)) return;
@ -418,7 +418,7 @@ pub const BlockEntityTypes = struct {
}
data.valuePtr.* = .{
.blockPos = pos,
.block = mesh.chunk.data.getValue(mesh.chunk.getLocalBlockIndex(pos)),
.block = mesh.chunk.data.getValueFromOwnerThread(mesh.chunk.getLocalBlockIndex(pos)),
.renderedTexture = null,
.text = main.globalAllocator.dupe(u8, newText),
};

View File

@ -296,7 +296,7 @@ pub const Chunk = struct { // MARK: Chunk
while(iterator.next()) |elem| {
const index = elem.key_ptr.*;
const entityDataIndex = elem.value_ptr.*;
const block = self.data.getValue(index);
const block = self.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse unreachable;
switch(side) {
.client => {
@ -327,7 +327,7 @@ pub const Chunk = struct { // MARK: Chunk
const y = _y >> self.voxelSizeShift;
const z = _z >> self.voxelSizeShift;
const index = getIndex(x, y, z);
return self.data.getValue(index);
return self.data.getValueFromOwnerThread(index);
}
/// Checks if the given relative coordinates lie within the bounds of this chunk.
@ -437,7 +437,7 @@ pub const ServerChunk = struct { // MARK: ServerChunk
const y = _y >> self.super.voxelSizeShift;
const z = _z >> self.super.voxelSizeShift;
const index = getIndex(x, y, z);
return self.super.data.getValue(index);
return self.super.data.getValueFromOwnerThread(index);
}
/// Updates a block if it is inside this chunk.
@ -487,7 +487,7 @@ pub const ServerChunk = struct { // MARK: ServerChunk
const y = _y >> self.super.voxelSizeShift;
const z = _z >> self.super.voxelSizeShift;
const index = getIndex(x, y, z);
const oldBlock = self.super.data.getValue(index);
const oldBlock = self.super.data.getValueFromOwnerThread(index);
if(oldBlock.typ == 0 or oldBlock.degradable()) {
self.super.data.setValue(index, newBlock);
}
@ -544,7 +544,7 @@ pub const ServerChunk = struct { // MARK: ServerChunk
while(dz <= 1) : (dz += 1) {
const index = getIndex(x*2 + dx, y*2 + dy, z*2 + dz);
const i = dx*4 + dz*2 + dy;
octantBlocks[i] = other.super.data.getValue(index);
octantBlocks[i] = other.super.data.getValueFromOwnerThread(index);
octantBlocks[i].typ = octantBlocks[i].lodReplacement();
if(octantBlocks[i].typ == 0) {
neighborCount[i] = 0;
@ -558,7 +558,7 @@ pub const ServerChunk = struct { // MARK: ServerChunk
const nz = z*2 + dz + n.relZ();
if((nx & chunkMask) == nx and (ny & chunkMask) == ny and (nz & chunkMask) == nz) { // If it's inside the chunk.
const neighborIndex = getIndex(nx, ny, nz);
if(other.super.data.getValue(neighborIndex).transparent()) {
if(other.super.data.getValueFromOwnerThread(neighborIndex).transparent()) {
count += 5;
}
} else {

View File

@ -1312,7 +1312,7 @@ pub const Protocols = struct {
mesh.mutex.lock();
defer mesh.mutex.unlock();
const index = mesh.chunk.getLocalBlockIndex(pos);
const block = mesh.chunk.data.getValue(index);
const block = mesh.chunk.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse return;
var writer = utils.BinaryWriter.init(main.stackAllocator);

View File

@ -799,7 +799,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
while(y < chunk.chunkSize) : (y += 1) {
var z: u8 = 0;
while(z < chunk.chunkSize) : (z += 1) {
const block = self.chunk.data.getValue(chunk.getIndex(x, y, z));
const block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(x, y, z));
if(block.light() != 0) lightEmittingBlocks.append(.{x, y, z});
}
}
@ -807,7 +807,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
self.mutex.unlock();
self.lightingData[0].propagateLights(lightEmittingBlocks.items, true, lightRefreshList);
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.raw.toSlice()[0].raw.typ == 0;
var sunStarters: [chunk.chunkSize*chunk.chunkSize][3]u8 = undefined;
var index: usize = 0;
const lightStartMap = mesh_storage.getLightMapPiece(self.pos.wx, self.pos.wy, self.pos.voxelSize) orelse break :sunLight;
@ -918,7 +918,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
var paletteCache = main.stackAllocator.alloc(OcclusionInfo, self.chunk.data.paletteLength);
defer main.stackAllocator.free(paletteCache);
for(0..self.chunk.data.paletteLength) |i| {
const block = self.chunk.data.palette[i];
const block = self.chunk.data.palette.raw.toSlice()[i].raw;
const model = blocks.meshes.model(block).model();
var result: OcclusionInfo = .{};
if(model.noNeighborsOccluded or block.viewThrough()) {
@ -1002,7 +1002,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
hasFaces[x][y] |= setBit;
}
if(occlusionInfo.hasInternalQuads) {
const block = self.chunk.data.palette[paletteId];
const block = self.chunk.data.palette.raw.toSlice()[paletteId].raw;
if(block.transparent()) {
appendInternalQuads(block, x, y, z, false, &transparentCore, main.stackAllocator);
} else {
@ -1022,10 +1022,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z = @ctz(bitMask);
const setBit = @as(u32, 1) << @intCast(z);
bitMask &= ~setBit;
var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z));
if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant();
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x - 1), @intCast(y), z));
const neighborBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x - 1), @intCast(y), z));
if(block == neighborBlock) continue;
}
if(block.transparent()) {
@ -1049,10 +1049,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z = @ctz(bitMask);
const setBit = @as(u32, 1) << @intCast(z);
bitMask &= ~setBit;
var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z));
if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant();
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x + 1), @intCast(y), z));
const neighborBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x + 1), @intCast(y), z));
if(block == neighborBlock) continue;
}
if(block.transparent()) {
@ -1076,10 +1076,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z = @ctz(bitMask);
const setBit = @as(u32, 1) << @intCast(z);
bitMask &= ~setBit;
var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z));
if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant();
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y - 1), z));
const neighborBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y - 1), z));
if(block == neighborBlock) continue;
}
if(block.transparent()) {
@ -1103,10 +1103,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z = @ctz(bitMask);
const setBit = @as(u32, 1) << @intCast(z);
bitMask &= ~setBit;
var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z));
if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant();
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y + 1), z));
const neighborBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y + 1), z));
if(block == neighborBlock) continue;
}
if(block.transparent()) {
@ -1130,10 +1130,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z = @ctz(bitMask);
const setBit = @as(u32, 1) << @intCast(z);
bitMask &= ~setBit;
var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z));
if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant();
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z - 1));
const neighborBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z - 1));
if(block == neighborBlock) continue;
}
if(block.transparent()) {
@ -1157,10 +1157,10 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z = @ctz(bitMask);
const setBit = @as(u32, 1) << @intCast(z);
bitMask &= ~setBit;
var block = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z));
if(depthFilteredViewThroughMask[x][y] & setBit != 0) block.typ = block.opaqueVariant();
if(block.viewThrough() and !block.alwaysViewThrough()) { // Needs to check the neighbor block
const neighborBlock = self.chunk.data.getValue(chunk.getIndex(@intCast(x), @intCast(y), z + 1));
const neighborBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(@intCast(x), @intCast(y), z + 1));
if(block == neighborBlock) continue;
}
if(block.transparent()) {
@ -1202,7 +1202,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const z: u5 = @intCast(_z & chunk.chunkMask);
var newBlock = _newBlock;
self.mutex.lock();
const oldBlock = self.chunk.data.getValue(chunk.getIndex(x, y, z));
const oldBlock = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(x, y, z));
if(oldBlock == newBlock) {
if(newBlock.blockEntity()) |blockEntity| {
@ -1240,7 +1240,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const index = chunk.getIndex(nnx, nny, nnz);
neighborChunkMesh.mutex.lock();
var neighborBlock = neighborChunkMesh.chunk.data.getValue(index);
var neighborBlock = neighborChunkMesh.chunk.data.getValueFromOwnerThread(index);
if(neighborBlock.mode().dependsOnNeighbors and neighborBlock.mode().updateData(&neighborBlock, neighbor.reverse(), newBlock)) {
neighborChunkMesh.chunk.data.setValue(index, neighborBlock);
@ -1254,7 +1254,7 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
} else {
const index = chunk.getIndex(nx, ny, nz);
self.mutex.lock();
var neighborBlock = self.chunk.data.getValue(index);
var neighborBlock = self.chunk.data.getValueFromOwnerThread(index);
if(neighborBlock.mode().dependsOnNeighbors and neighborBlock.mode().updateData(&neighborBlock, neighbor.reverse(), newBlock)) {
self.chunk.data.setValue(index, neighborBlock);
self.updateBlockLight(@intCast(nx), @intCast(ny), @intCast(nz), neighborBlock, lightRefreshList);
@ -1409,9 +1409,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const otherX = x +% neighbor.relX() & chunk.chunkMask;
const otherY = y +% neighbor.relY() & chunk.chunkMask;
const otherZ = z +% neighbor.relZ() & chunk.chunkMask;
var block = self.chunk.data.getValue(chunk.getIndex(x, y, z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(x, y, z));
if(settings.leavesQuality == 0) block.typ = block.opaqueVariant();
var otherBlock = neighborMesh.chunk.data.getValue(chunk.getIndex(otherX, otherY, otherZ));
var otherBlock = neighborMesh.chunk.data.getValueFromOwnerThread(chunk.getIndex(otherX, otherY, otherZ));
if(settings.leavesQuality == 0) otherBlock.typ = otherBlock.opaqueVariant();
if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) {
if(block.transparent()) {
@ -1501,9 +1501,9 @@ pub const ChunkMesh = struct { // MARK: ChunkMesh
const otherX = (x +% neighbor.relX() +% offsetX >> 1) & chunk.chunkMask;
const otherY = (y +% neighbor.relY() +% offsetY >> 1) & chunk.chunkMask;
const otherZ = (z +% neighbor.relZ() +% offsetZ >> 1) & chunk.chunkMask;
var block = self.chunk.data.getValue(chunk.getIndex(x, y, z));
var block = self.chunk.data.getValueFromOwnerThread(chunk.getIndex(x, y, z));
if(settings.leavesQuality == 0) block.typ = block.opaqueVariant();
var otherBlock = neighborMesh.chunk.data.getValue(chunk.getIndex(otherX, otherY, otherZ));
var otherBlock = neighborMesh.chunk.data.getValueFromOwnerThread(chunk.getIndex(otherX, otherY, otherZ));
if(settings.leavesQuality == 0) otherBlock.typ = otherBlock.opaqueVariant();
if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor.reverse())) {
if(otherBlock.transparent()) {

View File

@ -17,6 +17,21 @@ pub fn deinit() void {
memoryPool.deinit();
}
const LightValue = packed struct(u32) {
r: u8,
g: u8,
b: u8,
pad: u8 = undefined,
fn fromArray(arr: [3]u8) LightValue {
return .{.r = arr[0], .g = arr[1], .b = arr[2]};
}
fn toArray(self: LightValue) [3]u8 {
return .{self.r, self.g, self.b};
}
};
fn extractColor(in: u32) [3]u8 {
return .{
@truncate(in >> 16),
@ -26,7 +41,7 @@ fn extractColor(in: u32) [3]u8 {
}
pub const ChannelChunk = struct {
data: main.utils.PaletteCompressedRegion([3]u8, chunk.chunkVolume),
data: main.utils.PaletteCompressedRegion(LightValue, chunk.chunkVolume),
lock: main.utils.ReadWriteLock,
ch: *chunk.Chunk,
isSun: bool,
@ -66,9 +81,8 @@ pub const ChannelChunk = struct {
};
pub fn getValue(self: *ChannelChunk, x: i32, y: i32, z: i32) [3]u8 {
self.lock.assertLockedRead();
const index = chunk.getIndex(x, y, z);
return self.data.getValue(index);
return self.data.getValueUnordered(index).toArray();
}
fn calculateIncomingOcclusion(result: *[3]u8, block: blocks.Block, voxelSize: u31, neighbor: chunk.Neighbor) void {
@ -109,14 +123,14 @@ pub const ChannelChunk = struct {
self.lock.lockWrite();
while(lightQueue.popFront()) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z);
const oldValue: [3]u8 = self.data.getValue(index);
const oldValue: [3]u8 = self.data.getValueFromOwnerThread(index).toArray();
const newValue: [3]u8 = .{
@max(entry.value[0], oldValue[0]),
@max(entry.value[1], oldValue[1]),
@max(entry.value[2], oldValue[2]),
};
if(newValue[0] == oldValue[0] and newValue[1] == oldValue[1] and newValue[2] == oldValue[2]) continue;
self.data.setValue(index, newValue);
self.data.setValue(index, .fromArray(newValue));
for(chunk.Neighbor.iterable) |neighbor| {
if(neighbor.toInt() == entry.sourceDir) continue;
const nx = entry.x + neighbor.relX();
@ -128,14 +142,14 @@ pub const ChannelChunk = struct {
result.value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
result.value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
}
calculateOutgoingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, neighbor);
calculateOutgoingOcclusion(&result.value, self.ch.data.getValueFromOwnerThread(index), self.ch.pos.voxelSize, neighbor);
if(result.value[0] == 0 and result.value[1] == 0 and result.value[2] == 0) continue;
if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) {
neighborLists[neighbor.toInt()].append(main.stackAllocator, result);
continue;
}
const neighborIndex = chunk.getIndex(nx, ny, nz);
calculateIncomingOcclusion(&result.value, self.ch.data.getValue(neighborIndex), self.ch.pos.voxelSize, neighbor.reverse());
calculateIncomingOcclusion(&result.value, self.ch.data.getValueFromOwnerThread(neighborIndex), self.ch.pos.voxelSize, neighbor.reverse());
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.pushBack(result);
}
}
@ -175,7 +189,7 @@ pub const ChannelChunk = struct {
self.lock.lockWrite();
while(lightQueue.popFront()) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z);
const oldValue: [3]u8 = self.data.getValue(index);
const oldValue: [3]u8 = self.data.getValueFromOwnerThread(index).toArray();
var activeValue: @Vector(3, bool) = @bitCast(entry.activeValue);
var append: bool = false;
if(activeValue[0] and entry.value[0] != oldValue[0]) {
@ -209,7 +223,7 @@ pub const ChannelChunk = struct {
if(activeValue[0]) insertValue[0] = 0;
if(activeValue[1]) insertValue[1] = 0;
if(activeValue[2]) insertValue[2] = 0;
self.data.setValue(index, insertValue);
self.data.setValue(index, .fromArray(insertValue));
for(chunk.Neighbor.iterable) |neighbor| {
if(neighbor.toInt() == entry.sourceDir) continue;
const nx = entry.x + neighbor.relX();
@ -221,13 +235,13 @@ pub const ChannelChunk = struct {
result.value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
result.value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
}
calculateOutgoingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, neighbor);
calculateOutgoingOcclusion(&result.value, self.ch.data.getValueFromOwnerThread(index), self.ch.pos.voxelSize, neighbor);
if(nx < 0 or nx >= chunk.chunkSize or ny < 0 or ny >= chunk.chunkSize or nz < 0 or nz >= chunk.chunkSize) {
neighborLists[neighbor.toInt()].append(main.stackAllocator, result);
continue;
}
const neighborIndex = chunk.getIndex(nx, ny, nz);
calculateIncomingOcclusion(&result.value, self.ch.data.getValue(neighborIndex), self.ch.pos.voxelSize, neighbor.reverse());
calculateIncomingOcclusion(&result.value, self.ch.data.getValueFromOwnerThread(neighborIndex), self.ch.pos.voxelSize, neighbor.reverse());
lightQueue.pushBack(result);
}
}
@ -251,7 +265,7 @@ pub const ChannelChunk = struct {
for(lights) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z);
var result = entry;
calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, @enumFromInt(entry.sourceDir));
calculateIncomingOcclusion(&result.value, self.ch.data.getValueFromOwnerThread(index), self.ch.pos.voxelSize, @enumFromInt(entry.sourceDir));
if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.pushBack(result);
}
self.propagateDirect(lightQueue, lightRefreshList);
@ -262,7 +276,7 @@ pub const ChannelChunk = struct {
for(lights) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z);
var result = entry;
calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, @enumFromInt(entry.sourceDir));
calculateIncomingOcclusion(&result.value, self.ch.data.getValueFromOwnerThread(index), self.ch.pos.voxelSize, @enumFromInt(entry.sourceDir));
lightQueue.pushBack(result);
}
return self.propagateDestructive(lightQueue, constructiveEntries, false, lightRefreshList);
@ -276,7 +290,7 @@ pub const ChannelChunk = struct {
if(self.isSun) {
lightQueue.pushBack(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = .{255, 255, 255}, .sourceDir = 6, .activeValue = 0b111});
} else {
lightQueue.pushBack(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = extractColor(self.ch.data.getValue(index).light()), .sourceDir = 6, .activeValue = 0b111});
lightQueue.pushBack(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = extractColor(self.ch.data.getValueFromOwnerThread(index).light()), .sourceDir = 6, .activeValue = 0b111});
}
}
if(checkNeighbors) {
@ -311,15 +325,15 @@ pub const ChannelChunk = struct {
defer neighborLightChunk.lock.unlockRead();
const index = chunk.getIndex(x, y, z);
const neighborIndex = chunk.getIndex(otherX, otherY, otherZ);
var value: [3]u8 = neighborLightChunk.data.getValue(neighborIndex);
var value: [3]u8 = neighborLightChunk.data.getValueFromOwnerThread(neighborIndex).toArray();
if(!self.isSun or neighbor != .dirUp or value[0] != 255 or value[1] != 255 or value[2] != 255) {
value[0] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
}
calculateOutgoingOcclusion(&value, self.ch.data.getValue(neighborIndex), self.ch.pos.voxelSize, neighbor);
calculateOutgoingOcclusion(&value, self.ch.data.getValueFromOwnerThread(neighborIndex), self.ch.pos.voxelSize, neighbor);
if(value[0] == 0 and value[1] == 0 and value[2] == 0) continue;
calculateIncomingOcclusion(&value, self.ch.data.getValue(index), self.ch.pos.voxelSize, neighbor.reverse());
calculateIncomingOcclusion(&value, self.ch.data.getValueFromOwnerThread(index), self.ch.pos.voxelSize, neighbor.reverse());
if(value[0] != 0 or value[1] != 0 or value[2] != 0) lightQueue.pushBack(.{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .value = value, .sourceDir = neighbor.toInt(), .activeValue = 0b111});
}
}
@ -335,7 +349,7 @@ pub const ChannelChunk = struct {
self.data.deinit();
self.data.init();
}
self.data.palette[0] = .{255, 255, 255};
self.data.palette.raw.toSlice()[0].store(.fromArray(@splat(255)), .unordered);
self.lock.unlockWrite();
const val = 255 -| 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
@ -381,7 +395,7 @@ pub const ChannelChunk = struct {
self.lock.lockRead();
for(lights) |pos| {
const index = chunk.getIndex(pos[0], pos[1], pos[2]);
lightQueue.pushBack(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = self.data.getValue(index), .sourceDir = 6, .activeValue = 0b111});
lightQueue.pushBack(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = self.data.getValueFromOwnerThread(index).toArray(), .sourceDir = 6, .activeValue = 0b111});
}
self.lock.unlockRead();
var constructiveEntries: main.ListUnmanaged(ChunkEntries) = .{};
@ -398,15 +412,15 @@ pub const ChannelChunk = struct {
channelChunk.lock.lockWrite();
for(entryList.items) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z);
var value = channelChunk.data.getValue(index);
const light = if(self.isSun) .{0, 0, 0} else extractColor(channelChunk.ch.data.getValue(index).light());
var value = channelChunk.data.getValueFromOwnerThread(index).toArray();
const light = if(self.isSun) .{0, 0, 0} else extractColor(channelChunk.ch.data.getValueFromOwnerThread(index).light());
value = .{
@max(value[0], light[0]),
@max(value[1], light[1]),
@max(value[2], light[2]),
};
if(value[0] == 0 and value[1] == 0 and value[2] == 0) continue;
channelChunk.data.setValue(index, .{0, 0, 0});
channelChunk.data.setValue(index, .fromArray(.{0, 0, 0}));
lightQueue.pushBack(.{.x = entry.x, .y = entry.y, .z = entry.z, .value = value, .sourceDir = 6, .activeValue = 0b111});
}
channelChunk.lock.unlockWrite();

View File

@ -284,7 +284,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
fn compressBlockData(ch: *chunk.Chunk, allowLossy: bool, writer: *BinaryWriter) void {
if(ch.data.paletteLength == 1) {
writer.writeEnum(ChunkCompressionAlgo, .uniform);
writer.writeInt(u32, ch.data.palette[0].toInt());
writer.writeInt(u32, ch.data.getValueFromOwnerThread(0).toInt());
return;
}
if(ch.data.paletteLength < 256) {
@ -293,7 +293,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
for(0..chunk.chunkVolume) |i| {
uncompressedData[i] = @intCast(ch.data.data.getValue(i));
if(allowLossy) {
const block = ch.data.palette[uncompressedData[i]];
const block = ch.data.palette.raw.toSlice()[uncompressedData[i]].raw;
const model = main.blocks.meshes.model(block).model();
const occluder = model.allNeighborsOccluded and !block.viewThrough();
if(occluder) {
@ -326,7 +326,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
writer.writeInt(u8, @intCast(ch.data.paletteLength));
for(0..ch.data.paletteLength) |i| {
writer.writeInt(u32, ch.data.palette[i].toInt());
writer.writeInt(u32, ch.data.palette.raw.toSlice()[i].raw.toInt());
}
writer.writeVarInt(usize, compressedData.len);
writer.writeSlice(compressedData);
@ -336,7 +336,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
defer uncompressedWriter.deinit();
for(0..chunk.chunkVolume) |i| {
uncompressedWriter.writeInt(u32, ch.data.getValue(i).toInt());
uncompressedWriter.writeInt(u32, ch.data.getValueFromOwnerThread(i).toInt());
}
const compressedData = main.utils.Compression.deflate(main.stackAllocator, uncompressedWriter.data.items, .default);
defer main.stackAllocator.free(compressedData);
@ -375,7 +375,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
ch.data.initCapacity(paletteLength);
for(0..paletteLength) |i| {
ch.data.palette[i] = main.blocks.Block.fromInt(try reader.readInt(u32));
ch.data.palette.raw.toSlice()[i] = .init(main.blocks.Block.fromInt(try reader.readInt(u32)));
}
const decompressedData = main.stackAllocator.alloc(u8, chunk.chunkVolume);
@ -392,7 +392,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
}
},
.uniform => {
ch.data.palette[0] = main.blocks.Block.fromInt(try reader.readInt(u32));
ch.data.palette.raw.toSlice()[0] = .init(main.blocks.Block.fromInt(try reader.readInt(u32)));
},
}
}
@ -409,7 +409,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
while(iterator.next()) |entry| {
const index = entry.key_ptr.*;
const blockEntityIndex = entry.value_ptr.*;
const block = ch.data.getValue(index);
const block = ch.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse continue;
var tempWriter = BinaryWriter.init(main.stackAllocator);
@ -441,7 +441,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
const dataLength = try reader.readVarInt(usize);
const blockEntityData = try reader.readSlice(dataLength);
const block = ch.data.getValue(index);
const block = ch.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse {
std.log.err("Could not load BlockEntity at position {} for block {s}: Block has no block entity", .{pos, block.id()});
continue;

View File

@ -47,13 +47,13 @@ pub fn generate(worldSeed: u64, chunk: *main.chunk.ServerChunk, caveMap: CaveMap
if(minHeight > chunk.super.pos.wz +| chunk.super.width) {
chunk.super.data.deinit();
chunk.super.data.init();
chunk.super.data.palette[0] = stone;
chunk.super.data.palette.raw.toSlice()[0] = .init(stone);
return;
}
if(maxHeight < chunk.super.pos.wz) {
chunk.super.data.deinit();
chunk.super.data.init();
chunk.super.data.palette[0] = air;
chunk.super.data.palette.raw.toSlice()[0] = .init(air);
return;
}
}

View File

@ -328,8 +328,8 @@ const ChunkManager = struct { // MARK: ChunkManager
generator.generate(server.world.?.seed ^ generator.generatorSeed, ch, caveMap, biomeMap);
}
if(pos.voxelSize != 1) { // Generate LOD replacements
for(ch.super.data.palette[0..ch.super.data.paletteLength]) |*block| {
block.typ = block.lodReplacement();
for(ch.super.data.palette.raw.toSlice()[0..ch.super.data.paletteLength]) |*block| {
block.store(.{.typ = block.raw.lodReplacement(), .data = block.raw.data}, .unordered);
}
}
return ch;

View File

@ -1027,7 +1027,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
}
pub fn deinit(self: *Self) void {
main.heap.GarbageCollection.deferredFreeSlice(dynamicIntArrayAllocator.allocator(), Atomic(u32), self.content.swap(@bitCast(@as(u64, 0)), .monotonic).toSlice());
main.heap.GarbageCollection.deferredFreeSlice(dynamicIntArrayAllocator.allocator(), self.content.swap(@bitCast(@as(u64, 0)), .monotonic).toSlice());
}
inline fn bitInterleave(bits: comptime_int, source: u32) u32 {
@ -1060,7 +1060,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
},
else => unreachable,
}
main.heap.GarbageCollection.deferredFreeSlice(dynamicIntArrayAllocator.allocator(), Atomic(u32), oldContent.toSlice());
main.heap.GarbageCollection.deferredFreeSlice(dynamicIntArrayAllocator.allocator(), oldContent.toSlice());
self.content.store(newContent, .release);
}
@ -1110,7 +1110,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: PaletteCompressedRegion
return struct {
data: DynamicPackedIntArray(size) = .{},
palette: []T,
palette: Atomic(PowerOfTwoSlice(Atomic(T))),
paletteOccupancy: []u32,
paletteLength: u32,
activePaletteEntries: u32,
@ -1119,12 +1119,12 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
pub fn init(self: *Self) void {
self.* = .{
.palette = main.globalAllocator.alloc(T, 1),
.palette = .init(.fromSlice(main.globalAllocator.alignedAlloc(Atomic(T), .@"64", 1))),
.paletteOccupancy = main.globalAllocator.alloc(u32, 1),
.paletteLength = 1,
.activePaletteEntries = 1,
};
self.palette[0] = std.mem.zeroes(T);
self.palette.raw.toSlice()[0] = .init(std.mem.zeroes(T));
self.paletteOccupancy[0] = size;
}
@ -1134,18 +1134,18 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
const bufferLength = @as(u32, 1) << bitSize;
self.* = .{
.data = DynamicPackedIntArray(size).initCapacity(bitSize),
.palette = main.globalAllocator.alloc(T, bufferLength),
.palette = .init(.fromSlice(main.globalAllocator.alignedAlloc(Atomic(T), .@"64", bufferLength))),
.paletteOccupancy = main.globalAllocator.alloc(u32, bufferLength),
.paletteLength = paletteLength,
.activePaletteEntries = 1,
};
self.palette[0] = std.mem.zeroes(T);
self.palette.raw.toSlice()[0] = .init(std.mem.zeroes(T));
self.paletteOccupancy[0] = size;
}
pub fn deinit(self: *Self) void {
self.data.deinit();
main.globalAllocator.free(self.palette);
main.heap.GarbageCollection.deferredFreeSlice(main.globalAllocator, self.palette.raw.toSlice());
main.globalAllocator.free(self.paletteOccupancy);
}
@ -1156,29 +1156,40 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
return @as(u5, 1) << logLog;
}
pub fn getValue(self: *const Self, i: usize) T {
return self.palette[self.data.getValue(i)];
/// Uses the Java memory model to get a value.
/// In case of congestion a random value may get returned.
pub fn getValueUnordered(self: *const Self, i: usize) T {
const paletteIndex = self.data.getValue(i);
const palette = self.palette.load(.unordered).toSlice();
if(paletteIndex >= palette.len) return undefined;
return palette[paletteIndex].load(.unordered);
}
pub fn getValueFromOwnerThread(self: *const Self, i: usize) T {
return self.palette.raw.toSlice()[self.data.getValue(i)].raw;
}
fn getOrInsertPaletteIndex(noalias self: *Self, val: T) u32 {
std.debug.assert(self.paletteLength <= self.palette.len);
std.debug.assert(self.paletteLength <= self.palette.raw.toSlice().len);
var paletteIndex: u32 = 0;
while(paletteIndex < self.paletteLength) : (paletteIndex += 1) { // TODO: There got to be a faster way to do this. Either using SIMD or using a cache or hashmap.
if(std.meta.eql(self.palette[paletteIndex], val)) {
if(std.meta.eql(self.palette.raw.toSlice()[paletteIndex].raw, val)) {
break;
}
}
if(paletteIndex == self.paletteLength) {
if(self.paletteLength == self.palette.len) {
if(self.paletteLength == self.palette.raw.toSlice().len) {
self.data.resizeOnce();
self.palette = main.globalAllocator.realloc(self.palette, @as(usize, 1) << self.data.bitSizeUnsafe());
const newPalette = main.globalAllocator.alignedAlloc(Atomic(T), .@"64", @as(usize, 1) << self.data.bitSizeUnsafe());
@memcpy(newPalette[0..self.palette.raw.toSlice().len], self.palette.raw.toSlice());
main.heap.GarbageCollection.deferredFreeSlice(main.globalAllocator, self.palette.swap(.fromSlice(newPalette), .monotonic).toSlice());
const oldLen = self.paletteOccupancy.len;
self.paletteOccupancy = main.globalAllocator.realloc(self.paletteOccupancy, @as(usize, 1) << self.data.bitSizeUnsafe());
@memset(self.paletteOccupancy[oldLen..], 0);
}
self.palette[paletteIndex] = val;
self.palette.raw.toSlice()[paletteIndex].store(val, .unordered);
self.paletteLength += 1;
std.debug.assert(self.paletteLength <= self.palette.len);
std.debug.assert(self.paletteLength <= self.palette.raw.toSlice().len);
}
return paletteIndex;
}
@ -1247,7 +1258,7 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
if(len == i) break :outer;
}
paletteMap[len] = i;
self.palette[i] = self.palette[len];
self.palette.raw.toSlice()[i].store(self.palette.raw.toSlice()[len].raw, .unordered);
self.paletteOccupancy[i] = self.paletteOccupancy[len];
self.paletteOccupancy[len] = 0;
}
@ -1259,7 +1270,9 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
newData.content.raw = self.data.content.swap(newData.content.raw, .release);
newData.deinit();
self.paletteLength = self.activePaletteEntries;
self.palette = main.globalAllocator.realloc(self.palette, @as(usize, 1) << self.data.bitSizeUnsafe());
const newPalette = main.globalAllocator.alignedAlloc(Atomic(T), .@"64", @as(usize, 1) << self.data.bitSizeUnsafe());
@memcpy(newPalette, self.palette.raw.toSlice()[0..newPalette.len]);
main.heap.GarbageCollection.deferredFreeSlice(main.globalAllocator, self.palette.swap(.fromSlice(newPalette), .monotonic).toSlice());
self.paletteOccupancy = main.globalAllocator.realloc(self.paletteOccupancy, @as(usize, 1) << self.data.bitSizeUnsafe());
}
};

View File

@ -585,8 +585,9 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection
freeFunction: *const fn(*anyopaque) void,
};
const FreeSliceItem = struct {
slice: []const u8,
slice: []u8,
allocator: NeverFailingAllocator,
alignment: std.mem.Alignment,
};
threadlocal var lists: [4]main.ListUnmanaged(FreeItem) = undefined;
threadlocal var sliceLists: [4]main.ListUnmanaged(FreeSliceItem) = undefined;
@ -621,7 +622,7 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection
fn freeItemsFromSliceList(list: *main.ListUnmanaged(FreeSliceItem)) void {
while(list.popOrNull()) |item| {
item.allocator.free(item.slice);
item.allocator.rawFree(item.slice, item.alignment, @returnAddress());
}
}
@ -686,10 +687,13 @@ pub const GarbageCollection = struct { // MARK: GarbageCollection
lists[threadCycle].append(main.globalAllocator, item);
}
pub fn deferredFreeSlice(allocator: NeverFailingAllocator, comptime T: type, items: []T) void {
pub fn deferredFreeSlice(allocator: NeverFailingAllocator, items: anytype) void {
if(items.len == 0) return;
const Slice = @typeInfo(@TypeOf(items)).pointer;
sliceLists[threadCycle].append(main.globalAllocator, .{
.slice = std.mem.sliceAsBytes(items),
.slice = @constCast(std.mem.sliceAsBytes(items)),
.allocator = allocator,
.alignment = .fromByteUnits(Slice.alignment),
});
}