Indirectly lit

- "Functional" arena based lighting for indirect
- Strip out most of the reference counting stuffs for embeddings
- Naively re-buffer all tracked light sections every frame
This commit is contained in:
Jozufozu 2024-06-21 15:26:48 -07:00
parent e10852fe7a
commit 51224d618f
26 changed files with 134 additions and 110 deletions

View file

@ -8,5 +8,4 @@ public class Samplers {
public static final GlTextureUnit LIGHT = GlTextureUnit.T2; public static final GlTextureUnit LIGHT = GlTextureUnit.T2;
public static final GlTextureUnit CRUMBLING = GlTextureUnit.T3; public static final GlTextureUnit CRUMBLING = GlTextureUnit.T3;
public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4; public static final GlTextureUnit INSTANCE_BUFFER = GlTextureUnit.T4;
public static final GlTextureUnit EMBEDDED_LIGHT = GlTextureUnit.T5;
} }

View file

@ -13,7 +13,7 @@ public enum ContextShader {
DEFAULT(null, $ -> { DEFAULT(null, $ -> {
}), }),
CRUMBLING("_FLW_CRUMBLING", program -> program.setSamplerBinding("_flw_crumblingTex", Samplers.CRUMBLING)), CRUMBLING("_FLW_CRUMBLING", program -> program.setSamplerBinding("_flw_crumblingTex", Samplers.CRUMBLING)),
EMBEDDED("_FLW_EMBEDDED", program -> program.setSamplerBinding("_flw_lightVolume", Samplers.EMBEDDED_LIGHT)); EMBEDDED("_FLW_EMBEDDED", $ -> {});
@Nullable @Nullable
private final String define; private final String define;

View file

@ -5,7 +5,7 @@ import java.util.ArrayList;
import dev.engine_room.flywheel.api.Flywheel; import dev.engine_room.flywheel.api.Flywheel;
import dev.engine_room.flywheel.api.instance.InstanceType; import dev.engine_room.flywheel.api.instance.InstanceType;
import dev.engine_room.flywheel.api.layout.Layout; import dev.engine_room.flywheel.api.layout.Layout;
import dev.engine_room.flywheel.backend.engine.indirect.IndirectBuffers; import dev.engine_room.flywheel.backend.engine.indirect.BufferBindings;
import dev.engine_room.flywheel.backend.glsl.generate.FnSignature; import dev.engine_room.flywheel.backend.glsl.generate.FnSignature;
import dev.engine_room.flywheel.backend.glsl.generate.GlslBlock; import dev.engine_room.flywheel.backend.glsl.generate.GlslBlock;
import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder; import dev.engine_room.flywheel.backend.glsl.generate.GlslBuilder;
@ -43,7 +43,7 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent {
fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs)); fnBody.ret(GlslExpr.call(STRUCT_NAME, unpackArgs));
builder._addRaw("layout(std430, binding = " + IndirectBuffers.INSTANCE_INDEX + ") restrict readonly buffer InstanceBuffer {\n" builder._addRaw("layout(std430, binding = " + BufferBindings.INSTANCE_BUFFER_BINDING + ") restrict readonly buffer InstanceBuffer {\n"
+ " uint _flw_instances[];\n" + " uint _flw_instances[];\n"
+ "};"); + "};");
builder.blankLine(); builder.blankLine();

View file

