Light clean up

- Ensure section set returned by SectionTracker is Unmodifiable to avoid copy in LightUpdatedVisualStorage
- Do not recompute section set in ShaderLightVisualStorage if not dirty
- Fix BlockEntityStorage not clearing posLookup on recreation or invalidation
- Fix Storage.invalidate not clearing everything
- Inline TopLevelEmbeddedEnvironment and NestedEmbeddedEnvironment into AbstractEmbeddedEnvironment and rename to EmbeddedEnvironment
- Move some classes between packages
- Remove unused fields in EmbeddingUniforms
- Remove suffix on field names in BufferBindings
- Rename enqueueLightUpdateSection methods to onLightUpdate
- Rename SectionCollectorImpl to SectionTracker
- Rename classes, methods, fields, and parameters and edit javadoc and comments to match previously done renames, new renames, and other existing classes
This commit is contained in:
PepperCode1 2024-07-15 15:36:52 -06:00 committed by Jozufozu
parent 1ea94a859e
commit fe0eacad8e
38 changed files with 364 additions and 372 deletions

View file

@ -62,13 +62,6 @@ public interface Engine {
*/ */
Vec3i renderOrigin(); Vec3i renderOrigin();
/**
* Free all resources associated with this engine.
* <br>
* This engine will not be used again after this method is called.
*/
void delete();
/** /**
* Assign the set of sections that visuals have requested GPU light for. * Assign the set of sections that visuals have requested GPU light for.
* *
@ -78,6 +71,13 @@ public interface Engine {
*/ */
void lightSections(LongSet sections); void lightSections(LongSet sections);
/**
* Free all resources associated with this engine.
* <br>
* This engine will not be used again after this method is called.
*/
void delete();
/** /**
* A block to be rendered as a crumbling overlay. * A block to be rendered as a crumbling overlay.
* @param progress The progress of the crumbling animation in the range [0, 10). * @param progress The progress of the crumbling animation in the range [0, 10).

View file

@ -9,7 +9,8 @@ package dev.engine_room.flywheel.api.visual;
*/ */
public non-sealed interface LightUpdatedVisual extends SectionTrackedVisual { public non-sealed interface LightUpdatedVisual extends SectionTrackedVisual {
/** /**
* Called when a section this visual is contained in receives a light update. * Called when a section this visual is contained in receives a light update. It is not called
* unconditionally after visual creation or {@link SectionCollector#sections}.
* *
* <p>Even if multiple sections are updated at the same time, this method will only be called once.</p> * <p>Even if multiple sections are updated at the same time, this method will only be called once.</p>
* *
@ -17,8 +18,6 @@ public non-sealed interface LightUpdatedVisual extends SectionTrackedVisual {
* returned by {@link DynamicVisual#planFrame} simultaneously. It is safe to query/update light here, * returned by {@link DynamicVisual#planFrame} simultaneously. It is safe to query/update light here,
* but you must ensure proper synchronization if you want to mutate anything outside this visual or * but you must ensure proper synchronization if you want to mutate anything outside this visual or
* anything that is also mutated within {@link DynamicVisual#planFrame}.</p> * anything that is also mutated within {@link DynamicVisual#planFrame}.</p>
*
* <p>This method not is invoked automatically after visual creation.</p>
*/ */
void updateLight(float partialTick); void updateLight(float partialTick);
} }

View file

@ -6,15 +6,15 @@ import it.unimi.dsi.fastutil.longs.LongSet;
public sealed interface SectionTrackedVisual extends Visual permits ShaderLightVisual, LightUpdatedVisual { public sealed interface SectionTrackedVisual extends Visual permits ShaderLightVisual, LightUpdatedVisual {
/** /**
* Set the section property object. * Set the section collector object.
* *
* <p>This method is only called once, upon visual creation. * <p>This method is only called once, upon visual creation.
* <br>If the property is assigned to in this method, the * <br>If the collector is invoked in this method, the
* visual will immediately be tracked in the given sections. * visual will immediately be tracked in the given sections.
* *
* @param property The property. * @param collector The collector.
*/ */
void setSectionCollector(SectionCollector property); void setSectionCollector(SectionCollector collector);
@ApiStatus.NonExtendable @ApiStatus.NonExtendable
interface SectionCollector { interface SectionCollector {

View file

@ -6,6 +6,7 @@ package dev.engine_room.flywheel.api.visual;
* @see DynamicVisual * @see DynamicVisual
* @see TickableVisual * @see TickableVisual
* @see LightUpdatedVisual * @see LightUpdatedVisual
* @see ShaderLightVisual
*/ */
public interface Visual { public interface Visual {
/** /**

View file

@ -1,7 +1,8 @@
package dev.engine_room.flywheel.backend.engine.embed; package dev.engine_room.flywheel.backend;
import dev.engine_room.flywheel.lib.util.LevelAttached; import dev.engine_room.flywheel.lib.util.LevelAttached;
import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
@ -11,12 +12,15 @@ import net.minecraft.world.level.LevelAccessor;
public class LightUpdateHolder { public class LightUpdateHolder {
private static final LevelAttached<LightUpdateHolder> HOLDERS = new LevelAttached<>(level -> new LightUpdateHolder()); private static final LevelAttached<LightUpdateHolder> HOLDERS = new LevelAttached<>(level -> new LightUpdateHolder());
private final LongSet updatedSections = new LongOpenHashSet();
private LightUpdateHolder() {
}
public static LightUpdateHolder get(LevelAccessor level) { public static LightUpdateHolder get(LevelAccessor level) {
return HOLDERS.get(level); return HOLDERS.get(level);
} }
private final LongSet updatedSections = new LongArraySet();
public LongSet getAndClearUpdatedSections() { public LongSet getAndClearUpdatedSections() {
if (updatedSections.isEmpty()) { if (updatedSections.isEmpty()) {
return LongSet.of(); return LongSet.of();
@ -30,7 +34,4 @@ public class LightUpdateHolder {
public void add(long section) { public void add(long section) {
updatedSections.add(section); updatedSections.add(section);
} }
private LightUpdateHolder() {
}
} }

View file

@ -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 = " + BufferBindings.INSTANCE_BUFFER_BINDING + ") restrict readonly buffer InstanceBuffer {\n" builder._addRaw("layout(std430, binding = " + BufferBindings.INSTANCE + ") restrict readonly buffer InstanceBuffer {\n"
+ " uint _flw_instances[];\n" + " uint _flw_instances[];\n"
+ "};"); + "};");
builder.blankLine(); builder.blankLine();

View file

@ -1,4 +1,4 @@
package dev.engine_room.flywheel.backend.engine.embed; package dev.engine_room.flywheel.backend.engine;
import dev.engine_room.flywheel.lib.memory.MemoryBlock; import dev.engine_room.flywheel.lib.memory.MemoryBlock;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;

View file

@ -16,7 +16,6 @@ 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;

View file

@ -14,9 +14,9 @@ import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.api.task.TaskExecutor; import dev.engine_room.flywheel.api.task.TaskExecutor;
import dev.engine_room.flywheel.api.visualization.VisualEmbedding; import dev.engine_room.flywheel.api.visualization.VisualEmbedding;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.backend.engine.embed.EmbeddedEnvironment;
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.backend.engine.embed.EnvironmentStorage;
import dev.engine_room.flywheel.backend.engine.embed.TopLevelEmbeddedEnvironment;
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.lib.task.Flag; import dev.engine_room.flywheel.lib.task.Flag;
@ -30,11 +30,11 @@ import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class EngineImpl implements Engine { public class EngineImpl implements Engine {
private final int sqrMaxOriginDistance;
private final DrawManager<? extends AbstractInstancer<?>> drawManager; private final DrawManager<? extends AbstractInstancer<?>> drawManager;
private final int sqrMaxOriginDistance;
private final Flag flushFlag = new NamedFlag("flushed");
private final EnvironmentStorage environmentStorage; private final EnvironmentStorage environmentStorage;
private final LightStorage lightStorage; private final LightStorage lightStorage;
private final Flag flushFlag = new NamedFlag("flushed");
private BlockPos renderOrigin = BlockPos.ZERO; private BlockPos renderOrigin = BlockPos.ZERO;
@ -45,6 +45,11 @@ public class EngineImpl implements Engine {
lightStorage = new LightStorage(level); lightStorage = new LightStorage(level);
} }
@Override
public VisualizationContext createVisualizationContext(RenderStage stage) {
return new VisualizationContextImpl(stage);
}
@Override @Override
public Plan<RenderContext> createFramePlan() { public Plan<RenderContext> createFramePlan() {
return lightStorage.createFramePlan() return lightStorage.createFramePlan()
@ -69,11 +74,6 @@ public class EngineImpl implements Engine {
drawManager.renderCrumbling(crumblingBlocks); drawManager.renderCrumbling(crumblingBlocks);
} }
@Override
public VisualizationContext createVisualizationContext(RenderStage stage) {
return new VisualizationContextImpl(stage);
}
@Override @Override
public boolean updateRenderOrigin(Camera camera) { public boolean updateRenderOrigin(Camera camera) {
Vec3 cameraPos = camera.getPosition(); Vec3 cameraPos = camera.getPosition();
@ -97,14 +97,14 @@ public class EngineImpl implements Engine {
} }
@Override @Override
public void delete() { public void lightSections(LongSet sections) {
drawManager.delete(); lightStorage.sections(sections);
lightStorage.delete();
} }
@Override @Override
public void lightSections(LongSet sections) { public void delete() {
lightStorage.sections(sections); drawManager.delete();
lightStorage.delete();
} }
public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, RenderStage stage) { public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, RenderStage stage) {
@ -150,7 +150,7 @@ public class EngineImpl implements Engine {
@Override @Override
public VisualEmbedding createEmbedding() { public VisualEmbedding createEmbedding() {
var out = new TopLevelEmbeddedEnvironment(EngineImpl.this, stage); var out = new EmbeddedEnvironment(EngineImpl.this, stage);
environmentStorage.track(out); environmentStorage.track(out);
return out; return out;
} }

View file

@ -1,19 +0,0 @@
package dev.engine_room.flywheel.backend.engine;
import dev.engine_room.flywheel.backend.engine.embed.AbstractEmbeddedEnvironment;
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.dsi.fastutil.objects.ReferenceSets;
public class EnvironmentStorage {
protected final ReferenceSet<AbstractEmbeddedEnvironment> environments = ReferenceSets.synchronize(new ReferenceLinkedOpenHashSet<>());
public void track(AbstractEmbeddedEnvironment environment) {
environments.add(environment);
}
public void flush() {
environments.removeIf(AbstractEmbeddedEnvironment::isDeleted);
environments.forEach(AbstractEmbeddedEnvironment::flush);
}
}

View file

@ -1,4 +1,4 @@
package dev.engine_room.flywheel.backend.engine.embed; package dev.engine_room.flywheel.backend.engine;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -11,8 +11,8 @@ import it.unimi.dsi.fastutil.longs.LongComparator;
import it.unimi.dsi.fastutil.objects.ReferenceArrayList; import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
public class LightLut { public final class LightLut {
public static final LongComparator SECTION_X_THEN_Y_THEN_Z = (long a, long b) -> { private static final LongComparator SECTION_X_THEN_Y_THEN_Z = (long a, long b) -> {
final var xComp = Integer.compare(SectionPos.x(a), SectionPos.x(b)); final var xComp = Integer.compare(SectionPos.x(a), SectionPos.x(b));
if (xComp != 0) { if (xComp != 0) {
return xComp; return xComp;
@ -24,6 +24,8 @@ public class LightLut {
return Integer.compare(SectionPos.z(a), SectionPos.z(b)); return Integer.compare(SectionPos.z(a), SectionPos.z(b));
}; };
private LightLut() {
}
// Massive kudos to RogueLogix for figuring out this LUT scheme. // Massive kudos to RogueLogix for figuring out this LUT scheme.
// TODO: switch to y x z or x z y ordering // TODO: switch to y x z or x z y ordering

View file

@ -1,4 +1,4 @@
package dev.engine_room.flywheel.backend.engine.embed; package dev.engine_room.flywheel.backend.engine;
import java.util.BitSet; import java.util.BitSet;
@ -7,6 +7,7 @@ import org.lwjgl.system.MemoryUtil;
import dev.engine_room.flywheel.api.event.RenderContext; import dev.engine_room.flywheel.api.event.RenderContext;
import dev.engine_room.flywheel.api.task.Plan; import dev.engine_room.flywheel.api.task.Plan;
import dev.engine_room.flywheel.backend.LightUpdateHolder;
import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer; import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer;
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer; import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
import dev.engine_room.flywheel.lib.task.SimplePlan; import dev.engine_room.flywheel.lib.task.SimplePlan;

View file

@ -1,5 +1,6 @@
package dev.engine_room.flywheel.backend.engine.embed; package dev.engine_room.flywheel.backend.engine.embed;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f; import org.joml.Matrix3f;
import org.joml.Matrix3fc; import org.joml.Matrix3fc;
import org.joml.Matrix4f; import org.joml.Matrix4f;
@ -17,61 +18,44 @@ 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 net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
public abstract class AbstractEmbeddedEnvironment implements Environment, VisualEmbedding { public class EmbeddedEnvironment implements VisualEmbedding, Environment {
protected final Matrix4f pose = new Matrix4f(); private final EngineImpl engine;
protected final Matrix3f normal = new Matrix3f(); private final RenderStage renderStage;
@Nullable
private final EmbeddedEnvironment parent;
private final InstancerProvider instancerProvider;
private final Matrix4f pose = new Matrix4f();
private final Matrix3f normal = new Matrix3f();
private final Matrix4f poseComposed = new Matrix4f(); private final Matrix4f poseComposed = new Matrix4f();
private final Matrix3f normalComposed = new Matrix3f(); private final Matrix3f normalComposed = new Matrix3f();
private final InstancerProvider instancerProvider;
protected final EngineImpl engine;
private final RenderStage renderStage;
private boolean deleted = false; private boolean deleted = false;
public AbstractEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) { public EmbeddedEnvironment(EngineImpl engine, RenderStage renderStage, @Nullable EmbeddedEnvironment parent) {
this.engine = engine; this.engine = engine;
this.renderStage = renderStage; this.renderStage = renderStage;
this.parent = parent;
instancerProvider = new InstancerProvider() { instancerProvider = new InstancerProvider() {
@Override @Override
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) { public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
// Kinda cursed usage of anonymous classes here, but it does the job. // Kinda cursed usage of anonymous classes here, but it does the job.
return engine.instancer(AbstractEmbeddedEnvironment.this, type, model, renderStage); return engine.instancer(EmbeddedEnvironment.this, type, model, renderStage);
} }
}; };
} }
public EmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) {
this(engine, renderStage, null);
}
@Override @Override
public void transforms(Matrix4fc pose, Matrix3fc normal) { public void transforms(Matrix4fc pose, Matrix3fc normal) {
this.pose.set(pose); this.pose.set(pose);
this.normal.set(normal); this.normal.set(normal);
} }
public void flush() {
poseComposed.identity();
normalComposed.identity();
composeMatrices(poseComposed, normalComposed);
}
@Override
public void setupDraw(GlProgram program) {
program.setMat4(EmbeddingUniforms.MODEL_MATRIX, poseComposed);
program.setMat3(EmbeddingUniforms.NORMAL_MATRIX, normalComposed);
}
@Override
public void setupCull(GlProgram program) {
program.setBool(EmbeddingUniforms.USE_MODEL_MATRIX, true);
program.setMat4(EmbeddingUniforms.MODEL_MATRIX1, poseComposed);
}
@Override
public ContextShader contextShader() {
return ContextShader.EMBEDDED;
}
@Override @Override
public InstancerProvider instancerProvider() { public InstancerProvider instancerProvider() {
return instancerProvider; return instancerProvider;
@ -84,12 +68,47 @@ public abstract class AbstractEmbeddedEnvironment implements Environment, Visual
@Override @Override
public VisualEmbedding createEmbedding() { public VisualEmbedding createEmbedding() {
var out = new NestedEmbeddedEnvironment(this, engine, renderStage); var out = new EmbeddedEnvironment(engine, renderStage, this);
engine.environmentStorage() engine.environmentStorage()
.track(out); .track(out);
return out; return out;
} }
@Override
public ContextShader contextShader() {
return ContextShader.EMBEDDED;
}
@Override
public void setupCull(GlProgram program) {
program.setBool(EmbeddingUniforms.USE_MODEL_MATRIX, true);
program.setMat4(EmbeddingUniforms.MODEL_MATRIX, poseComposed);
}
@Override
public void setupDraw(GlProgram program) {
program.setMat4(EmbeddingUniforms.MODEL_MATRIX, poseComposed);
program.setMat3(EmbeddingUniforms.NORMAL_MATRIX, normalComposed);
}
public void flush() {
poseComposed.identity();
normalComposed.identity();
composeMatrices(poseComposed, normalComposed);
}
private void composeMatrices(Matrix4f pose, Matrix3f normal) {
if (parent != null) {
parent.composeMatrices(pose, normal);
pose.mul(this.pose);
normal.mul(this.normal);
} else {
pose.set(this.pose);
normal.set(this.normal);
}
}
public boolean isDeleted() { public boolean isDeleted() {
return deleted; return deleted;
} }
@ -101,6 +120,4 @@ public abstract class AbstractEmbeddedEnvironment implements Environment, Visual
public void delete() { public void delete() {
deleted = true; deleted = true;
} }
public abstract void composeMatrices(Matrix4f pose, Matrix3f normal);
} }

View file

@ -1,11 +1,13 @@
package dev.engine_room.flywheel.backend.engine.embed; package dev.engine_room.flywheel.backend.engine.embed;
public class EmbeddingUniforms { public final class EmbeddingUniforms {
/**
* Only used by cull shaders.
*/
public static final String USE_MODEL_MATRIX = "_flw_useModelMatrix";
public static final String MODEL_MATRIX = "_flw_modelMatrix"; public static final String MODEL_MATRIX = "_flw_modelMatrix";
public static final String NORMAL_MATRIX = "_flw_normalMatrix"; public static final String NORMAL_MATRIX = "_flw_normalMatrix";
public static final String USE_MODEL_MATRIX = "_flw_useModelMatrix";
public static final String MODEL_MATRIX1 = "_flw_modelMatrix"; private EmbeddingUniforms() {
public static final String ONE_OVER_LIGHT_BOX_SIZE = "_flw_oneOverLightBoxSize"; }
public static final String LIGHT_VOLUME_MIN = "_flw_lightVolumeMin";
public static final String USE_LIGHT_VOLUME = "_flw_useLightVolume";
} }

View file

@ -6,7 +6,7 @@ import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
public interface Environment { public interface Environment {
ContextShader contextShader(); ContextShader contextShader();
void setupDraw(GlProgram drawProgram);
void setupCull(GlProgram cullProgram); void setupCull(GlProgram cullProgram);
void setupDraw(GlProgram drawProgram);
} }

View file

@ -0,0 +1,18 @@
package dev.engine_room.flywheel.backend.engine.embed;
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.dsi.fastutil.objects.ReferenceSets;
public class EnvironmentStorage {
protected final ReferenceSet<EmbeddedEnvironment> environments = ReferenceSets.synchronize(new ReferenceLinkedOpenHashSet<>());
public void track(EmbeddedEnvironment environment) {
environments.add(environment);
}
public void flush() {
environments.removeIf(EmbeddedEnvironment::isDeleted);
environments.forEach(EmbeddedEnvironment::flush);
}
}

View file

@ -14,13 +14,12 @@ public class GlobalEnvironment implements Environment {
return ContextShader.DEFAULT; return ContextShader.DEFAULT;
} }
@Override
public void setupDraw(GlProgram drawProgram) {
}
@Override @Override
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 setupDraw(GlProgram drawProgram) {
}
} }

View file

@ -1,23 +0,0 @@
package dev.engine_room.flywheel.backend.engine.embed;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import dev.engine_room.flywheel.api.event.RenderStage;
import dev.engine_room.flywheel.backend.engine.EngineImpl;
public class NestedEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
private final AbstractEmbeddedEnvironment parent;
public NestedEmbeddedEnvironment(AbstractEmbeddedEnvironment parent, EngineImpl engine, RenderStage renderStage) {
super(engine, renderStage);
this.parent = parent;
}
@Override
public void composeMatrices(Matrix4f pose, Matrix3f normal) {
parent.composeMatrices(pose, normal);
pose.mul(this.pose);
normal.mul(this.normal);
}
}

View file

@ -1,19 +0,0 @@
package dev.engine_room.flywheel.backend.engine.embed;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import dev.engine_room.flywheel.api.event.RenderStage;
import dev.engine_room.flywheel.backend.engine.EngineImpl;
public class TopLevelEmbeddedEnvironment extends AbstractEmbeddedEnvironment {
public TopLevelEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) {
super(engine, renderStage);
}
@Override
public void composeMatrices(Matrix4f pose, Matrix3f normal) {
pose.set(this.pose);
normal.set(this.normal);
}
}

