diff --git a/src/chunk.zig b/src/chunk.zig index 3ad4abcf..947b2c62 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -171,7 +171,7 @@ pub const ChunkPosition = struct { pub const Chunk = struct { pos: ChunkPosition, - blocks: [chunkVolume]Block = undefined, + data: main.utils.PaletteCompressedRegion(Block, chunkVolume) = undefined, wasChanged: bool = false, /// When a chunk is cleaned, it won't be saved by the ChunkManager anymore, so following changes need to be saved directly. @@ -199,10 +199,12 @@ pub const Chunk = struct { .widthShift = voxelSizeShift + chunkShift, .mutex = std.Thread.Mutex{}, }; + self.data.init(); return self; } pub fn deinit(self: *Chunk) void { + self.data.deinit(); memoryPoolMutex.lock(); memoryPool.destroy(@alignCast(self)); memoryPoolMutex.unlock(); @@ -260,8 +262,9 @@ pub const Chunk = struct { const y = _y >> self.voxelSizeShift; const z = _z >> self.voxelSizeShift; const index = getIndex(x, y, z); - if (self.blocks[index].typ == 0 or self.blocks[index].degradable()) { - self.blocks[index] = newBlock; + const oldBlock = self.data.getValue(index); + if(oldBlock.typ == 0 or oldBlock.degradable()) { + self.data.setValue(index, newBlock); } } @@ -272,7 +275,7 @@ pub const Chunk = struct { const y = _y >> self.voxelSizeShift; const z = _z >> self.voxelSizeShift; const index = getIndex(x, y, z); - self.blocks[index] = newBlock; + self.data.setValue(index, newBlock); } /// Updates a block if it is inside this chunk. Should be used in generation to prevent accidently storing these as changes. @@ -282,7 +285,7 @@ pub const Chunk = struct { const y = _y >> self.voxelSizeShift; const z = _z >> self.voxelSizeShift; const index = getIndex(x, y, z); - self.blocks[index] = newBlock; + self.data.setValue(index, newBlock); } /// Gets a block if it is inside this chunk. @@ -292,7 +295,7 @@ pub const Chunk = struct { const y = _y >> self.voxelSizeShift; const z = _z >> self.voxelSizeShift; const index = getIndex(x, y, z); - return self.blocks[index]; + return self.data.getValue(index); } pub fn getNeighbors(self: *const Chunk, x: i32, y: i32, z: i32, neighborsArray: *[6]Block) void { @@ -340,7 +343,7 @@ pub const Chunk = struct { 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.blocks[index]; + octantBlocks[i] = other.data.getValue(index); if(octantBlocks[i] == 0) continue; // I don't care about air blocks. var count: u32 = 0; @@ -350,7 +353,7 @@ pub const Chunk = struct { const nz = z*2 + dz + Neighbors.relZ[n]; 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.blocks[neighborIndex].transparent()) { + if(other.data.getValue(neighborIndex).transparent()) { count += 5; } } else { @@ -368,12 +371,12 @@ pub const Chunk = struct { for(0..8) |i| { const appliedPermutation = permutationStart ^ i; if(neighborCount[appliedPermutation] >= maxCount - 1) { // Avoid pattern breaks at chunk borders. - block = blocks[appliedPermutation]; + block = octantBlocks[appliedPermutation]; } } // Update the block: const thisIndex = getIndex(x + xOffset, y + yOffset, z + zOffset); - self.blocks[thisIndex] = block; + self.data.setValue(thisIndex, block); } } } diff --git a/src/gui/windows/graphics.zig b/src/gui/windows/graphics.zig index d0a19d7b..4058abc9 100644 --- a/src/gui/windows/graphics.zig +++ b/src/gui/windows/graphics.zig @@ -17,9 +17,10 @@ pub var window = GuiWindow { }; const padding: f32 = 8; +const renderDistances = [_]u16{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; fn renderDistanceCallback(newValue: u16) void { - settings.renderDistance = newValue + 1; + settings.renderDistance = newValue + renderDistances[0]; } fn LODFactorCallback(newValue: u16) void { @@ -42,8 +43,7 @@ fn anisotropicFilteringCallback(newValue: bool) void { pub fn onOpen() void { const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); - const renderDistances = [_]u32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, settings.renderDistance - 1, &renderDistanceCallback)); + list.add(DiscreteSlider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, settings.renderDistance - renderDistances[0], &renderDistanceCallback)); list.add(CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback)); list.add(CheckBox.init(.{0, 0}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback)); list.add(CheckBox.init(.{0, 0}, 128, "Anisotropic Filtering", settings.anisotropicFiltering, &anisotropicFilteringCallback)); diff --git a/src/network.zig b/src/network.zig index 9f5d947c..e49d986d 100644 --- a/src/network.zig +++ b/src/network.zig @@ -744,16 +744,16 @@ pub const Protocols = struct { } data = _inflatedData; const ch = chunk.Chunk.init(pos); - for(&ch.blocks) |*block| { - block.* = Block.fromInt(std.mem.readInt(u32, data[0..4], .big)); + for(0..chunk.chunkVolume) |i| { + ch.data.setValue(i, Block.fromInt(std.mem.readInt(u32, data[0..4], .big))); data = data[4..]; } renderer.mesh_storage.updateChunkMesh(ch); } fn sendChunkOverTheNetwork(conn: *Connection, ch: *chunk.Chunk) void { - var uncompressedData: [@sizeOf(@TypeOf(ch.blocks))]u8 = undefined; // TODO: #15280 - for(&ch.blocks, 0..) |*block, i| { - std.mem.writeInt(u32, uncompressedData[4*i..][0..4], block.toInt(), .big); + var uncompressedData: [chunk.chunkVolume*@sizeOf(u32)]u8 = undefined; + for(0..chunk.chunkVolume) |i| { + std.mem.writeInt(u32, uncompressedData[4*i..][0..4], ch.data.getValue(i).toInt(), .big); } const compressedData = utils.Compression.deflate(main.stackAllocator, &uncompressedData); defer main.stackAllocator.free(compressedData); @@ -768,7 +768,8 @@ pub const Protocols = struct { } fn sendChunkLocally(ch: *chunk.Chunk) void { const chunkCopy = chunk.Chunk.init(ch.pos); - @memcpy(&chunkCopy.blocks, &ch.blocks); + chunkCopy.data.deinit(); + chunkCopy.data.initCopy(&ch.data); renderer.mesh_storage.updateChunkMesh(chunkCopy); } pub fn sendChunk(conn: *Connection, ch: *chunk.Chunk) void { diff --git a/src/renderer/chunk_meshing.zig b/src/renderer/chunk_meshing.zig index 239490ee..f221d9a3 100644 --- a/src/renderer/chunk_meshing.zig +++ b/src/renderer/chunk_meshing.zig @@ -586,7 +586,7 @@ pub const ChunkMesh = struct { while(y < chunk.chunkSize): (y += 1) { var z: u8 = 0; while(z < chunk.chunkSize): (z += 1) { - const block = (&self.chunk.blocks)[chunk.getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + const block = self.chunk.data.getValue(chunk.getIndex(x, y, z)); if(block.light() != 0) lightEmittingBlocks.append(.{x, y, z}); } } @@ -666,7 +666,7 @@ pub const ChunkMesh = struct { while(y < chunk.chunkSize): (y += 1) { var z: u8 = 0; while(z < chunk.chunkSize): (z += 1) { - const block = (&self.chunk.blocks)[chunk.getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + const block = self.chunk.data.getValue(chunk.getIndex(x, y, z)); if(block.typ == 0) continue; // Check all neighbors: for(chunk.Neighbors.iterable) |i| { @@ -675,7 +675,7 @@ pub const ChunkMesh = struct { const y2 = y + chunk.Neighbors.relY[i]; const z2 = z + chunk.Neighbors.relZ[i]; if(x2&chunk.chunkMask != x2 or y2&chunk.chunkMask != y2 or z2&chunk.chunkMask != z2) continue; // Neighbor is outside the chunk. - const neighborBlock = (&self.chunk.blocks)[chunk.getIndex(x2, y2, z2)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + const neighborBlock = self.chunk.data.getValue(chunk.getIndex(x2, y2, z2)); if(canBeSeenThroughOtherBlock(block, neighborBlock, i)) { if(block.transparent()) { if(block.hasBackFace()) { @@ -700,12 +700,12 @@ pub const ChunkMesh = struct { while(x < chunk.chunkSize): (x += 1) { var y: u8 = 0; while(y < chunk.chunkSize): (y += 1) { - self.chunkBorders[chunk.Neighbors.dirNegX].adjustToBlock((&self.chunk.blocks)[chunk.getIndex(0, x, y)], .{0, x, y}, chunk.Neighbors.dirNegX); // TODO: Wait for the compiler bug to get fixed. - self.chunkBorders[chunk.Neighbors.dirPosX].adjustToBlock((&self.chunk.blocks)[chunk.getIndex(chunk.chunkSize-1, x, y)], .{chunk.chunkSize, x, y}, chunk.Neighbors.dirPosX); // TODO: Wait for the compiler bug to get fixed. - self.chunkBorders[chunk.Neighbors.dirNegY].adjustToBlock((&self.chunk.blocks)[chunk.getIndex(x, 0, y)], .{x, 0, y}, chunk.Neighbors.dirNegY); // TODO: Wait for the compiler bug to get fixed. - self.chunkBorders[chunk.Neighbors.dirPosY].adjustToBlock((&self.chunk.blocks)[chunk.getIndex(x, chunk.chunkSize-1, y)], .{x, chunk.chunkSize, y}, chunk.Neighbors.dirPosY); // TODO: Wait for the compiler bug to get fixed. - self.chunkBorders[chunk.Neighbors.dirDown].adjustToBlock((&self.chunk.blocks)[chunk.getIndex(x, y, 0)], .{x, y, 0}, chunk.Neighbors.dirDown); // TODO: Wait for the compiler bug to get fixed. - self.chunkBorders[chunk.Neighbors.dirUp].adjustToBlock((&self.chunk.blocks)[chunk.getIndex(x, y, chunk.chunkSize-1)], .{x, y, chunk.chunkSize}, chunk.Neighbors.dirUp); // TODO: Wait for the compiler bug to get fixed. + self.chunkBorders[chunk.Neighbors.dirNegX].adjustToBlock(self.chunk.data.getValue(chunk.getIndex(0, x, y)), .{0, x, y}, chunk.Neighbors.dirNegX); + self.chunkBorders[chunk.Neighbors.dirPosX].adjustToBlock(self.chunk.data.getValue(chunk.getIndex(chunk.chunkSize-1, x, y)), .{chunk.chunkSize, x, y}, chunk.Neighbors.dirPosX); + self.chunkBorders[chunk.Neighbors.dirNegY].adjustToBlock(self.chunk.data.getValue(chunk.getIndex(x, 0, y)), .{x, 0, y}, chunk.Neighbors.dirNegY); + self.chunkBorders[chunk.Neighbors.dirPosY].adjustToBlock(self.chunk.data.getValue(chunk.getIndex(x, chunk.chunkSize-1, y)), .{x, chunk.chunkSize, y}, chunk.Neighbors.dirPosY); + self.chunkBorders[chunk.Neighbors.dirDown].adjustToBlock(self.chunk.data.getValue(chunk.getIndex(x, y, 0)), .{x, y, 0}, chunk.Neighbors.dirDown); + self.chunkBorders[chunk.Neighbors.dirUp].adjustToBlock(self.chunk.data.getValue(chunk.getIndex(x, y, chunk.chunkSize-1)), .{x, y, chunk.chunkSize}, chunk.Neighbors.dirUp); } } self.mutex.unlock(); @@ -729,10 +729,10 @@ pub const ChunkMesh = struct { defer neighborChunkMesh.decreaseRefCount(); const index = chunk.getIndex(nx & chunk.chunkMask, ny & chunk.chunkMask, nz & chunk.chunkMask); neighborChunkMesh.mutex.lock(); - var neighborBlock = neighborChunkMesh.chunk.blocks[index]; + var neighborBlock = neighborChunkMesh.chunk.data.getValue(index); if(neighborBlock.mode().dependsOnNeighbors) { if(neighborBlock.mode().updateData(&neighborBlock, neighbor ^ 1, newBlock)) { - neighborChunkMesh.chunk.blocks[index] = neighborBlock; + neighborChunkMesh.chunk.data.setValue(index, neighborBlock); neighborChunkMesh.opaqueMesh.coreFaces.clearRetainingCapacity(); neighborChunkMesh.transparentMesh.coreFaces.clearRetainingCapacity(); neighborChunkMesh.mutex.unlock(); @@ -745,10 +745,10 @@ pub const ChunkMesh = struct { } else { const index = chunk.getIndex(nx, ny, nz); self.mutex.lock(); - var neighborBlock = self.chunk.blocks[index]; + var neighborBlock = self.chunk.data.getValue(index); if(neighborBlock.mode().dependsOnNeighbors) { if(neighborBlock.mode().updateData(&neighborBlock, neighbor ^ 1, newBlock)) { - self.chunk.blocks[index] = neighborBlock; + self.chunk.data.setValue(index, neighborBlock); } } self.mutex.unlock(); @@ -761,7 +761,7 @@ pub const ChunkMesh = struct { } } self.mutex.lock(); - self.chunk.blocks[chunk.getIndex(x, y, z)] = newBlock; + self.chunk.data.setValue(chunk.getIndex(x, y, z), newBlock); self.mutex.unlock(); for(self.lightingData[0..]) |lightingData| { lightingData.propagateLightsDestructive(&.{.{@intCast(x), @intCast(y), @intCast(z)}}); @@ -872,8 +872,8 @@ pub const ChunkMesh = struct { const otherX = x+%chunk.Neighbors.relX[neighbor] & chunk.chunkMask; const otherY = y+%chunk.Neighbors.relY[neighbor] & chunk.chunkMask; const otherZ = z+%chunk.Neighbors.relZ[neighbor] & chunk.chunkMask; - const block = (&self.chunk.blocks)[chunk.getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. - const otherBlock = (&neighborMesh.chunk.blocks)[chunk.getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + const block = self.chunk.data.getValue(chunk.getIndex(x, y, z)); + const otherBlock = neighborMesh.chunk.data.getValue(chunk.getIndex(otherX, otherY, otherZ)); if(canBeSeenThroughOtherBlock(block, otherBlock, neighbor)) { if(block.transparent()) { if(block.hasBackFace()) { @@ -953,8 +953,8 @@ pub const ChunkMesh = struct { const otherX = (x+%chunk.Neighbors.relX[neighbor]+%offsetX >> 1) & chunk.chunkMask; const otherY = (y+%chunk.Neighbors.relY[neighbor]+%offsetY >> 1) & chunk.chunkMask; const otherZ = (z+%chunk.Neighbors.relZ[neighbor]+%offsetZ >> 1) & chunk.chunkMask; - const block = (&self.chunk.blocks)[chunk.getIndex(x, y, z)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. - const otherBlock = (&neighborMesh.chunk.blocks)[chunk.getIndex(otherX, otherY, otherZ)]; // ← a temporary fix to a compiler performance bug. TODO: check if this was fixed. + const block = self.chunk.data.getValue(chunk.getIndex(x, y, z)); + const otherBlock = neighborMesh.chunk.data.getValue(chunk.getIndex(otherX, otherY, otherZ)); if(canBeSeenThroughOtherBlock(otherBlock, block, neighbor ^ 1)) { if(otherBlock.transparent()) { self.transparentMesh.appendNeighborFacingQuadsToNeighbor(otherBlock, neighbor ^ 1, x, y, z, false, true); diff --git a/src/renderer/lighting.zig b/src/renderer/lighting.zig index 66b65c5c..bc5faa53 100644 --- a/src/renderer/lighting.zig +++ b/src/renderer/lighting.zig @@ -133,14 +133,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.blocks[index], self.ch.pos.voxelSize, neighbor); + calculateOutgoingOcclusion(&result.value, self.ch.data.getValue(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].append(main.stackAllocator, result); continue; } const neighborIndex = chunk.getIndex(nx, ny, nz); - calculateIncomingOcclusion(&result.value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor ^ 1); + calculateIncomingOcclusion(&result.value, self.ch.data.getValue(neighborIndex), self.ch.pos.voxelSize, neighbor ^ 1); if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result); } } @@ -213,13 +213,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.blocks[index], self.ch.pos.voxelSize, neighbor); + calculateOutgoingOcclusion(&result.value, self.ch.data.getValue(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].append(main.stackAllocator, result); continue; } const neighborIndex = chunk.getIndex(nx, ny, nz); - calculateIncomingOcclusion(&result.value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor ^ 1); + calculateIncomingOcclusion(&result.value, self.ch.data.getValue(neighborIndex), self.ch.pos.voxelSize, neighbor ^ 1); lightQueue.enqueue(result); } } @@ -245,7 +245,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.blocks[index], self.ch.pos.voxelSize, entry.sourceDir); + calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, entry.sourceDir); if(result.value[0] != 0 or result.value[1] != 0 or result.value[2] != 0) lightQueue.enqueue(result); } self.propagateDirect(lightQueue); @@ -256,7 +256,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.blocks[index], self.ch.pos.voxelSize, entry.sourceDir); + calculateIncomingOcclusion(&result.value, self.ch.data.getValue(index), self.ch.pos.voxelSize, entry.sourceDir); lightQueue.enqueue(result); } return self.propagateDestructive(lightQueue, constructiveEntries, false); @@ -270,7 +270,7 @@ pub const ChannelChunk = struct { if(self.isSun) { lightQueue.enqueue(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = .{255, 255, 255}, .sourceDir = 6, .activeValue = 0b111}); } else { - lightQueue.enqueue(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = extractColor(self.ch.blocks[index].light()), .sourceDir = 6, .activeValue = 0b111}); + lightQueue.enqueue(.{.x = @intCast(pos[0]), .y = @intCast(pos[1]), .z = @intCast(pos[2]), .value = extractColor(self.ch.data.getValue(index).light()), .sourceDir = 6, .activeValue = 0b111}); } } if(checkNeighbors) { @@ -312,9 +312,9 @@ pub const ChannelChunk = struct { value[1] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize)); value[2] -|= 8*|@as(u8, @intCast(self.ch.pos.voxelSize)); } - calculateOutgoingOcclusion(&value, self.ch.blocks[neighborIndex], self.ch.pos.voxelSize, neighbor); + calculateOutgoingOcclusion(&value, self.ch.data.getValue(neighborIndex), self.ch.pos.voxelSize, neighbor); if(value[0] == 0 and value[1] == 0 and value[2] == 0) continue; - calculateIncomingOcclusion(&value, self.ch.blocks[index], self.ch.pos.voxelSize, neighbor ^ 1); + calculateIncomingOcclusion(&value, self.ch.data.getValue(index), self.ch.pos.voxelSize, neighbor ^ 1); if(value[0] != 0 or value[1] != 0 or value[2] != 0) lightQueue.enqueue(.{.x = @intCast(x), .y = @intCast(y), .z = @intCast(z), .value = value, .sourceDir = @intCast(neighbor), .activeValue = 0b111}); } } diff --git a/src/renderer/mesh_storage.zig b/src/renderer/mesh_storage.zig index aae4766c..a00030f6 100644 --- a/src/renderer/mesh_storage.zig +++ b/src/renderer/mesh_storage.zig @@ -29,7 +29,7 @@ const ChunkMeshNode = struct { active: bool, rendered: bool, }; -const storageSize = 32; +const storageSize = 64; const storageMask = storageSize - 1; var storageLists: [settings.highestLOD + 1]*[storageSize*storageSize*storageSize]ChunkMeshNode = undefined; var mapStorageLists: [settings.highestLOD + 1]*[storageSize*storageSize]Atomic(?*LightMap.LightMapFragment) = undefined; diff --git a/src/utils.zig b/src/utils.zig index ff11cdbb..6e0d440c 100644 --- a/src/utils.zig +++ b/src/utils.zig @@ -1215,7 +1215,7 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { pub fn init(self: *Self) void { self.* = .{ - .palette = main.globalAllocator.alloc([3]u8, 1), + .palette = main.globalAllocator.alloc(T, 1), .paletteOccupancy = main.globalAllocator.alloc(u32, 1), .paletteLength = 1, .activePaletteEntries = 1, @@ -1224,13 +1224,26 @@ pub fn PaletteCompressedRegion(T: type, size: comptime_int) type { self.paletteOccupancy[0] = size; } + pub fn initCopy(self: *Self, template: *const Self) void { + self.* = .{ + .data = .{ + .data = main.globalAllocator.dupe(u8, template.data.data), + .bitSize = template.data.bitSize, + }, + .palette = main.globalAllocator.dupe(T, template.palette), + .paletteOccupancy = main.globalAllocator.dupe(u32, template.paletteOccupancy), + .paletteLength = template.paletteLength, + .activePaletteEntries = template.activePaletteEntries, + }; + } + pub fn deinit(self: *Self) void { self.data.deinit(main.globalAllocator); main.globalAllocator.free(self.palette); main.globalAllocator.free(self.paletteOccupancy); } - pub fn getValue(self: *Self, i: usize) T { + pub fn getValue(self: *const Self, i: usize) T { return self.palette[self.data.getValue(i)]; }