@ -25,8 +25,6 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
protected AbstractInstancer(InstanceType<I> type, Environment environment) { protected AbstractInstancer(InstanceType<I> type, Environment environment) {
this.type = type; this.type = type;
this.environment = environment; this.environment = environment;
environment.acquire();
} }
@Override @Override
@ -177,9 +175,7 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
deleted.clear(); deleted.clear();
} }
public void delete() { public abstract void delete();
environment.release();
}
@Override @Override
public String toString() { public String toString() {

View file

@ -16,6 +16,7 @@ import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.model.Model; import dev.engine_room.flywheel.api.model.Model;
import dev.engine_room.flywheel.backend.FlwBackend; import dev.engine_room.flywheel.backend.FlwBackend;
import dev.engine_room.flywheel.backend.engine.embed.Environment; import dev.engine_room.flywheel.backend.engine.embed.Environment;
import dev.engine_room.flywheel.backend.engine.embed.LightStorage;
import dev.engine_room.flywheel.lib.util.Pair; import dev.engine_room.flywheel.lib.util.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -45,7 +46,7 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
initializationQueue.clear(); initializationQueue.clear();
} }
public void flush() { public void flush(LightStorage lightStorage) {
// Thread safety: flush is called from the render thread after all visual updates have been made, // Thread safety: flush is called from the render thread after all visual updates have been made,
// so there are no:tm: threads we could be racing with. // so there are no:tm: threads we could be racing with.
for (var instancer : initializationQueue) { for (var instancer : initializationQueue) {

View file

@ -31,7 +31,7 @@ import net.minecraft.world.phys.Vec3;
public class EngineImpl implements Engine { public class EngineImpl implements Engine {
private final int sqrMaxOriginDistance; private final int sqrMaxOriginDistance;
private final DrawManager<? extends AbstractInstancer<?>> drawManager; private final DrawManager<? extends AbstractInstancer<?>> drawManager;
private final EnvironmentStorage environmentStorage = new EnvironmentStorage(); private final EnvironmentStorage environmentStorage;
private final LightStorage lightStorage; private final LightStorage lightStorage;
private final Flag flushFlag = new NamedFlag("flushed"); private final Flag flushFlag = new NamedFlag("flushed");
@ -40,12 +40,13 @@ public class EngineImpl implements Engine {
public EngineImpl(LevelAccessor level, DrawManager<? extends AbstractInstancer<?>> drawManager, int maxOriginDistance) { public EngineImpl(LevelAccessor level, DrawManager<? extends AbstractInstancer<?>> drawManager, int maxOriginDistance) {
this.drawManager = drawManager; this.drawManager = drawManager;
sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance; sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
lightStorage = new LightStorage(level); environmentStorage = new EnvironmentStorage();
lightStorage = new LightStorage(level, environmentStorage);
} }
@Override @Override
public Plan<RenderContext> createFramePlan() { public Plan<RenderContext> createFramePlan() {
return SyncedPlan.of(this::flush); return lightStorage.createFramePlan().then(SyncedPlan.of(this::flush));
} }
@Override @Override
@ -96,7 +97,6 @@ public class EngineImpl implements Engine {
@Override @Override
public void delete() { public void delete() {
drawManager.delete(); drawManager.delete();
environmentStorage.delete();
lightStorage.delete(); lightStorage.delete();
} }
@ -107,8 +107,8 @@ public class EngineImpl implements Engine {
private void flush(RenderContext ctx) { private void flush(RenderContext ctx) {
try (var state = GlStateTracker.getRestoreState()) { try (var state = GlStateTracker.getRestoreState()) {
Uniforms.update(ctx); Uniforms.update(ctx);
drawManager.flush();
environmentStorage.flush(); environmentStorage.flush();
drawManager.flush(lightStorage);
} }
flushFlag.raise(); flushFlag.raise();

View file

@ -1,6 +1,8 @@
package dev.engine_room.flywheel.backend.engine; package dev.engine_room.flywheel.backend.engine;
import dev.engine_room.flywheel.backend.engine.embed.AbstractEmbeddedEnvironment; import dev.engine_room.flywheel.backend.engine.embed.AbstractEmbeddedEnvironment;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet; import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet; import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.dsi.fastutil.objects.ReferenceSets; import it.unimi.dsi.fastutil.objects.ReferenceSets;
@ -13,10 +15,13 @@ public class EnvironmentStorage {
} }
public void flush() { public void flush() {
environments.removeIf(AbstractEmbeddedEnvironment::isDeleted);
environments.forEach(AbstractEmbeddedEnvironment::flush); environments.forEach(AbstractEmbeddedEnvironment::flush);
} }
public void delete() { public LongSet allLightSections() {
environments.clear(); var out = new LongOpenHashSet();
environments.forEach(e -> e.addLightSections(out));
return out;
} }
} }

View file

@ -15,10 +15,10 @@ import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.ContextShader;
import dev.engine_room.flywheel.backend.engine.EngineImpl; import dev.engine_room.flywheel.backend.engine.EngineImpl;
import dev.engine_room.flywheel.backend.gl.shader.GlProgram; import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
import dev.engine_room.flywheel.backend.util.AtomicReferenceCounted; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
public abstract class AbstractEmbeddedEnvironment extends AtomicReferenceCounted implements Environment, VisualEmbedding { public abstract class AbstractEmbeddedEnvironment implements Environment, VisualEmbedding {
protected final Matrix4f pose = new Matrix4f(); protected final Matrix4f pose = new Matrix4f();
protected final Matrix3f normal = new Matrix3f(); protected final Matrix3f normal = new Matrix3f();
private final Matrix4f poseComposed = new Matrix4f(); private final Matrix4f poseComposed = new Matrix4f();
@ -27,6 +27,8 @@ public abstract class AbstractEmbeddedEnvironment extends AtomicReferenceCounted
protected final EngineImpl engine; protected final EngineImpl engine;
private final RenderStage renderStage; private final RenderStage renderStage;
private boolean deleted = false;
public AbstractEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { public AbstractEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) {
this.engine = engine; this.engine = engine;
this.renderStage = renderStage; this.renderStage = renderStage;
@ -38,9 +40,6 @@ public abstract class AbstractEmbeddedEnvironment extends AtomicReferenceCounted
return engine.instancer(AbstractEmbeddedEnvironment.this, type, model, renderStage); return engine.instancer(AbstractEmbeddedEnvironment.this, type, model, renderStage);
} }
}; };
// Acquire the reference owned by the visual that created this.
acquire();
} }
@Override @Override
@ -94,15 +93,20 @@ public abstract class AbstractEmbeddedEnvironment extends AtomicReferenceCounted
return out; return out;
} }
public boolean isDeleted() {
return deleted;
}
public void addLightSections(LongSet out) {
}
/** /**
* Called by visuals * Called by visuals
*/ */
@Override @Override
public void delete() { public void delete() {
// Release the reference owned by the visual that created this. deleted = true;
// Note that visuals don't explicitly call acquire, instead the
// storage acquired a reference when this was constructed.
release();
} }
public abstract void setupLight(GlProgram program); public abstract void setupLight(GlProgram program);

View file

@ -47,4 +47,8 @@ public class Arena {
public void delete() { public void delete() {
memoryBlock.free(); memoryBlock.free();
} }
public int capacity() {
return top;
}
} }

View file

@ -9,8 +9,4 @@ public interface Environment {
void setupDraw(GlProgram drawProgram); void setupDraw(GlProgram drawProgram);
void setupCull(GlProgram cullProgram); void setupCull(GlProgram cullProgram);
void acquire();
void release();
} }

View file

@ -23,14 +23,4 @@ public class GlobalEnvironment implements Environment {
public void setupCull(GlProgram cullProgram) { public void setupCull(GlProgram cullProgram) {
cullProgram.setBool(EmbeddingUniforms.USE_MODEL_MATRIX, false); cullProgram.setBool(EmbeddingUniforms.USE_MODEL_MATRIX, false);
} }
@Override
public void acquire() {
}
@Override
public void release() {
}
} }