View file

@ -1,13 +1,13 @@
package dev.engine_room.flywheel.backend.engine.indirect; package dev.engine_room.flywheel.backend.engine.indirect;
public class BufferBindings { public final class BufferBindings {
public static final int INSTANCE_BUFFER_BINDING = 0; public static final int INSTANCE = 0;
public static final int TARGET_BUFFER_BINDING = 1; public static final int TARGET = 1;
public static final int MODEL_INDEX_BUFFER_BINDING = 2; public static final int MODEL_INDEX = 2;
public static final int MODEL_BUFFER_BINDING = 3; public static final int MODEL = 3;
public static final int DRAW_BUFFER_BINDING = 4; public static final int DRAW = 4;
public static final int LIGHT_LUT_BINDING = 5; public static final int LIGHT_LUT = 5;
public static final int LIGHT_SECTION_BINDING = 6; public static final int LIGHT_SECTION = 6;
private BufferBindings() { private BufferBindings() {
} }

View file

@ -110,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, BufferBindings.INSTANCE_BUFFER_BINDING, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET); nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.INSTANCE, IndirectBuffers.BUFFER_COUNT, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
} }
/** /**
@ -118,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, BufferBindings.INSTANCE_BUFFER_BINDING, 4, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET); nglBindBuffersRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.INSTANCE, 4, ptr, ptr + OFFSET_OFFSET, ptr + SIZE_OFFSET);
} }
public void delete() { public void delete() {

View file

@ -20,10 +20,10 @@ import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
import dev.engine_room.flywheel.backend.engine.DrawManager; import dev.engine_room.flywheel.backend.engine.DrawManager;
import dev.engine_room.flywheel.backend.engine.GroupKey; import dev.engine_room.flywheel.backend.engine.GroupKey;
import dev.engine_room.flywheel.backend.engine.InstancerKey; import dev.engine_room.flywheel.backend.engine.InstancerKey;
import dev.engine_room.flywheel.backend.engine.LightStorage;
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,18 +40,15 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
private final GlVertexArray vertexArray; private final GlVertexArray vertexArray;
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; private final LightBuffers lightBuffers;
public IndirectDrawManager(IndirectPrograms programs) { public IndirectDrawManager(IndirectPrograms programs) {
this.programs = programs; this.programs = programs;
programs.acquire(); programs.acquire();
stagingBuffer = new StagingBuffer(this.programs); stagingBuffer = new StagingBuffer(this.programs);
meshPool = new MeshPool(); meshPool = new MeshPool();
vertexArray = GlVertexArray.create(); vertexArray = GlVertexArray.create();
meshPool.bind(vertexArray); meshPool.bind(vertexArray);
lightBuffers = new LightBuffers(); lightBuffers = new LightBuffers();
} }
@ -170,7 +167,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, BufferBindings.DRAW_BUFFER_BINDING, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, BufferBindings.DRAW, 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

@ -3,14 +3,11 @@ package dev.engine_room.flywheel.backend.engine.indirect;
import org.lwjgl.opengl.GL46; 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.LightStorage;
public class LightBuffers { public class LightBuffers {
private final ResizableStorageArray sections = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
private final ResizableStorageArray lut = new ResizableStorageArray(4); private final ResizableStorageArray lut = new ResizableStorageArray(4);
private final ResizableStorageArray sections = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
public LightBuffers() {
}
public void flush(StagingBuffer staging, LightStorage light) { public void flush(StagingBuffer staging, LightStorage light) {
var capacity = light.capacity(); var capacity = light.capacity();
@ -40,7 +37,7 @@ public class LightBuffers {
return; return;
} }
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.LIGHT_LUT_BINDING, lut.handle(), 0, lut.byteCapacity()); GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.LIGHT_LUT, lut.handle(), 0, lut.byteCapacity());
GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.LIGHT_SECTION_BINDING, sections.handle(), 0, sections.byteCapacity()); GL46.glBindBufferRange(GL46.GL_SHADER_STORAGE_BUFFER, BufferBindings.LIGHT_SECTION, sections.handle(), 0, sections.byteCapacity());
} }
} }

View file

@ -18,11 +18,11 @@ import dev.engine_room.flywheel.backend.engine.CommonCrumbling;
import dev.engine_room.flywheel.backend.engine.DrawManager; import dev.engine_room.flywheel.backend.engine.DrawManager;
import dev.engine_room.flywheel.backend.engine.GroupKey; import dev.engine_room.flywheel.backend.engine.GroupKey;
import dev.engine_room.flywheel.backend.engine.InstancerKey; import dev.engine_room.flywheel.backend.engine.InstancerKey;
import dev.engine_room.flywheel.backend.engine.LightStorage;
import dev.engine_room.flywheel.backend.engine.MaterialEncoder; 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;

View file

@ -4,7 +4,7 @@ import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import dev.engine_room.flywheel.backend.Samplers; import dev.engine_room.flywheel.backend.Samplers;
import dev.engine_room.flywheel.backend.engine.embed.LightStorage; import dev.engine_room.flywheel.backend.engine.LightStorage;
import dev.engine_room.flywheel.backend.gl.TextureBuffer; import dev.engine_room.flywheel.backend.gl.TextureBuffer;
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer; import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
import dev.engine_room.flywheel.lib.memory.MemoryBlock; import dev.engine_room.flywheel.lib.memory.MemoryBlock;
@ -16,8 +16,8 @@ public class InstancedLight {
private final TextureBuffer sectionsTexture; private final TextureBuffer sectionsTexture;
public InstancedLight() { public InstancedLight() {
sections = new GlBuffer();
lut = new GlBuffer(); lut = new GlBuffer();
sections = new GlBuffer();
lutTexture = new TextureBuffer(GL32.GL_R32UI); lutTexture = new TextureBuffer(GL32.GL_R32UI);
sectionsTexture = new TextureBuffer(GL32.GL_R32UI); sectionsTexture = new TextureBuffer(GL32.GL_R32UI);
} }
@ -54,8 +54,8 @@ public class InstancedLight {
} }
public void delete() { public void delete() {
sections.delete();
lut.delete(); lut.delete();
sections.delete();
lutTexture.delete(); lutTexture.delete();
sectionsTexture.delete(); sectionsTexture.delete();
} }

View file

@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import dev.engine_room.flywheel.backend.engine.embed.LightUpdateHolder; import dev.engine_room.flywheel.backend.LightUpdateHolder;
import net.minecraft.client.multiplayer.ClientChunkCache; import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;

View file

@ -3,5 +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_LIGHT_LUT_BINDING 5 #define _FLW_LIGHT_LUT_BUFFER_BINDING 5
#define _FLW_LIGHT_SECTIONS_BINDING 6 #define _FLW_LIGHT_SECTIONS_BUFFER_BINDING 6

View file

@ -1,10 +1,10 @@
#include "flywheel:internal/light_lut.glsl" #include "flywheel:internal/light_lut.glsl"
layout(std430, binding = _FLW_LIGHT_LUT_BINDING) restrict readonly buffer LightLut { layout(std430, binding = _FLW_LIGHT_LUT_BUFFER_BINDING) restrict readonly buffer LightLut {
uint _flw_lightLut[]; uint _flw_lightLut[];
}; };
layout(std430, binding = _FLW_LIGHT_SECTIONS_BINDING) restrict readonly buffer LightSections { layout(std430, binding = _FLW_LIGHT_SECTIONS_BUFFER_BINDING) restrict readonly buffer LightSections {
uint _flw_lightSections[]; uint _flw_lightSections[];
}; };

View file

@ -24,7 +24,7 @@ abstract class ClientChunkCacheMixin {
var manager = VisualizationManagerImpl.get(level); var manager = VisualizationManagerImpl.get(level);
if (manager != null) { if (manager != null) {
manager.enqueueLightUpdateSection(pos.asLong()); manager.onLightUpdate(pos.asLong());
} }
} }
} }

View file

@ -106,11 +106,11 @@ public class VisualizationManagerImpl implements VisualizationManager {
.ifFalse(update) .ifFalse(update)
.plan() .plan()
.then(SimplePlan.of(() -> { .then(SimplePlan.of(() -> {
if (blockEntities.lightSectionsDirty() || entities.lightSectionsDirty() || effects.lightSectionsDirty()) { if (blockEntities.areGpuLightSectionsDirty() || entities.areGpuLightSectionsDirty() || effects.areGpuLightSectionsDirty()) {
var out = new LongOpenHashSet(); var out = new LongOpenHashSet();
out.addAll(blockEntities.lightSections()); out.addAll(blockEntities.gpuLightSections());
out.addAll(entities.lightSections()); out.addAll(entities.gpuLightSections());
out.addAll(effects.lightSections()); out.addAll(effects.gpuLightSections());
engine.lightSections(out); engine.lightSections(out);
} }
})) }))
@ -302,6 +302,12 @@ public class VisualizationManagerImpl implements VisualizationManager {
} }
} }
public void onLightUpdate(long section) {
blockEntities.onLightUpdate(section);
entities.onLightUpdate(section);
effects.onLightUpdate(section);
}
/** /**
* Free all acquired resources and delete this manager. * Free all acquired resources and delete this manager.
*/ */
@ -315,13 +321,4 @@ public class VisualizationManagerImpl implements VisualizationManager {
effects.invalidate(); effects.invalidate();
engine.delete(); engine.delete();
} }
public void enqueueLightUpdateSection(long section) {
blockEntities.getStorage()
.enqueueLightUpdateSection(section);
entities.getStorage()
.enqueueLightUpdateSection(section);
effects.getStorage()
.enqueueLightUpdateSection(section);
}
} }

