Skip to content

Commit

Permalink
"Functional" voxelization
Browse files Browse the repository at this point in the history
  • Loading branch information
IMS212 committed Feb 14, 2025
1 parent 4630bb3 commit 5c1458e
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.caffeinemc.mods.sodium.client.render.chunk;

import net.caffeinemc.mods.sodium.client.gl.arena.GlBufferSegment;
import net.caffeinemc.mods.sodium.client.render.chunk.compile.estimation.MeshResultSize;
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
import net.caffeinemc.mods.sodium.client.render.chunk.occlusion.GraphDirection;
Expand Down Expand Up @@ -57,7 +58,7 @@ public class RenderSection {

// Lifetime state
private boolean disposed;
private long voxelOffset;
private GlBufferSegment voxelOffset;

public RenderSection(RenderRegion region, int chunkX, int chunkY, int chunkZ) {
this.chunkX = chunkX;
Expand Down Expand Up @@ -264,10 +265,10 @@ public int getSectionIndex() {
}

public long getVoxelOffset() {
return this.voxelOffset;
return this.voxelOffset.getOffset();
}

public void setVoxelOffset(long voxelOffset) {
public void setVoxelOffset(GlBufferSegment voxelOffset) {
this.voxelOffset = voxelOffset;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,8 @@ public Collection<String> getDebugStrings() {
count++;
}

list.add(String.format("Voxel buffer: %d/%d MiB", MathUtil.toMib(regions.voxelArena.getDeviceUsedMemory()), MathUtil.toMib(regions.voxelArena.getDeviceAllocatedMemory())));

list.add(String.format("Geometry Pool: %d/%d MiB (%d buffers)", MathUtil.toMib(deviceUsed), MathUtil.toMib(deviceAllocated), count));
list.add(String.format("Transfer Queue: %s", this.regions.getStagingBuffer().toString()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void destroy() {
}

if (this.voxels != null) {
this.voxels.free();
// this.voxels.free();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LightTexture;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.chunk.VisGraph;
import net.minecraft.client.resources.model.BakedModel;
Expand Down Expand Up @@ -119,16 +120,17 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke
BlockState blockState = slice.getBlockState(x, y, z);
blockPos.set(x, y, z);

int light = LightTexture.pack(cache.getWorldSlice().getBrightness(LightLayer.BLOCK, blockPos), cache.getWorldSlice().getBrightness(LightLayer.SKY, blockPos));
MemoryUtil.memPutInt(addr + (to1D(x & 15, y & 15, z & 15) * 8) + 4, light);

if (blockState.isAir() && !blockState.hasBlockEntity()) {
MemoryUtil.memPutInt(addr + (to1D(x & 15, y & 15, z & 15) * 8), 0);
MemoryUtil.memPutInt(addr + (to1D(x & 15, y & 15, z & 15) * 8) + 4, cache.getWorldSlice().getBrightness(LightLayer.SKY, blockPos));

continue;
}

modelOffset.set(x & 15, y & 15, z & 15);
MemoryUtil.memPutInt(addr + (to1D(x & 15, y & 15, z & 15) * 8), 1);
MemoryUtil.memPutInt(addr + (to1D(x & 15, y & 15, z & 15) * 8) + 4, cache.getWorldSlice().getBrightness(LightLayer.SKY, blockPos));

if (blockState.getRenderShape() == RenderShape.MODEL) {
BakedModel model = cache.getBlockModels()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.caffeinemc.mods.sodium.client.render.chunk.region;

import net.caffeinemc.mods.sodium.client.gl.arena.GlBufferSegment;
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;

public record PendingDispatch(int frame, GlBufferSegment segment, RenderSection section) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.caffeinemc.mods.sodium.client.render.chunk.region;

import it.unimi.dsi.fastutil.longs.Long2LongFunction;
import org.lwjgl.opengl.GL46C;

public class PersistentBufferObject {
public int frameId;
public int bufferId;
public long size;
private long mapLocation;

public PersistentBufferObject(long size) {
bufferId = GL46C.glCreateBuffers();
this.size = size;
if (size == 0) return;

GL46C.glNamedBufferStorage(bufferId, size * 3, GL46C.GL_MAP_WRITE_BIT | GL46C.GL_MAP_PERSISTENT_BIT);
mapLocation = GL46C.nglMapNamedBufferRange(bufferId, 0, size * 3, GL46C.GL_MAP_WRITE_BIT | GL46C.GL_MAP_FLUSH_EXPLICIT_BIT | GL46C.GL_MAP_PERSISTENT_BIT);
}

public void beginFrame() {
frameId = (frameId + 1) % 3;
}

public void updateAndFlush(Long2LongFunction updater) {
if (size == 0) return;

long usedSize = updater.applyAsLong(mapLocation + (size * frameId));

GL46C.glFlushMappedNamedBufferRange(bufferId, size * frameId, usedSize);
}

public void bind() {
if (size == 0) return;
GL46C.glBindBufferRange(GL46C.GL_UNIFORM_BUFFER, 9, bufferId, size * frameId, size);
}

public void close() {
GL46C.glUnmapNamedBuffer(bufferId);
GL46C.glDeleteBuffers(bufferId);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.caffeinemc.mods.sodium.client.render.chunk.region;

import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.caffeinemc.mods.sodium.client.SodiumClientMod;
Expand All @@ -21,38 +22,70 @@
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
import net.caffeinemc.mods.sodium.client.util.NativeBuffer;
import net.minecraft.client.Minecraft;
import net.minecraft.core.SectionPos;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.opengl.GL46C;
import org.lwjgl.system.MemoryUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class RenderRegionManager {
private static final int NUM_OF_CHUNKS = 128;
private final Long2ReferenceOpenHashMap<RenderRegion> regions = new Long2ReferenceOpenHashMap<>();

private final StagingBuffer stagingBuffer;
private final GlBufferArena voxelArena;
private PendingSectionVoxelUpload upcomingUpload;
public final GlBufferArena voxelArena;
private Deque<PendingSectionVoxelUpload> upcomingUploads = new ArrayDeque<>();
private Deque<PendingDispatch> upcomingDispatches = new ArrayDeque<>();
private PersistentBufferObject ubo = new PersistentBufferObject(16 * NUM_OF_CHUNKS);
private List<PendingDispatch>[] dispatches = new ArrayList[3];

private int frame = 0;

public RenderRegionManager(CommandList commandList) {
this.stagingBuffer = createStagingBuffer(commandList);
int rd = 2 * Minecraft.getInstance().options.getEffectiveRenderDistance() + 1;
this.voxelArena = new GlBufferArena(commandList, rd * 16,
32768, stagingBuffer);
for (int i = 0; i < 3; i++) {
dispatches[i] = new ArrayList<>();
}

int buffer = GL46C.glGenBuffers();
GL46C.glNamedBufferStorage(buffer, 4, 0);

GL46C.glBindBufferBase(GL46C.GL_SHADER_STORAGE_BUFFER, 5, buffer);
}

public void update() {
GL46C.glBindBufferBase(GL46C.GL_SHADER_STORAGE_BUFFER, 8, voxelArena.getBufferObject().handle());

this.stagingBuffer.flip();


try (CommandList commandList = RenderDevice.INSTANCE.createCommandList()) {
int queueSize = Math.min(256, this.upcomingUploads.size());
PendingSectionVoxelUpload[] toUpload = new PendingSectionVoxelUpload[queueSize];

for (int i = 0; i < queueSize; i++) {
toUpload[i] = this.upcomingUploads.poll();
if (toUpload[i].uploaded.get()) throw new IllegalStateException("HOW");
}

boolean bufferChanged = voxelArena.upload(commandList, Arrays.stream(toUpload)
.map(upload1 -> upload1.upload));

for (int i = 0; i < queueSize; i++) {
toUpload[i].section.setVoxelOffset(toUpload[i].upload.getResult());
toUpload[i].data.free();
toUpload[i].uploaded.set(true);
DefaultShaderInterface.VOXEL = (int) (toUpload[i].upload.getResult().getOffset());

upcomingDispatches.add(new PendingDispatch(frame, toUpload[i].upload.getResult(), toUpload[i].section));
}


Iterator<RenderRegion> it = this.regions.values()
.iterator();

Expand All @@ -66,27 +99,42 @@ public void update() {
it.remove();
}
}
}

if (this.upcomingUpload != null) {
PendingSectionVoxelUpload upload = this.upcomingUpload;
this.upcomingUpload = null;
GL46C.glFinish();
System.out.println("Chunk " + upload.section.toString());
long addr = MemoryUtil.nmemAlloc(32768);
GL46C.nglGetNamedBufferSubData(voxelArena.getBufferObject().handle(), upload.upload.getResult().getOffset() * 32768, 32768, addr);
GL46C.glFinish();
DefaultShaderInterface.VOXEL = (int) (upload.upload.getResult().getOffset());
for (int x = 0; x < 4096; x++) {
boolean hasBlock = MemoryUtil.memGetInt(addr + (x * 8)) != 0;
int[] v = to3D(x);
if (!hasBlock) {
System.out.println("Block " + (upload.section.getOriginX() + v[0]) + ", " + (upload.section.getOriginY() + v[1]) + ", " + (upload.section.getOriginZ() + v[2]) + " is missing");
} else {
System.out.println("Block " + (upload.section.getOriginX() + v[0]) + ", " + (upload.section.getOriginY() + v[1]) + ", " + (upload.section.getOriginZ() + v[2]) + " exists");
ubo.beginFrame();

ubo.updateAndFlush((buffer) -> {
long offset = 0;

for (int i = 0; i < Math.min(upcomingDispatches.size(), NUM_OF_CHUNKS); i++) {
// pretend for now
PendingDispatch dispatch = upcomingDispatches.poll();
if (frame - dispatch.frame() < 2) {
upcomingDispatches.add(dispatch);
continue;
}

dispatches[frame % 3].add(dispatch);
MemoryUtil.memPutInt(buffer + offset, dispatch.section().getChunkX());
MemoryUtil.memPutInt(buffer + offset + 4, dispatch.section().getChunkY());
MemoryUtil.memPutInt(buffer + offset + 8, dispatch.section().getChunkZ());
MemoryUtil.memPutInt(buffer + offset + 12, (int) (dispatch.segment().getOffset()));

offset += 16;
}

return offset;
});

VoxelCompute.run(dispatches[frame % 3].size(), ubo);

for (PendingDispatch dispatch : dispatches[(frame + 2) % 3]) {
dispatch.segment().delete();
}

dispatches[(frame + 2) % 3].clear();
}

frame++;
}

public void uploadResults(CommandList commandList, Collection<BuilderTaskOutput> results) {
Expand All @@ -105,7 +153,7 @@ public int[] to3D( int idx ) {

private void uploadResults(CommandList commandList, RenderRegion region, Collection<BuilderTaskOutput> results) {
var uploads = new ArrayList<PendingSectionMeshUpload>();
var voxelUpload = new ArrayList<PendingSectionVoxelUpload>();
var voxelUpload = new ObjectArraySet<PendingSectionVoxelUpload>();
var indexUploads = new ArrayList<PendingSectionIndexBufferUpload>();

for (BuilderTaskOutput result : results) {
Expand Down Expand Up @@ -133,7 +181,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect
NativeBuffer voxels = chunkBuildOutput.getVoxels();

if (voxels != null) {
voxelUpload.add(new PendingSectionVoxelUpload(result.render, voxels, new PendingUpload(voxels)));
voxelUpload.add(new PendingSectionVoxelUpload(result.render, voxels, new PendingUpload(voxels), new AtomicBoolean()));
}
}
}
Expand Down Expand Up @@ -188,17 +236,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect
profiler.popPush("upload_voxels");

if (!voxelUpload.isEmpty()) {
boolean bufferChanged = voxelArena.upload(commandList, voxelUpload.stream()
.map(upload -> upload.upload));

// Collect the upload results
for (PendingSectionVoxelUpload upload : voxelUpload) {
if (SectionPos.of(Minecraft.getInstance().player.blockPosition()).equals(SectionPos.of(upload.section.getChunkX(), upload.section.getChunkY(), upload.section.getChunkZ()))) {
this.upcomingUpload = upload;
}

upload.section.setVoxelOffset(upload.upload.getResult().getOffset());
}
upcomingUploads.addAll(voxelUpload);
}

profiler.popPush("upload_indices");
Expand Down Expand Up @@ -238,6 +276,7 @@ public void delete(CommandList commandList) {
}

this.regions.clear();
this.voxelArena.delete(commandList);
this.stagingBuffer.delete(commandList);
}

Expand Down Expand Up @@ -276,7 +315,22 @@ private RenderRegion create(int x, int y, int z) {
private record PendingSectionMeshUpload(RenderSection section, BuiltSectionMeshParts meshData, TerrainRenderPass pass, PendingUpload vertexUpload) {
}

private record PendingSectionVoxelUpload(RenderSection section, NativeBuffer data, PendingUpload upload) {
private record PendingSectionVoxelUpload(RenderSection section, NativeBuffer data, PendingUpload upload, AtomicBoolean uploaded) {
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PendingSectionVoxelUpload)) {
return false;
}

PendingSectionVoxelUpload other = (PendingSectionVoxelUpload) obj;

return section.equals(other.section) && data.getAddress() == other.data.getAddress();
}

@Override
public int hashCode() {
return Objects.hash(section, data.getAddress());
}
}

private record PendingSectionIndexBufferUpload(RenderSection section, PendingUpload indexBufferUpload) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package net.caffeinemc.mods.sodium.client.render.chunk.region;

import com.mojang.blaze3d.platform.GlStateManager;
import org.apache.commons.io.IOUtils;
import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GL46C;

import java.io.IOException;

public class VoxelCompute {
private static int compute;

static {
compute = GL46C.glCreateProgram();
int shader = GL46C.glCreateShader(GL46C.GL_COMPUTE_SHADER);
try {
GlStateManager.glShaderSource(shader, IOUtils.toString(VoxelCompute.class.getResourceAsStream("/voxel.csh")));
} catch (IOException e) {
throw new RuntimeException(e);
}
GL46C.glCompileShader(shader);
GL46C.glAttachShader(compute, shader);
GL46C.glLinkProgram(compute);

String log = GlStateManager.glGetProgramInfoLog(compute, 16384);

if (!log.isEmpty()) {
System.out.println("Program link log for voxel: " + log);

}
int result = GlStateManager.glGetProgrami(compute, GL20C.GL_LINK_STATUS);

if (result != GL20C.GL_TRUE) {
throw new RuntimeException(log);
}

GL46C.glDetachShader(compute, shader);
GL46C.glDeleteShader(shader);
}

public static void run(int size, PersistentBufferObject ubo) {
if (size == 0) return;
ubo.bind();
GL46C.glUseProgram(compute);
GL46C.glDispatchCompute(size, 1, 1);
}
}
Loading

0 comments on commit 5c1458e

Please sign in to comment.