View file

@ -4,7 +4,11 @@ import java.util.BitSet;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import dev.engine_room.flywheel.api.event.RenderContext;
import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.backend.engine.EnvironmentStorage;
import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer; import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer;
import dev.engine_room.flywheel.lib.task.SimplePlan;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
@ -29,11 +33,12 @@ import net.minecraft.world.level.LightLayer;
* <p>Thus, each section occupies 5832 bytes. * <p>Thus, each section occupies 5832 bytes.
*/ */
public class LightStorage { public class LightStorage {
private final long SECTION_SIZE_BYTES = 9 * 9 * 9 * 8; public static final long SECTION_SIZE_BYTES = 9 * 9 * 9 * 8;
private final int DEFAULT_ARENA_CAPACITY_SECTIONS = 64; private static final int DEFAULT_ARENA_CAPACITY_SECTIONS = 64;
private final int INVALID_SECTION = -1; private static final int INVALID_SECTION = -1;
private final LevelAccessor level; private final LevelAccessor level;
private final EnvironmentStorage environmentStorage;
private final Arena arena; private final Arena arena;
private final Long2IntMap section2ArenaIndex = new Long2IntOpenHashMap(); private final Long2IntMap section2ArenaIndex = new Long2IntOpenHashMap();
@ -44,12 +49,24 @@ public class LightStorage {
private final BitSet changed = new BitSet(); private final BitSet changed = new BitSet();
private boolean newSections = false; private boolean newSections = false;
public LightStorage(LevelAccessor level) { public LightStorage(LevelAccessor level, EnvironmentStorage environmentStorage) {
this.level = level; this.level = level;
this.environmentStorage = environmentStorage;
arena = new Arena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS); arena = new Arena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS);
} }
public Plan<RenderContext> createFramePlan() {
return SimplePlan.of(() -> {
var longs = environmentStorage.allLightSections();
longs.forEach(this::addSection);
});
}
public int capacity() {
return arena.capacity();
}
public void addSection(long section) { public void addSection(long section) {
var lightEngine = level.getLightEngine(); var lightEngine = level.getLightEngine();
@ -187,6 +204,7 @@ public class LightStorage {
for (int i = changed.nextSetBit(0); i >= 0; i = changed.nextSetBit(i + 1)) { for (int i = changed.nextSetBit(0); i >= 0; i = changed.nextSetBit(i + 1)) {
staging.enqueueCopy(arena.indexToPointer(i), SECTION_SIZE_BYTES, dstVbo, i * SECTION_SIZE_BYTES); staging.enqueueCopy(arena.indexToPointer(i), SECTION_SIZE_BYTES, dstVbo, i * SECTION_SIZE_BYTES);
} }
changed.clear();
} }
public IntArrayList createLut() { public IntArrayList createLut() {

View file

@ -14,7 +14,6 @@ public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
public NestedEmbeddedEnvironment(AbstractEmbeddedEnvironment parent, EngineImpl engine, RenderStage renderStage) { public NestedEmbeddedEnvironment(AbstractEmbeddedEnvironment parent, EngineImpl engine, RenderStage renderStage) {
super(engine, renderStage); super(engine, renderStage);
this.parent = parent; this.parent = parent;
parent.acquire();
} }
@Override @Override
@ -33,9 +32,4 @@ public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
pose.mul(this.pose); pose.mul(this.pose);
normal.mul(this.normal); normal.mul(this.normal);
} }
@Override
public void _delete() {
parent.release();
}
} }