View file

@ -69,8 +69,20 @@ public class BlockEntityStorage extends Storage<BlockEntity> {
@Override @Override
public void remove(BlockEntity obj) { public void remove(BlockEntity obj) {
super.remove(obj);
posLookup.remove(obj.getBlockPos() posLookup.remove(obj.getBlockPos()
.asLong()); .asLong());
super.remove(obj);
}
@Override
public void recreateAll(float partialTick) {
posLookup.clear();
super.recreateAll(partialTick);
}
@Override
public void invalidate() {
posLookup.clear();
super.invalidate();
} }
} }

View file

@ -54,10 +54,6 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
queue.add(Transaction.update(obj)); queue.add(Transaction.update(obj));
} }
public void invalidate() {
getStorage().invalidate();
}
public void processQueue(float partialTick) { public void processQueue(float partialTick) {
var storage = getStorage(); var storage = getStorage();
Transaction<T> transaction; Transaction<T> transaction;
@ -76,13 +72,22 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
.then(storage.tickPlan()); .then(storage.tickPlan());
} }
public boolean lightSectionsDirty() { public void onLightUpdate(long section) {
return getStorage().smoothLitStorage() getStorage().lightUpdatedVisuals()
.sectionsDirty(); .onLightUpdate(section);
} }
public LongSet lightSections() { public boolean areGpuLightSectionsDirty() {
return getStorage().smoothLitStorage() return getStorage().shaderLightVisuals()
.isDirty();
}
public LongSet gpuLightSections() {
return getStorage().shaderLightVisuals()
.sections(); .sections();
} }
public void invalidate() {
getStorage().invalidate();
}
} }

