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

View File

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

View File

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

View File

@ -17,6 +17,21 @@ pub fn deinit() void {
memoryPool.deinit(); 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 { fn extractColor(in: u32) [3]u8 {
return .{ return .{
@truncate(in >> 16), @truncate(in >> 16),
@ -26,7 +41,7 @@ fn extractColor(in: u32) [3]u8 {
} }
pub const ChannelChunk = struct { pub const ChannelChunk = struct {
data: main.utils.PaletteCompressedRegion([3]u8, chunk.chunkVolume), data: main.utils.PaletteCompressedRegion(LightValue, chunk.chunkVolume),
lock: main.utils.ReadWriteLock, lock: main.utils.ReadWriteLock,
ch: *chunk.Chunk, ch: *chunk.Chunk,
isSun: bool, isSun: bool,
@ -66,9 +81,8 @@ pub const ChannelChunk = struct {
}; };
pub fn getValue(self: *ChannelChunk, x: i32, y: i32, z: i32) [3]u8 { pub fn getValue(self: *ChannelChunk, x: i32, y: i32, z: i32) [3]u8 {
self.lock.assertLockedRead();
const index = chunk.getIndex(x, y, z); 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 { 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(); self.lock.lockWrite();
while(lightQueue.popFront()) |entry| { while(lightQueue.popFront()) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z); 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 = .{ const newValue: [3]u8 = .{
@max(entry.value[0], oldValue[0]), @max(entry.value[0], oldValue[0]),
@max(entry.value[1], oldValue[1]), @max(entry.value[1], oldValue[1]),
@max(entry.value[2], oldValue[2]), @max(entry.value[2], oldValue[2]),
}; };
if(newValue[0] == oldValue[0] and newValue[1] == oldValue[1] and newValue[2] == oldValue[2]) continue; 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| { for(chunk.Neighbor.iterable) |neighbor| {
if(neighbor.toInt() == entry.sourceDir) continue; if(neighbor.toInt() == entry.sourceDir) continue;
const nx = entry.x + neighbor.relX(); 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[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
result.value[2] -|= 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(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) { 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); neighborLists[neighbor.toInt()].append(main.stackAllocator, result);
continue; continue;
} }
const neighborIndex = chunk.getIndex(nx, ny, nz); 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); 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(); self.lock.lockWrite();
while(lightQueue.popFront()) |entry| { while(lightQueue.popFront()) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z); 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 activeValue: @Vector(3, bool) = @bitCast(entry.activeValue);
var append: bool = false; var append: bool = false;
if(activeValue[0] and entry.value[0] != oldValue[0]) { 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[0]) insertValue[0] = 0;
if(activeValue[1]) insertValue[1] = 0; if(activeValue[1]) insertValue[1] = 0;
if(activeValue[2]) insertValue[2] = 0; if(activeValue[2]) insertValue[2] = 0;
self.data.setValue(index, insertValue); self.data.setValue(index, .fromArray(insertValue));
for(chunk.Neighbor.iterable) |neighbor| { for(chunk.Neighbor.iterable) |neighbor| {
if(neighbor.toInt() == entry.sourceDir) continue; if(neighbor.toInt() == entry.sourceDir) continue;
const nx = entry.x + neighbor.relX(); 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[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
result.value[2] -|= 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) { 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); neighborLists[neighbor.toInt()].append(main.stackAllocator, result);
continue; continue;
} }
const neighborIndex = chunk.getIndex(nx, ny, nz); 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); lightQueue.pushBack(result);
} }
} }
@ -251,7 +265,7 @@ pub const ChannelChunk = struct {
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);
var result = entry; 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); if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.pushBack(result);
} }
self.propagateDirect(lightQueue, lightRefreshList); self.propagateDirect(lightQueue, lightRefreshList);
@ -262,7 +276,7 @@ pub const ChannelChunk = struct {
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);
var result = entry; 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); lightQueue.pushBack(result);
} }
return self.propagateDestructive(lightQueue, constructiveEntries, false, lightRefreshList); return self.propagateDestructive(lightQueue, constructiveEntries, false, lightRefreshList);
@ -276,7 +290,7 @@ pub const ChannelChunk = struct {
if(self.isSun) { 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}); lightQueue.pushBack(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = .{255, 255, 255}, .sourceDir = 6, .activeValue = 0b111});
} else { } 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) { if(checkNeighbors) {
@ -311,15 +325,15 @@ pub const ChannelChunk = struct {
defer neighborLightChunk.lock.unlockRead(); defer neighborLightChunk.lock.unlockRead();
const index = chunk.getIndex(x, y, z); const index = chunk.getIndex(x, y, z);
const neighborIndex = chunk.getIndex(otherX, otherY, otherZ); 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) { 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[0] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
value[1] -|= 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)); 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; 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}); 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.deinit();
self.data.init(); self.data.init();
} }
self.data.palette[0] = .{255, 255, 255}; self.data.palette.raw.toSlice()[0].store(.fromArray(@splat(255)), .unordered);
self.lock.unlockWrite(); self.lock.unlockWrite();
const val = 255 -| 8*|@as(u8, @intCast(self.ch.pos.voxelSize)); const val = 255 -| 8*|@as(u8, @intCast(self.ch.pos.voxelSize));
var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12); var lightQueue = main.utils.CircularBufferQueue(Entry).init(main.stackAllocator, 1 << 12);
@ -381,7 +395,7 @@ pub const ChannelChunk = struct {
self.lock.lockRead(); self.lock.lockRead();
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]);
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(); self.lock.unlockRead();
var constructiveEntries: main.ListUnmanaged(ChunkEntries) = .{}; var constructiveEntries: main.ListUnmanaged(ChunkEntries) = .{};
@ -398,15 +412,15 @@ pub const ChannelChunk = struct {
channelChunk.lock.lockWrite(); channelChunk.lock.lockWrite();
for(entryList.items) |entry| { for(entryList.items) |entry| {
const index = chunk.getIndex(entry.x, entry.y, entry.z); const index = chunk.getIndex(entry.x, entry.y, entry.z);
var value = channelChunk.data.getValue(index); var value = channelChunk.data.getValueFromOwnerThread(index).toArray();
const light = if(self.isSun) .{0, 0, 0} else extractColor(channelChunk.ch.data.getValue(index).light()); const light = if(self.isSun) .{0, 0, 0} else extractColor(channelChunk.ch.data.getValueFromOwnerThread(index).light());
value = .{ value = .{
@max(value[0], light[0]), @max(value[0], light[0]),
@max(value[1], light[1]), @max(value[1], light[1]),
@max(value[2], light[2]), @max(value[2], light[2]),
}; };
if(value[0] == 0 and value[1] == 0 and value[2] == 0) continue; 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}); lightQueue.pushBack(.{.x = entry.x, .y = entry.y, .z = entry.z, .value = value, .sourceDir = 6, .activeValue = 0b111});
} }
channelChunk.lock.unlockWrite(); 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 { fn compressBlockData(ch: *chunk.Chunk, allowLossy: bool, writer: *BinaryWriter) void {
if(ch.data.paletteLength == 1) { if(ch.data.paletteLength == 1) {
writer.writeEnum(ChunkCompressionAlgo, .uniform); writer.writeEnum(ChunkCompressionAlgo, .uniform);
writer.writeInt(u32, ch.data.palette[0].toInt()); writer.writeInt(u32, ch.data.getValueFromOwnerThread(0).toInt());
return; return;
} }
if(ch.data.paletteLength < 256) { if(ch.data.paletteLength < 256) {
@ -293,7 +293,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
for(0..chunk.chunkVolume) |i| { for(0..chunk.chunkVolume) |i| {
uncompressedData[i] = @intCast(ch.data.data.getValue(i)); uncompressedData[i] = @intCast(ch.data.data.getValue(i));
if(allowLossy) { 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 model = main.blocks.meshes.model(block).model();
const occluder = model.allNeighborsOccluded and !block.viewThrough(); const occluder = model.allNeighborsOccluded and !block.viewThrough();
if(occluder) { if(occluder) {
@ -326,7 +326,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
writer.writeInt(u8, @intCast(ch.data.paletteLength)); writer.writeInt(u8, @intCast(ch.data.paletteLength));
for(0..ch.data.paletteLength) |i| { 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.writeVarInt(usize, compressedData.len);
writer.writeSlice(compressedData); writer.writeSlice(compressedData);
@ -336,7 +336,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
defer uncompressedWriter.deinit(); defer uncompressedWriter.deinit();
for(0..chunk.chunkVolume) |i| { 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); const compressedData = main.utils.Compression.deflate(main.stackAllocator, uncompressedWriter.data.items, .default);
defer main.stackAllocator.free(compressedData); defer main.stackAllocator.free(compressedData);
@ -375,7 +375,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
ch.data.initCapacity(paletteLength); ch.data.initCapacity(paletteLength);
for(0..paletteLength) |i| { 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); const decompressedData = main.stackAllocator.alloc(u8, chunk.chunkVolume);
@ -392,7 +392,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
} }
}, },
.uniform => { .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| { while(iterator.next()) |entry| {
const index = entry.key_ptr.*; const index = entry.key_ptr.*;
const blockEntityIndex = entry.value_ptr.*; const blockEntityIndex = entry.value_ptr.*;
const block = ch.data.getValue(index); const block = ch.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse continue; const blockEntity = block.blockEntity() orelse continue;
var tempWriter = BinaryWriter.init(main.stackAllocator); var tempWriter = BinaryWriter.init(main.stackAllocator);
@ -441,7 +441,7 @@ pub const ChunkCompression = struct { // MARK: ChunkCompression
const dataLength = try reader.readVarInt(usize); const dataLength = try reader.readVarInt(usize);
const blockEntityData = try reader.readSlice(dataLength); const blockEntityData = try reader.readSlice(dataLength);
const block = ch.data.getValue(index); const block = ch.data.getValueFromOwnerThread(index);
const blockEntity = block.blockEntity() orelse { 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()}); std.log.err("Could not load BlockEntity at position {} for block {s}: Block has no block entity", .{pos, block.id()});
continue; 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) { if(minHeight > chunk.super.pos.wz +| chunk.super.width) {
chunk.super.data.deinit(); chunk.super.data.deinit();
chunk.super.data.init(); chunk.super.data.init();
chunk.super.data.palette[0] = stone; chunk.super.data.palette.raw.toSlice()[0] = .init(stone);
return; return;
} }
if(maxHeight < chunk.super.pos.wz) { if(maxHeight < chunk.super.pos.wz) {
chunk.super.data.deinit(); chunk.super.data.deinit();
chunk.super.data.init(); chunk.super.data.init();
chunk.super.data.palette[0] = air; chunk.super.data.palette.raw.toSlice()[0] = .init(air);
return; return;
} }
} }

View File

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

View File

@ -1027,7 +1027,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
} }
pub fn deinit(self: *Self) void { 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 { inline fn bitInterleave(bits: comptime_int, source: u32) u32 {
@ -1060,7 +1060,7 @@ pub fn DynamicPackedIntArray(size: comptime_int) type { // MARK: DynamicPackedIn
}, },
else => unreachable, 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); 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 pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: PaletteCompressedRegion
return struct { return struct {
data: DynamicPackedIntArray(size) = .{}, data: DynamicPackedIntArray(size) = .{},
palette: []T, palette: Atomic(PowerOfTwoSlice(Atomic(T))),
paletteOccupancy: []u32, paletteOccupancy: []u32,
paletteLength: u32, paletteLength: u32,
activePaletteEntries: u32, activePaletteEntries: u32,
@ -1119,12 +1119,12 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
pub fn init(self: *Self) void { pub fn init(self: *Self) void {
self.* = .{ self.* = .{
.palette = main.globalAllocator.alloc(T, 1), .palette = .init(.fromSlice(main.globalAllocator.alignedAlloc(Atomic(T), .@"64", 1))),
.paletteOccupancy = main.globalAllocator.alloc(u32, 1), .paletteOccupancy = main.globalAllocator.alloc(u32, 1),
.paletteLength = 1, .paletteLength = 1,
.activePaletteEntries = 1, .activePaletteEntries = 1,
}; };
self.palette[0] = std.mem.zeroes(T); self.palette.raw.toSlice()[0] = .init(std.mem.zeroes(T));
self.paletteOccupancy[0] = size; 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; const bufferLength = @as(u32, 1) << bitSize;
self.* = .{ self.* = .{
.data = DynamicPackedIntArray(size).initCapacity(bitSize), .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), .paletteOccupancy = main.globalAllocator.alloc(u32, bufferLength),
.paletteLength = paletteLength, .paletteLength = paletteLength,
.activePaletteEntries = 1, .activePaletteEntries = 1,
}; };
self.palette[0] = std.mem.zeroes(T); self.palette.raw.toSlice()[0] = .init(std.mem.zeroes(T));
self.paletteOccupancy[0] = size; self.paletteOccupancy[0] = size;
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.data.deinit(); self.data.deinit();
main.globalAllocator.free(self.palette); main.heap.GarbageCollection.deferredFreeSlice(main.globalAllocator, self.palette.raw.toSlice());
main.globalAllocator.free(self.paletteOccupancy); 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; return @as(u5, 1) << logLog;
} }
pub fn getValue(self: *const Self, i: usize) T { /// Uses the Java memory model to get a value.
return self.palette[self.data.getValue(i)]; /// 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 { 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; 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. 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; break;
} }
} }
if(paletteIndex == self.paletteLength) { if(paletteIndex == self.paletteLength) {
if(self.paletteLength == self.palette.len) { if(self.paletteLength == self.palette.raw.toSlice().len) {
self.data.resizeOnce(); 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; const oldLen = self.paletteOccupancy.len;
self.paletteOccupancy = main.globalAllocator.realloc(self.paletteOccupancy, @as(usize, 1) << self.data.bitSizeUnsafe()); self.paletteOccupancy = main.globalAllocator.realloc(self.paletteOccupancy, @as(usize, 1) << self.data.bitSizeUnsafe());
@memset(self.paletteOccupancy[oldLen..], 0); @memset(self.paletteOccupancy[oldLen..], 0);
} }
self.palette[paletteIndex] = val; self.palette.raw.toSlice()[paletteIndex].store(val, .unordered);
self.paletteLength += 1; self.paletteLength += 1;
std.debug.assert(self.paletteLength <= self.palette.len); std.debug.assert(self.paletteLength <= self.palette.raw.toSlice().len);
} }
return paletteIndex; return paletteIndex;
} }
@ -1247,7 +1258,7 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { // MARK: Pale
if(len == i) break :outer; if(len == i) break :outer;
} }
paletteMap[len] = i; 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[i] = self.paletteOccupancy[len];
self.paletteOccupancy[len] = 0; 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.content.raw = self.data.content.swap(newData.content.raw, .release);
newData.deinit(); newData.deinit();
self.paletteLength = self.activePaletteEntries; 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()); 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, freeFunction: *const fn(*anyopaque) void,
}; };
const FreeSliceItem = struct { const FreeSliceItem = struct {
slice: []const u8, slice: []u8,
allocator: NeverFailingAllocator, allocator: NeverFailingAllocator,
alignment: std.mem.Alignment,
}; };
threadlocal var lists: [4]main.ListUnmanaged(FreeItem) = undefined; threadlocal var lists: [4]main.ListUnmanaged(FreeItem) = undefined;
threadlocal var sliceLists: [4]main.ListUnmanaged(FreeSliceItem) = 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 { fn freeItemsFromSliceList(list: *main.ListUnmanaged(FreeSliceItem)) void {
while(list.popOrNull()) |item| { 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); 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, .{ sliceLists[threadCycle].append(main.globalAllocator, .{
.slice = std.mem.sliceAsBytes(items), .slice = @constCast(std.mem.sliceAsBytes(items)),
.allocator = allocator, .allocator = allocator,
.alignment = .fromByteUnits(Slice.alignment),
}); });
} }