View file

@ -16,19 +16,17 @@ public class TopLevelEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
super(engine, renderStage); super(engine, renderStage);
} }
@Override
public void flush() {
super.flush();
lightSections.forEach(engine.lightStorage()::addSection);
}
@Override @Override
public void lightChunks(LongSet chunks) { public void lightChunks(LongSet chunks) {
lightSections.clear(); lightSections.clear();
lightSections.addAll(chunks); lightSections.addAll(chunks);
} }
@Override
public void addLightSections(LongSet out) {
out.addAll(lightSections);
}
@Override @Override
public void setupLight(GlProgram program) { public void setupLight(GlProgram program) {
program.setBool(EmbeddingUniforms.USE_LIGHT_VOLUME, !lightSections.isEmpty()); program.setBool(EmbeddingUniforms.USE_LIGHT_VOLUME, !lightSections.isEmpty());
@ -39,9 +37,4 @@ public class TopLevelEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
pose.set(this.pose); pose.set(this.pose);
normal.set(this.normal); normal.set(this.normal);
} }
@Override
protected void _delete() {
}
} }

View file

@ -0,0 +1,14 @@
package dev.engine_room.flywheel.backend.engine.indirect;
public class BufferBindings {
public static final int INSTANCE_BUFFER_BINDING = 0;
public static final int TARGET_BUFFER_BINDING = 1;
public static final int MODEL_INDEX_BUFFER_BINDING = 2;
public static final int MODEL_BUFFER_BINDING = 3;
public static final int DRAW_BUFFER_BINDING = 4;
public static final int EMBEDDING_LUT_BINDING = 5;
public static final int EMBEDDING_LIGHT_BINDING = 6;
private BufferBindings() {
}
}