View file

@ -16,7 +16,6 @@ import dev.engine_room.flywheel.lib.task.SimplyComposedPlan;
import dev.engine_room.flywheel.lib.task.Synchronizer; import dev.engine_room.flywheel.lib.task.Synchronizer;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@ -24,15 +23,15 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
/** /**
* Keeps track of what chunks/sections each listener is in, so we can update exactly what needs to be updated. * Keeps track of what chunks/sections each listener is in, so we can update exactly what needs to be updated.
*/ */
public class LightUpdatedStorage { public class LightUpdatedVisualStorage {
private static final long NEVER_UPDATED = Long.MIN_VALUE; private static final long NEVER_UPDATED = Long.MIN_VALUE;
private static final long INITIAL_UPDATE_ID = NEVER_UPDATED + 1; private static final long INITIAL_UPDATE_ID = NEVER_UPDATED + 1;
private final Map<LightUpdatedVisual, LongSet> visuals2Sections = new WeakHashMap<>(); private final Map<LightUpdatedVisual, LongSet> visual2Sections = new WeakHashMap<>();
private final Long2ObjectMap<List<Updater>> sections2Visuals = new Long2ObjectOpenHashMap<>(); private final Long2ObjectMap<List<Updater>> section2Updaters = new Long2ObjectOpenHashMap<>();
private final Queue<MovedVisual> movedVisuals = new ConcurrentLinkedQueue<>();
private final LongSet sectionsUpdatedThisFrame = new LongOpenHashSet(); private final LongSet sectionsUpdatedThisFrame = new LongOpenHashSet();
private final Queue<MovedVisual> movedVisuals = new ConcurrentLinkedQueue<>();
private long updateId = INITIAL_UPDATE_ID; private long updateId = INITIAL_UPDATE_ID;
@ -54,9 +53,9 @@ public class LightUpdatedStorage {
Updater.Context updaterContext = new Updater.Context(updateId, context.partialTick()); Updater.Context updaterContext = new Updater.Context(updateId, context.partialTick());
for (long section : sectionsUpdatedThisFrame) { for (long section : sectionsUpdatedThisFrame) {
var visuals = sections2Visuals.get(section); var updaters = section2Updaters.get(section);
if (visuals != null && !visuals.isEmpty()) { if (updaters != null && !updaters.isEmpty()) {
taskExecutor.execute(() -> Distribute.tasks(taskExecutor, updaterContext, sync, visuals, Updater::updateLight)); taskExecutor.execute(() -> Distribute.tasks(taskExecutor, updaterContext, sync, updaters, Updater::updateLight));
} else { } else {
sync.decrementAndEventuallyRun(); sync.decrementAndEventuallyRun();
} }
@ -69,7 +68,7 @@ public class LightUpdatedStorage {
while ((moved = movedVisuals.poll()) != null) { while ((moved = movedVisuals.poll()) != null) {
// If the visual isn't there when we try to remove it that means it was deleted before we got to it. // If the visual isn't there when we try to remove it that means it was deleted before we got to it.
if (remove(moved.visual)) { if (remove(moved.visual)) {
updateTracking(moved.tracker, moved.visual); addInner(moved.visual, moved.tracker);
} }
} }
} }
@ -86,43 +85,32 @@ public class LightUpdatedStorage {
return out; return out;
} }
public boolean isEmpty() { public void add(LightUpdatedVisual visual, SectionTracker tracker) {
return visuals2Sections.isEmpty(); var moved = new MovedVisual(visual, tracker);
}
public void add(SectionCollectorImpl tracker, LightUpdatedVisual visual) {
var moved = new MovedVisual(tracker, visual);
tracker.addListener(() -> movedVisuals.add(moved)); tracker.addListener(() -> movedVisuals.add(moved));
updateTracking(tracker, visual); addInner(visual, tracker);
} }
public void updateTracking(SectionCollectorImpl tracker, LightUpdatedVisual visual) { private void addInner(LightUpdatedVisual visual, SectionTracker tracker) {
if (tracker.sections.isEmpty()) { if (tracker.sections().isEmpty()) {
// Add the visual to the map even if sections is empty, this way we can distinguish from deleted visuals // Add the visual to the map even if sections is empty, this way we can distinguish from deleted visuals
visuals2Sections.put(visual, LongSet.of()); visual2Sections.put(visual, LongSet.of());
// Don't bother creating an updater if the visual isn't in any sections. // Don't bother creating an updater if the visual isn't in any sections.
return; return;
} }
// Create a copy of the array, so we know what section to remove the visual from later. var sections = tracker.sections();
var sections = new LongArraySet(tracker.sections); visual2Sections.put(visual, sections);
visuals2Sections.put(visual, sections);
var updater = createUpdater(visual, sections.size()); var updater = createUpdater(visual, sections.size());
for (long section : sections) { for (long section : sections) {
sections2Visuals.computeIfAbsent(section, $ -> new ObjectArrayList<>()) section2Updaters.computeIfAbsent(section, $ -> new ObjectArrayList<>())
.add(updater); .add(updater);
} }
} }
public void enqueueLightUpdateSection(long section) {
sectionsUpdatedThisFrame.add(section);
}
/** /**
* Remove the visual from this storage. * Remove the visual from this storage.
* *
@ -130,31 +118,36 @@ public class LightUpdatedStorage {
* @return {@code true} if the visual was removed, {@code false} otherwise. * @return {@code true} if the visual was removed, {@code false} otherwise.
*/ */
public boolean remove(LightUpdatedVisual visual) { public boolean remove(LightUpdatedVisual visual) {
var sections = visuals2Sections.remove(visual); var sections = visual2Sections.remove(visual);
if (sections == null) { if (sections == null) {
return false; return false;
} }
for (long section : sections) { for (long section : sections) {
List<Updater> listeners = sections2Visuals.get(section); List<Updater> updaters = section2Updaters.get(section);
if (listeners != null) { if (updaters != null) {
listeners.remove(indexOfUpdater(listeners, visual)); updaters.remove(indexOfUpdater(updaters, visual));
} }
} }
return true; return true;
} }
public void clear() { public void onLightUpdate(long section) {
visuals2Sections.clear(); sectionsUpdatedThisFrame.add(section);
sections2Visuals.clear();
sectionsUpdatedThisFrame.clear();
} }
private static int indexOfUpdater(List<Updater> listeners, LightUpdatedVisual visual) { public void clear() {
for (int i = 0; i < listeners.size(); i++) { visual2Sections.clear();
if (listeners.get(i) section2Updaters.clear();
sectionsUpdatedThisFrame.clear();
movedVisuals.clear();
}
private static int indexOfUpdater(List<Updater> updaters, LightUpdatedVisual visual) {
for (int i = 0; i < updaters.size(); i++) {
if (updaters.get(i)
.visual() == visual) { .visual() == visual) {
return i; return i;
} }
@ -171,7 +164,7 @@ public class LightUpdatedStorage {
} }
// Breaking this into 2 separate cases allows us to avoid the overhead of atomics in the common case. // Breaking this into 2 separate cases allows us to avoid the overhead of atomics in the common case.
sealed interface Updater { private sealed interface Updater {
void updateLight(Context ctx); void updateLight(Context ctx);
LightUpdatedVisual visual(); LightUpdatedVisual visual();
@ -201,6 +194,6 @@ public class LightUpdatedStorage {
} }
} }
private record MovedVisual(SectionCollectorImpl tracker, LightUpdatedVisual visual) { private record MovedVisual(LightUpdatedVisual visual, SectionTracker tracker) {
} }
} }

View file

@ -3,20 +3,27 @@ package dev.engine_room.flywheel.impl.visualization.storage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.Unmodifiable;
import dev.engine_room.flywheel.api.visual.SectionTrackedVisual; import dev.engine_room.flywheel.api.visual.SectionTrackedVisual;
import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.longs.LongSets;
public class SectionCollectorImpl implements SectionTrackedVisual.SectionCollector { public class SectionTracker implements SectionTrackedVisual.SectionCollector {
public final LongSet sections = new LongArraySet();
private final List<Runnable> listeners = new ArrayList<>(2); private final List<Runnable> listeners = new ArrayList<>(2);
@Unmodifiable
private LongSet sections = LongSet.of();
@Unmodifiable
public LongSet sections() {
return sections;
}
@Override @Override
public void sections(LongSet sections) { public void sections(LongSet sections) {
this.sections.clear(); this.sections = LongSets.unmodifiable(new LongArraySet(sections));
this.sections.addAll(sections);
listeners.forEach(Runnable::run); listeners.forEach(Runnable::run);
} }

View file

@ -1,52 +0,0 @@
package dev.engine_room.flywheel.impl.visualization.storage;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import dev.engine_room.flywheel.api.visual.ShaderLightVisual;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
public class ShaderLightStorage {
private final Map<ShaderLightVisual, SectionCollectorImpl> visuals = new Reference2ObjectOpenHashMap<>();
@Nullable
private LongSet cachedSections;
public boolean sectionsDirty() {
return cachedSections == null;
}
public void markDirty() {
cachedSections = null;
}
public LongSet sections() {
cachedSections = new LongOpenHashSet();
for (var value : visuals.values()) {
cachedSections.addAll(value.sections);
}
return cachedSections;
}
public void remove(ShaderLightVisual visual) {
visuals.remove(visual);
}
public void add(SectionCollectorImpl tracker, ShaderLightVisual visual) {
visuals.put(visual, tracker);
tracker.addListener(this::markDirty);
if (!tracker.sections.isEmpty()) {
markDirty();
}
}
public void clear() {
visuals.clear();
}
}

View file

@ -0,0 +1,53 @@
package dev.engine_room.flywheel.impl.visualization.storage;
import java.util.Map;
import dev.engine_room.flywheel.api.visual.ShaderLightVisual;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
public class ShaderLightVisualStorage {
private final Map<ShaderLightVisual, SectionTracker> trackers = new Reference2ReferenceOpenHashMap<>();
private final LongSet sections = new LongOpenHashSet();
private boolean isDirty;
public LongSet sections() {
if (isDirty) {
sections.clear();
for (var tracker : trackers.values()) {
sections.addAll(tracker.sections());
}
isDirty = false;
}
return sections;
}
public boolean isDirty() {
return isDirty;
}
public void markDirty() {
isDirty = true;
}
public void add(ShaderLightVisual visual, SectionTracker tracker) {
trackers.put(visual, tracker);
tracker.addListener(this::markDirty);
if (!tracker.sections().isEmpty()) {
markDirty();
}
}
public void remove(ShaderLightVisual visual) {
trackers.remove(visual);
}
public void clear() {
trackers.clear();
markDirty();
}
}

View file

@ -25,14 +25,14 @@ import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
public abstract class Storage<T> { public abstract class Storage<T> {
protected final Supplier<VisualizationContext> visualizationContextSupplier; protected final Supplier<VisualizationContext> visualizationContextSupplier;
private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>();
protected final PlanMap<DynamicVisual, DynamicVisual.Context> dynamicVisuals = new PlanMap<>(); protected final PlanMap<DynamicVisual, DynamicVisual.Context> dynamicVisuals = new PlanMap<>();
protected final PlanMap<TickableVisual, TickableVisual.Context> tickableVisuals = new PlanMap<>(); protected final PlanMap<TickableVisual, TickableVisual.Context> tickableVisuals = new PlanMap<>();
protected final List<SimpleDynamicVisual> simpleDynamicVisuals = new ArrayList<>(); protected final List<SimpleDynamicVisual> simpleDynamicVisuals = new ArrayList<>();
protected final List<SimpleTickableVisual> simpleTickableVisuals = new ArrayList<>(); protected final List<SimpleTickableVisual> simpleTickableVisuals = new ArrayList<>();
protected final LightUpdatedStorage litVisuals = new LightUpdatedStorage(); protected final LightUpdatedVisualStorage lightUpdatedVisuals = new LightUpdatedVisualStorage();
protected final ShaderLightStorage smoothLitVisuals = new ShaderLightStorage(); protected final ShaderLightVisualStorage shaderLightVisuals = new ShaderLightVisualStorage();
private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>();
public Storage(Supplier<VisualizationContext> visualizationContextSupplier) { public Storage(Supplier<VisualizationContext> visualizationContextSupplier) {
this.visualizationContextSupplier = visualizationContextSupplier; this.visualizationContextSupplier = visualizationContextSupplier;
@ -42,6 +42,29 @@ public abstract class Storage<T> {
return visuals.values(); return visuals.values();
} }
public Plan<DynamicVisual.Context> framePlan() {
return NestedPlan.of(dynamicVisuals, lightUpdatedVisuals.plan(), ForEachPlan.of(() -> simpleDynamicVisuals, SimpleDynamicVisual::beginFrame));
}
public Plan<TickableVisual.Context> tickPlan() {
return NestedPlan.of(tickableVisuals, ForEachPlan.of(() -> simpleTickableVisuals, SimpleTickableVisual::tick));
}
public LightUpdatedVisualStorage lightUpdatedVisuals() {
return lightUpdatedVisuals;
}
public ShaderLightVisualStorage shaderLightVisuals() {
return shaderLightVisuals;
}
/**
* Is the given object currently capable of being added?
*
* @return true if the object is currently capable of being visualized.
*/
public abstract boolean willAccept(T obj);
public void add(T obj, float partialTick) { public void add(T obj, float partialTick) {
Visual visual = visuals.get(obj); Visual visual = visuals.get(obj);
@ -57,13 +80,6 @@ public abstract class Storage<T> {
return; return;
} }
if (visual instanceof TickableVisual tickable) {
if (visual instanceof SimpleTickableVisual simpleTickable) {
simpleTickableVisuals.remove(simpleTickable);
} else {
tickableVisuals.remove(tickable);
}
}
if (visual instanceof DynamicVisual dynamic) { if (visual instanceof DynamicVisual dynamic) {
if (visual instanceof SimpleDynamicVisual simpleDynamic) { if (visual instanceof SimpleDynamicVisual simpleDynamic) {
simpleDynamicVisuals.remove(simpleDynamic); simpleDynamicVisuals.remove(simpleDynamic);
@ -71,12 +87,20 @@ public abstract class Storage<T> {
dynamicVisuals.remove(dynamic); dynamicVisuals.remove(dynamic);
} }
} }
if (visual instanceof LightUpdatedVisual lit) { if (visual instanceof TickableVisual tickable) {
litVisuals.remove(lit); if (visual instanceof SimpleTickableVisual simpleTickable) {
simpleTickableVisuals.remove(simpleTickable);
} else {
tickableVisuals.remove(tickable);
}
} }
if (visual instanceof ShaderLightVisual smoothLit) { if (visual instanceof LightUpdatedVisual lightUpdated) {
smoothLitVisuals.remove(smoothLit); lightUpdatedVisuals.remove(lightUpdated);
} }
if (visual instanceof ShaderLightVisual shaderLight) {
shaderLightVisuals.remove(shaderLight);
}
visual.delete(); visual.delete();
} }
@ -91,12 +115,13 @@ public abstract class Storage<T> {
} }
public void recreateAll(float partialTick) { public void recreateAll(float partialTick) {
tickableVisuals.clear();
dynamicVisuals.clear(); dynamicVisuals.clear();
simpleTickableVisuals.clear(); tickableVisuals.clear();
simpleDynamicVisuals.clear(); simpleDynamicVisuals.clear();
litVisuals.clear(); simpleTickableVisuals.clear();
smoothLitVisuals.clear(); lightUpdatedVisuals.clear();
shaderLightVisuals.clear();
visuals.replaceAll((obj, visual) -> { visuals.replaceAll((obj, visual) -> {
visual.delete(); visual.delete();
@ -110,15 +135,6 @@ public abstract class Storage<T> {
}); });
} }
public void invalidate() {
tickableVisuals.clear();
dynamicVisuals.clear();
litVisuals.clear();
visuals.values()
.forEach(Visual::delete);
visuals.clear();
}
private void create(T obj, float partialTick) { private void create(T obj, float partialTick) {
var visual = createRaw(obj, partialTick); var visual = createRaw(obj, partialTick);
@ -131,27 +147,7 @@ public abstract class Storage<T> {
@Nullable @Nullable
protected abstract Visual createRaw(T obj, float partialTick); protected abstract Visual createRaw(T obj, float partialTick);
public Plan<DynamicVisual.Context> framePlan() {
return NestedPlan.of(dynamicVisuals, litVisuals.plan(), ForEachPlan.of(() -> simpleDynamicVisuals, SimpleDynamicVisual::beginFrame));
}
public Plan<TickableVisual.Context> tickPlan() {
return NestedPlan.of(tickableVisuals, ForEachPlan.of(() -> simpleTickableVisuals, SimpleTickableVisual::tick));
}
public void enqueueLightUpdateSection(long section) {
litVisuals.enqueueLightUpdateSection(section);
}
private void setup(Visual visual) { private void setup(Visual visual) {
if (visual instanceof TickableVisual tickable) {
if (visual instanceof SimpleTickableVisual simpleTickable) {
simpleTickableVisuals.add(simpleTickable);
} else {
tickableVisuals.add(tickable, tickable.planTick());
}
}
if (visual instanceof DynamicVisual dynamic) { if (visual instanceof DynamicVisual dynamic) {
if (visual instanceof SimpleDynamicVisual simpleDynamic) { if (visual instanceof SimpleDynamicVisual simpleDynamic) {
simpleDynamicVisuals.add(simpleDynamic); simpleDynamicVisuals.add(simpleDynamic);
@ -160,30 +156,39 @@ public abstract class Storage<T> {
} }
} }
if (visual instanceof SectionTrackedVisual tracked) { if (visual instanceof TickableVisual tickable) {
SectionCollectorImpl sectionProperty = new SectionCollectorImpl(); if (visual instanceof SimpleTickableVisual simpleTickable) {
simpleTickableVisuals.add(simpleTickable);
} else {
tickableVisuals.add(tickable, tickable.planTick());
}
}
// Give the visual a chance to fill in the property. if (visual instanceof SectionTrackedVisual tracked) {
tracked.setSectionCollector(sectionProperty); SectionTracker tracker = new SectionTracker();
// Give the visual a chance to invoke the collector.
tracked.setSectionCollector(tracker);
if (visual instanceof LightUpdatedVisual lightUpdated) { if (visual instanceof LightUpdatedVisual lightUpdated) {
litVisuals.add(sectionProperty, lightUpdated); lightUpdatedVisuals.add(lightUpdated, tracker);
} }
if (visual instanceof ShaderLightVisual shaderLight) { if (visual instanceof ShaderLightVisual shaderLight) {
smoothLitVisuals.add(sectionProperty, shaderLight); shaderLightVisuals.add(shaderLight, tracker);
} }
} }
} }
/** public void invalidate() {
* Is the given object currently capable of being added? dynamicVisuals.clear();
* tickableVisuals.clear();
* @return true if the object is currently capable of being visualized. simpleDynamicVisuals.clear();
*/ simpleTickableVisuals.clear();
public abstract boolean willAccept(T obj); lightUpdatedVisuals.clear();
shaderLightVisuals.clear();
public ShaderLightStorage smoothLitStorage() { visuals.values()
return smoothLitVisuals; .forEach(Visual::delete);
visuals.clear();
} }
} }

View file

@ -194,7 +194,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
body.setTransform(stack) body.setTransform(stack)
.setChanged(); .setChanged();
// TODO: Use LitVisual if possible. // TODO: Use LightUpdatedVisual/ShaderLightVisual if possible.
updateLight(partialTick); updateLight(partialTick);
} }