View file

@ -22,13 +22,6 @@ public class IndirectBuffers {
public static final long DRAW_COMMAND_STRIDE = 40; public static final long DRAW_COMMAND_STRIDE = 40;
public static final long DRAW_COMMAND_OFFSET = 0; public static final long DRAW_COMMAND_OFFSET = 0;
public static final int INSTANCE_INDEX = 0;
public static final int TARGET_INDEX = 1;
public static final int MODEL_INDEX_INDEX = 2;
public static final int MODEL_INDEX = 3;
public static final int DRAW_INDEX = 4;
// Offsets to the 3 segments // Offsets to the 3 segments
private static final long HANDLE_OFFSET = 0; private static final long HANDLE_OFFSET = 0;
private static final long OFFSET_OFFSET = BUFFER_COUNT * INT_SIZE; private static final long OFFSET_OFFSET = BUFFER_COUNT * INT_SIZE;
@ -117,7 +110,7 @@ public class IndirectBuffers {
private void multiBind() { private void multiBind() {
final long ptr = multiBindBlock.ptr(); final long ptr = multiBindBlock.ptr();
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET); nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.INSTANCE_BUFFER_BINDING, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
} }
/** /**
@ -125,7 +118,7 @@ public class IndirectBuffers {
*/ */
public void bindForCrumbling() { public void bindForCrumbling() {
final long ptr = multiBindBlock.ptr(); final long ptr = multiBindBlock.ptr();
nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, 0, 4, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET); nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.INSTANCE_BUFFER_BINDING, 4, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
} }
public void delete() { public void delete() {

View file

@ -23,6 +23,7 @@ import dev.engine_room.flywheel.backend.engine.InstancerKey;
import dev.engine_room.flywheel.backend.engine.MaterialRenderState; import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
import dev.engine_room.flywheel.backend.engine.MeshPool; import dev.engine_room.flywheel.backend.engine.MeshPool;
import dev.engine_room.flywheel.backend.engine.TextureBinder; import dev.engine_room.flywheel.backend.engine.TextureBinder;
import dev.engine_room.flywheel.backend.engine.embed.LightStorage;
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
import dev.engine_room.flywheel.backend.gl.GlStateTracker; import dev.engine_room.flywheel.backend.gl.GlStateTracker;
import dev.engine_room.flywheel.backend.gl.array.GlVertexArray; import dev.engine_room.flywheel.backend.gl.array.GlVertexArray;
@ -40,6 +41,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
private final Map<GroupKey<?>, IndirectCullingGroup<?>> cullingGroups = new HashMap<>(); private final Map<GroupKey<?>, IndirectCullingGroup<?>> cullingGroups = new HashMap<>();
private final GlBuffer crumblingDrawBuffer = new GlBuffer(); private final GlBuffer crumblingDrawBuffer = new GlBuffer();
private final LightBuffers lightBuffers;
public IndirectDrawManager(IndirectPrograms programs) { public IndirectDrawManager(IndirectPrograms programs) {
this.programs = programs; this.programs = programs;
programs.acquire(); programs.acquire();
@ -50,6 +53,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
vertexArray = GlVertexArray.create(); vertexArray = GlVertexArray.create();
meshPool.bind(vertexArray); meshPool.bind(vertexArray);
lightBuffers = new LightBuffers();
} }
@Override @Override
@ -83,6 +87,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
TextureBinder.bindLightAndOverlay(); TextureBinder.bindLightAndOverlay();
vertexArray.bindForDraw(); vertexArray.bindForDraw();
lightBuffers.bind();
Uniforms.bindAll(); Uniforms.bindAll();
for (var group : cullingGroups.values()) { for (var group : cullingGroups.values()) {
@ -95,8 +100,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
} }
@Override @Override
public void flush() { public void flush(LightStorage lightStorage) {
super.flush(); super.flush(lightStorage);
for (var group : cullingGroups.values()) { for (var group : cullingGroups.values()) {
group.flushInstancers(); group.flushInstancers();
@ -108,6 +113,8 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
stagingBuffer.reclaim(); stagingBuffer.reclaim();
lightBuffers.flush(stagingBuffer, lightStorage);
for (var group : cullingGroups.values()) { for (var group : cullingGroups.values()) {
group.upload(stagingBuffer); group.upload(stagingBuffer);
} }
@ -159,7 +166,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
var block = MemoryBlock.malloc(IndirectBuffers.DRAW_COMMAND_STRIDE); var block = MemoryBlock.malloc(IndirectBuffers.DRAW_COMMAND_STRIDE);
GlBufferType.DRAW_INDIRECT_BUFFER.bind(crumblingDrawBuffer.handle()); GlBufferType.DRAW_INDIRECT_BUFFER.bind(crumblingDrawBuffer.handle());
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, IndirectBuffers.DRAW_INDEX, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.DRAW_BUFFER_BINDING, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE);
for (var groupEntry : byType.entrySet()) { for (var groupEntry : byType.entrySet()) {
var byProgress = groupEntry.getValue(); var byProgress = groupEntry.getValue();

View file

@ -125,8 +125,6 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
@Override @Override
public void delete() { public void delete() {
super.delete();
for (IndirectDraw draw : draws()) { for (IndirectDraw draw : draws()) {
draw.delete(); draw.delete();
} }

View file

@ -1,17 +1,25 @@
package dev.engine_room.flywheel.backend.engine.indirect; package dev.engine_room.flywheel.backend.engine.indirect;
import org.lwjgl.opengl.GL46;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import dev.engine_room.flywheel.backend.engine.embed.LightStorage; import dev.engine_room.flywheel.backend.engine.embed.LightStorage;
public class LightBuffers { public class LightBuffers {
private final ResizableStorageBuffer lightArena = new ResizableStorageBuffer(); private final ResizableStorageArray lightArena = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
private final ResizableStorageArray lut = new ResizableStorageArray(4); private final ResizableStorageArray lut = new ResizableStorageArray(4);
public LightBuffers() { public LightBuffers() {
} }
public void flush(StagingBuffer staging, LightStorage light) { public void flush(StagingBuffer staging, LightStorage light) {
var capacity = light.capacity();
if (capacity == 0) {
return;
}
lightArena.ensureCapacity(capacity);
light.uploadChangedSections(staging, lightArena.handle()); light.uploadChangedSections(staging, lightArena.handle());
if (light.hasNewSections()) { if (light.hasNewSections()) {
@ -26,4 +34,13 @@ public class LightBuffers {
}); });
} }
} }
public void bind() {
if (lightArena.capacity() == 0) {
return;
}
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.EMBEDDING_LUT_BINDING, lut.handle(), 0, lut.byteCapacity());
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.EMBEDDING_LIGHT_BINDING, lightArena.handle(), 0, lightArena.byteCapacity());
}
} }

View file

@ -22,6 +22,7 @@ import dev.engine_room.flywheel.backend.engine.MaterialEncoder;
import dev.engine_room.flywheel.backend.engine.MaterialRenderState; import dev.engine_room.flywheel.backend.engine.MaterialRenderState;
import dev.engine_room.flywheel.backend.engine.MeshPool; import dev.engine_room.flywheel.backend.engine.MeshPool;
import dev.engine_room.flywheel.backend.engine.TextureBinder; import dev.engine_room.flywheel.backend.engine.TextureBinder;
import dev.engine_room.flywheel.backend.engine.embed.LightStorage;
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms; import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
import dev.engine_room.flywheel.backend.gl.GlStateTracker; import dev.engine_room.flywheel.backend.gl.GlStateTracker;
import dev.engine_room.flywheel.backend.gl.TextureBuffer; import dev.engine_room.flywheel.backend.gl.TextureBuffer;
@ -55,8 +56,8 @@ public class InstancedDrawManager extends DrawManager<InstancedInstancer<?>> {
} }
@Override @Override
public void flush() { public void flush(LightStorage lightStorage) {
super.flush(); super.flush(lightStorage);
var instancers = this.instancers.values(); var instancers = this.instancers.values();
instancers.removeIf(instancer -> { instancers.removeIf(instancer -> {

View file

@ -116,8 +116,6 @@ public class InstancedInstancer<I extends Instance> extends AbstractInstancer<I>
} }
public void delete() { public void delete() {
super.delete();
if (vbo == null) { if (vbo == null) {
return; return;
} }

View file

@ -13,14 +13,6 @@ uniform sampler2D _flw_crumblingTex;
in vec2 _flw_crumblingTexCoord; in vec2 _flw_crumblingTexCoord;
#endif #endif
#ifdef _FLW_EMBEDDED
uniform sampler3D _flw_lightVolume;
uniform bool _flw_useLightVolume;
in vec3 _flw_lightVolumeCoord;
#endif
flat in uint _flw_instanceID; flat in uint _flw_instanceID;
out vec4 _flw_outputColor; out vec4 _flw_outputColor;
@ -43,12 +35,6 @@ void _flw_main() {
flw_fragOverlay = flw_vertexOverlay; flw_fragOverlay = flw_vertexOverlay;
flw_fragLight = flw_vertexLight; flw_fragLight = flw_vertexLight;
#ifdef _FLW_EMBEDDED
if (_flw_useLightVolume) {
flw_fragLight = max(flw_fragLight, texture(_flw_lightVolume, _flw_lightVolumeCoord).rg);
}
#endif
flw_materialFragment(); flw_materialFragment();
#ifdef _FLW_CRUMBLING #ifdef _FLW_CRUMBLING
@ -98,11 +84,6 @@ void _flw_main() {
case 6u: case 6u:
color = vec4(vec3(diffuseFactor), 1.); color = vec4(vec3(diffuseFactor), 1.);
break; break;
#ifdef _FLW_EMBEDDED
case 7u:
color = vec4(_flw_lightVolumeCoord, 1.);
break;
#endif
} }
_flw_outputColor = flw_fogFilter(color); _flw_outputColor = flw_fogFilter(color);

View file

@ -70,7 +70,7 @@ vec2 getCrumblingTexCoord() {
uniform mat4 _flw_modelMatrix; uniform mat4 _flw_modelMatrix;
uniform mat3 _flw_normalMatrix; uniform mat3 _flw_normalMatrix;
bool _flw_embeddedLight(vec3 worldPos, vec3 normal, out vec2 lightCoord); bool _flw_embeddedLight(vec3 worldPos, out vec2 lightCoord);
#endif #endif
flat out uint _flw_instanceID; flat out uint _flw_instanceID;
@ -89,7 +89,7 @@ void _flw_main(in FlwInstance instance, in uint stableInstanceID) {
flw_vertexNormal = _flw_normalMatrix * flw_vertexNormal; flw_vertexNormal = _flw_normalMatrix * flw_vertexNormal;
vec2 embeddedLight; vec2 embeddedLight;
if (_flw_embeddedLight(flw_vertexPos, flw_vertexNormal, embeddedLight)) { if (_flw_embeddedLight(flw_vertexPos.xyz, embeddedLight)) {
flw_vertexLight = max(flw_vertexLight, embeddedLight); flw_vertexLight = max(flw_vertexLight, embeddedLight);
} }
#endif #endif

View file

@ -3,3 +3,5 @@
#define _FLW_MODEL_INDEX_BUFFER_BINDING 2 #define _FLW_MODEL_INDEX_BUFFER_BINDING 2
#define _FLW_MODEL_BUFFER_BINDING 3 #define _FLW_MODEL_BUFFER_BINDING 3
#define _FLW_DRAW_BUFFER_BINDING 4 #define _FLW_DRAW_BUFFER_BINDING 4
#define _FLW_EMBEDDING_LUT_BINDING 5
#define _FLW_EMBEDDING_LIGHT_BINDING 6

View file

@ -13,24 +13,33 @@ layout(std430, binding = _FLW_DRAW_BUFFER_BINDING) restrict readonly buffer Draw
#ifdef _FLW_EMBEDDED #ifdef _FLW_EMBEDDED
layout(std430, binding = 8) restrict readonly buffer EmbeddingLut { layout(std430, binding = _FLW_EMBEDDING_LUT_BINDING) restrict readonly buffer EmbeddingLut {
uint _flw_embeddingLut[]; uint _flw_embeddingLut[];
}; };
const uint _FLW_LIGHT_SECTION_SIZE_BYTES = 18 * 18 * 18; const uint _FLW_LIGHT_SECTION_SIZE_BYTES = 18 * 18 * 18;
const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4; const uint _FLW_LIGHT_SECTION_SIZE_INTS = _FLW_LIGHT_SECTION_SIZE_BYTES / 4;
layout(std430, binding = 9) restrict readonly buffer LightSections { layout(std430, binding = _FLW_EMBEDDING_LIGHT_BINDING) restrict readonly buffer LightSections {
uint _flw_lightSections[]; uint _flw_lightSections[];
}; };
/// Find the index for the next step in the LUT.
/// @param base The base index in the LUT, should point to the start of a coordinate span.
/// @param coord The coordinate to look for.
/// @param next Output. The index of the next step in the LUT.
/// @return true if the coordinate is not in the span.
bool _flw_nextLut(uint base, int coord, out uint next) { bool _flw_nextLut(uint base, int coord, out uint next) {
// The base coordinate.
int start = int(_flw_embeddingLut[base]); int start = int(_flw_embeddingLut[base]);
// The width of the coordinate span.
uint size = _flw_embeddingLut[base + 1]; uint size = _flw_embeddingLut[base + 1];
// Index of the coordinate in the span.
int i = coord - start; int i = coord - start;
if (i < 0 || i >= size) { if (i < 0 || i >= size) {
// We missed.
return true; return true;
} }
@ -52,25 +61,29 @@ bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) {
return _flw_nextLut(z, sectionPos.z, index); return _flw_nextLut(z, sectionPos.z, index);
} }
vec2 _flw_lightAt(uint sectionOffset, ivec3 blockInSectionPos) { vec2 _flw_lightAt(uint sectionOffset, uvec3 blockInSectionPos) {
uint byteOffset = blockInSectionPos.x + blockInSectionPos.z * 18u + blockInSectionPos.y * 18u * 18u; uint byteOffset = blockInSectionPos.x + blockInSectionPos.z * 18u + blockInSectionPos.y * 18u * 18u;
uint uintOffset = byteOffset >> 2u; uint uintOffset = byteOffset >> 2u;
uint bitOffset = (byteOffset & 3u) << 3; uint bitOffset = (byteOffset & 3u) << 3;
uint packed = _flw_lightSections[sectionOffset + uintOffset]; uint raw = _flw_lightSections[sectionOffset + uintOffset];
uint block = (packed >> bitOffset) & 0xFu; uint block = (raw >> bitOffset) & 0xFu;
uint sky = (packed >> (bitOffset + 4u)) & 0xFu; uint sky = (raw >> (bitOffset + 4u)) & 0xFu;
return vec2(block, sky); return vec2(block, sky);
} }
bool _flw_embeddedLight(vec3 worldPos, vec3 normal, out vec2 lightCoord) { bool _flw_embeddedLight(vec3 worldPos, out vec2 lightCoord) {
ivec3 blockPos = ivec3(floor(worldPos));
ivec3 sectionPos = blockPos >> 4; ivec3 sectionPos = blockPos >> 4;
uvec3 blockInSectionPos = (blockPos & 0xF) + 1; uvec3 blockInSectionPos = (blockPos & 0xF) + 1;
uint lightSectionIndex; uint lightSectionIndex;
if (_flw_chunkCoordToSectionIndex(sectionPos, lightSectionIndex)) { if (_flw_chunkCoordToSectionIndex(sectionPos, lightSectionIndex)) {
// TODO: useful debug mode for this.
// flw_vertexOverlay = ivec2(0, 3);
return false; return false;
} }

View file

@ -5,7 +5,7 @@ uniform uvec4 _flw_packedMaterial;
uniform int _flw_baseInstance = 0; uniform int _flw_baseInstance = 0;
#ifdef _FLW_EMBEDDED #ifdef _FLW_EMBEDDED
bool _flw_embeddedLight(vec3 worldPos, vec3 normal, out vec2 lightCoord) { bool _flw_embeddedLight(vec3 worldPos, out vec2 lightCoord) {
return true; return true;
} }
#endif #endif