mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-14 00:06:12 +01:00
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:
parent
1ea94a859e
commit
fe0eacad8e
38 changed files with 364 additions and 372 deletions
|
@ -62,13 +62,6 @@ public interface Engine {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -78,6 +71,13 @@ public interface Engine {
|
|||
*/
|
||||
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.
|
||||
* @param progress The progress of the crumbling animation in the range [0, 10).
|
||||
|
|
|
@ -9,7 +9,8 @@ package dev.engine_room.flywheel.api.visual;
|
|||
*/
|
||||
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>
|
||||
*
|
||||
|
@ -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,
|
||||
* 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>
|
||||
*
|
||||
* <p>This method not is invoked automatically after visual creation.</p>
|
||||
*/
|
||||
void updateLight(float partialTick);
|
||||
}
|
||||
|
|
|
@ -6,15 +6,15 @@ import it.unimi.dsi.fastutil.longs.LongSet;
|
|||
|
||||
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.
|
||||
* <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.
|
||||
*
|
||||
* @param property The property.
|
||||
* @param collector The collector.
|
||||
*/
|
||||
void setSectionCollector(SectionCollector property);
|
||||
void setSectionCollector(SectionCollector collector);
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
interface SectionCollector {
|
||||
|
|
|
@ -6,6 +6,7 @@ package dev.engine_room.flywheel.api.visual;
|
|||
* @see DynamicVisual
|
||||
* @see TickableVisual
|
||||
* @see LightUpdatedVisual
|
||||
* @see ShaderLightVisual
|
||||
*/
|
||||
public interface Visual {
|
||||
/**
|
||||
|
|
|
@ -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 it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
|
@ -11,12 +12,15 @@ import net.minecraft.world.level.LevelAccessor;
|
|||
public class 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) {
|
||||
return HOLDERS.get(level);
|
||||
}
|
||||
|
||||
private final LongSet updatedSections = new LongArraySet();
|
||||
|
||||
public LongSet getAndClearUpdatedSections() {
|
||||
if (updatedSections.isEmpty()) {
|
||||
return LongSet.of();
|
||||
|
@ -30,7 +34,4 @@ public class LightUpdateHolder {
|
|||
public void add(long section) {
|
||||
updatedSections.add(section);
|
||||
}
|
||||
|
||||
private LightUpdateHolder() {
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ public class SsboInstanceComponent extends InstanceAssemblerComponent {
|
|||
|
||||
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"
|
||||
+ "};");
|
||||
builder.blankLine();
|
||||
|
|
|
@ -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 it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
@ -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.backend.FlwBackend;
|
||||
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 it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
|
|
|
@ -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.visualization.VisualEmbedding;
|
||||
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.LightStorage;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.TopLevelEmbeddedEnvironment;
|
||||
import dev.engine_room.flywheel.backend.engine.embed.EnvironmentStorage;
|
||||
import dev.engine_room.flywheel.backend.engine.uniform.Uniforms;
|
||||
import dev.engine_room.flywheel.backend.gl.GlStateTracker;
|
||||
import dev.engine_room.flywheel.lib.task.Flag;
|
||||
|
@ -30,11 +30,11 @@ import net.minecraft.world.level.LevelAccessor;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class EngineImpl implements Engine {
|
||||
private final int sqrMaxOriginDistance;
|
||||
private final DrawManager<? extends AbstractInstancer<?>> drawManager;
|
||||
private final int sqrMaxOriginDistance;
|
||||
private final Flag flushFlag = new NamedFlag("flushed");
|
||||
private final EnvironmentStorage environmentStorage;
|
||||
private final LightStorage lightStorage;
|
||||
private final Flag flushFlag = new NamedFlag("flushed");
|
||||
|
||||
private BlockPos renderOrigin = BlockPos.ZERO;
|
||||
|
||||
|
@ -45,6 +45,11 @@ public class EngineImpl implements Engine {
|
|||
lightStorage = new LightStorage(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisualizationContext createVisualizationContext(RenderStage stage) {
|
||||
return new VisualizationContextImpl(stage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plan<RenderContext> createFramePlan() {
|
||||
return lightStorage.createFramePlan()
|
||||
|
@ -69,11 +74,6 @@ public class EngineImpl implements Engine {
|
|||
drawManager.renderCrumbling(crumblingBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisualizationContext createVisualizationContext(RenderStage stage) {
|
||||
return new VisualizationContextImpl(stage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateRenderOrigin(Camera camera) {
|
||||
Vec3 cameraPos = camera.getPosition();
|
||||
|
@ -97,14 +97,14 @@ public class EngineImpl implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
drawManager.delete();
|
||||
lightStorage.delete();
|
||||
public void lightSections(LongSet sections) {
|
||||
lightStorage.sections(sections);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lightSections(LongSet sections) {
|
||||
lightStorage.sections(sections);
|
||||
public void delete() {
|
||||
drawManager.delete();
|
||||
lightStorage.delete();
|
||||
}
|
||||
|
||||
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
|
||||
public VisualEmbedding createEmbedding() {
|
||||
var out = new TopLevelEmbeddedEnvironment(EngineImpl.this, stage);
|
||||
var out = new EmbeddedEnvironment(EngineImpl.this, stage);
|
||||
environmentStorage.track(out);
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package dev.engine_room.flywheel.backend.engine.embed;
|
||||
package dev.engine_room.flywheel.backend.engine;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -11,8 +11,8 @@ import it.unimi.dsi.fastutil.longs.LongComparator;
|
|||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
||||
import net.minecraft.core.SectionPos;
|
||||
|
||||
public class LightLut {
|
||||
public static final LongComparator SECTION_X_THEN_Y_THEN_Z = (long a, long b) -> {
|
||||
public final class LightLut {
|
||||
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));
|
||||
if (xComp != 0) {
|
||||
return xComp;
|
||||
|
@ -24,6 +24,8 @@ public class LightLut {
|
|||
return Integer.compare(SectionPos.z(a), SectionPos.z(b));
|
||||
};
|
||||
|
||||
private LightLut() {
|
||||
}
|
||||
|
||||
// Massive kudos to RogueLogix for figuring out this LUT scheme.
|
||||
// TODO: switch to y x z or x z y ordering
|
|
@ -1,4 +1,4 @@
|
|||
package dev.engine_room.flywheel.backend.engine.embed;
|
||||
package dev.engine_room.flywheel.backend.engine;
|
||||
|
||||
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.task.Plan;
|
||||
import dev.engine_room.flywheel.backend.LightUpdateHolder;
|
||||
import dev.engine_room.flywheel.backend.engine.indirect.StagingBuffer;
|
||||
import dev.engine_room.flywheel.backend.gl.buffer.GlBuffer;
|
||||
import dev.engine_room.flywheel.lib.task.SimplePlan;
|
|
@ -1,5 +1,6 @@
|
|||
package dev.engine_room.flywheel.backend.engine.embed;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix3fc;
|
||||
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 net.minecraft.core.Vec3i;
|
||||
|
||||
public abstract class AbstractEmbeddedEnvironment implements Environment, VisualEmbedding {
|
||||
protected final Matrix4f pose = new Matrix4f();
|
||||
protected final Matrix3f normal = new Matrix3f();
|
||||
public class EmbeddedEnvironment implements VisualEmbedding, Environment {
|
||||
private final EngineImpl engine;
|
||||
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 Matrix3f normalComposed = new Matrix3f();
|
||||
private final InstancerProvider instancerProvider;
|
||||
protected final EngineImpl engine;
|
||||
private final RenderStage renderStage;
|
||||
|
||||
private boolean deleted = false;
|
||||
|
||||
public AbstractEmbeddedEnvironment(EngineImpl engine, RenderStage renderStage) {
|
||||
public EmbeddedEnvironment(EngineImpl engine, RenderStage renderStage, @Nullable EmbeddedEnvironment parent) {
|
||||
this.engine = engine;
|
||||
this.renderStage = renderStage;
|
||||
this.parent = parent;
|
||||
|
||||
instancerProvider = new InstancerProvider() {
|
||||
@Override
|
||||
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
|
||||
// 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
|
||||
public void transforms(Matrix4fc pose, Matrix3fc normal) {
|
||||
this.pose.set(pose);
|
||||
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
|
||||
public InstancerProvider instancerProvider() {
|
||||
return instancerProvider;
|
||||
|
@ -84,12 +68,47 @@ public abstract class AbstractEmbeddedEnvironment implements Environment, Visual
|
|||
|
||||
@Override
|
||||
public VisualEmbedding createEmbedding() {
|
||||
var out = new NestedEmbeddedEnvironment(this, engine, renderStage);
|
||||
var out = new EmbeddedEnvironment(engine, renderStage, this);
|
||||
engine.environmentStorage()
|
||||
.track(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() {
|
||||
return deleted;
|
||||
}
|
||||
|
@ -101,6 +120,4 @@ public abstract class AbstractEmbeddedEnvironment implements Environment, Visual
|
|||
public void delete() {
|
||||
deleted = true;
|
||||
}
|
||||
|
||||
public abstract void composeMatrices(Matrix4f pose, Matrix3f normal);
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
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 NORMAL_MATRIX = "_flw_normalMatrix";
|
||||
public static final String USE_MODEL_MATRIX = "_flw_useModelMatrix";
|
||||
public static final String MODEL_MATRIX1 = "_flw_modelMatrix";
|
||||
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";
|
||||
|
||||
private EmbeddingUniforms() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import dev.engine_room.flywheel.backend.gl.shader.GlProgram;
|
|||
public interface Environment {
|
||||
ContextShader contextShader();
|
||||
|
||||
void setupDraw(GlProgram drawProgram);
|
||||
|
||||
void setupCull(GlProgram cullProgram);
|
||||
|
||||
void setupDraw(GlProgram drawProgram);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -14,13 +14,12 @@ public class GlobalEnvironment implements Environment {
|
|||
return ContextShader.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupDraw(GlProgram drawProgram) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupCull(GlProgram cullProgram) {
|
||||
cullProgram.setBool(EmbeddingUniforms.USE_MODEL_MATRIX, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupDraw(GlProgram drawProgram) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
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 LIGHT_LUT_BINDING = 5;
|
||||
public static final int LIGHT_SECTION_BINDING = 6;
|
||||
public final class BufferBindings {
|
||||
public static final int INSTANCE = 0;
|
||||
public static final int TARGET = 1;
|
||||
public static final int MODEL_INDEX = 2;
|
||||
public static final int MODEL = 3;
|
||||
public static final int DRAW = 4;
|
||||
public static final int LIGHT_LUT = 5;
|
||||
public static final int LIGHT_SECTION = 6;
|
||||
|
||||
private BufferBindings() {
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ public class IndirectBuffers {
|
|||
|
||||
private void multiBind() {
|
||||
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() {
|
||||
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() {
|
||||
|
|
|
@ -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.GroupKey;
|
||||
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.MeshPool;
|
||||
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.gl.GlStateTracker;
|
||||
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 Map<GroupKey<?>, IndirectCullingGroup<?>> cullingGroups = new HashMap<>();
|
||||
private final GlBuffer crumblingDrawBuffer = new GlBuffer();
|
||||
|
||||
private final LightBuffers lightBuffers;
|
||||
|
||||
public IndirectDrawManager(IndirectPrograms programs) {
|
||||
this.programs = programs;
|
||||
programs.acquire();
|
||||
|
||||
stagingBuffer = new StagingBuffer(this.programs);
|
||||
|
||||
meshPool = new MeshPool();
|
||||
|
||||
vertexArray = GlVertexArray.create();
|
||||
|
||||
meshPool.bind(vertexArray);
|
||||
lightBuffers = new LightBuffers();
|
||||
}
|
||||
|
@ -170,7 +167,7 @@ public class IndirectDrawManager extends DrawManager<IndirectInstancer<?>> {
|
|||
var block = MemoryBlock.malloc(IndirectBuffers.DRAW_COMMAND_STRIDE);
|
||||
|
||||
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()) {
|
||||
var byProgress = groupEntry.getValue();
|
||||
|
|
|
@ -3,14 +3,11 @@ package dev.engine_room.flywheel.backend.engine.indirect;
|
|||
import org.lwjgl.opengl.GL46;
|
||||
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 {
|
||||
private final ResizableStorageArray sections = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
|
||||
private final ResizableStorageArray lut = new ResizableStorageArray(4);
|
||||
|
||||
public LightBuffers() {
|
||||
}
|
||||
private final ResizableStorageArray sections = new ResizableStorageArray(LightStorage.SECTION_SIZE_BYTES);
|
||||
|
||||
public void flush(StagingBuffer staging, LightStorage light) {
|
||||
var capacity = light.capacity();
|
||||
|
@ -40,7 +37,7 @@ public class LightBuffers {
|
|||
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_SECTION_BINDING, sections.handle(), 0, sections.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, sections.handle(), 0, sections.byteCapacity());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.GroupKey;
|
||||
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.MaterialRenderState;
|
||||
import dev.engine_room.flywheel.backend.engine.MeshPool;
|
||||
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.gl.GlStateTracker;
|
||||
import dev.engine_room.flywheel.backend.gl.TextureBuffer;
|
||||
|
|
|
@ -4,7 +4,7 @@ import org.lwjgl.opengl.GL32;
|
|||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
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.buffer.GlBuffer;
|
||||
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
||||
|
@ -16,8 +16,8 @@ public class InstancedLight {
|
|||
private final TextureBuffer sectionsTexture;
|
||||
|
||||
public InstancedLight() {
|
||||
sections = new GlBuffer();
|
||||
lut = new GlBuffer();
|
||||
sections = new GlBuffer();
|
||||
lutTexture = new TextureBuffer(GL32.GL_R32UI);
|
||||
sectionsTexture = new TextureBuffer(GL32.GL_R32UI);
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ public class InstancedLight {
|
|||
}
|
||||
|
||||
public void delete() {
|
||||
sections.delete();
|
||||
lut.delete();
|
||||
sections.delete();
|
||||
lutTexture.delete();
|
||||
sectionsTexture.delete();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
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.ClientLevel;
|
||||
import net.minecraft.core.SectionPos;
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
#define _FLW_MODEL_INDEX_BUFFER_BINDING 2
|
||||
#define _FLW_MODEL_BUFFER_BINDING 3
|
||||
#define _FLW_DRAW_BUFFER_BINDING 4
|
||||
#define _FLW_LIGHT_LUT_BINDING 5
|
||||
#define _FLW_LIGHT_SECTIONS_BINDING 6
|
||||
#define _FLW_LIGHT_LUT_BUFFER_BINDING 5
|
||||
#define _FLW_LIGHT_SECTIONS_BUFFER_BINDING 6
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
#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[];
|
||||
};
|
||||
|
||||
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[];
|
||||
};
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ abstract class ClientChunkCacheMixin {
|
|||
var manager = VisualizationManagerImpl.get(level);
|
||||
|
||||
if (manager != null) {
|
||||
manager.enqueueLightUpdateSection(pos.asLong());
|
||||
manager.onLightUpdate(pos.asLong());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,11 +106,11 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
.ifFalse(update)
|
||||
.plan()
|
||||
.then(SimplePlan.of(() -> {
|
||||
if (blockEntities.lightSectionsDirty() || entities.lightSectionsDirty() || effects.lightSectionsDirty()) {
|
||||
if (blockEntities.areGpuLightSectionsDirty() || entities.areGpuLightSectionsDirty() || effects.areGpuLightSectionsDirty()) {
|
||||
var out = new LongOpenHashSet();
|
||||
out.addAll(blockEntities.lightSections());
|
||||
out.addAll(entities.lightSections());
|
||||
out.addAll(effects.lightSections());
|
||||
out.addAll(blockEntities.gpuLightSections());
|
||||
out.addAll(entities.gpuLightSections());
|
||||
out.addAll(effects.gpuLightSections());
|
||||
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.
|
||||
*/
|
||||
|
@ -315,13 +321,4 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
effects.invalidate();
|
||||
engine.delete();
|
||||
}
|
||||
|
||||
public void enqueueLightUpdateSection(long section) {
|
||||
blockEntities.getStorage()
|
||||
.enqueueLightUpdateSection(section);
|
||||
entities.getStorage()
|
||||
.enqueueLightUpdateSection(section);
|
||||
effects.getStorage()
|
||||
.enqueueLightUpdateSection(section);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,8 +69,20 @@ public class BlockEntityStorage extends Storage<BlockEntity> {
|
|||
|
||||
@Override
|
||||
public void remove(BlockEntity obj) {
|
||||
super.remove(obj);
|
||||
posLookup.remove(obj.getBlockPos()
|
||||
.asLong());
|
||||
super.remove(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateAll(float partialTick) {
|
||||
posLookup.clear();
|
||||
super.recreateAll(partialTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
posLookup.clear();
|
||||
super.invalidate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,10 +54,6 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
|
|||
queue.add(Transaction.update(obj));
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
getStorage().invalidate();
|
||||
}
|
||||
|
||||
public void processQueue(float partialTick) {
|
||||
var storage = getStorage();
|
||||
Transaction<T> transaction;
|
||||
|
@ -76,13 +72,22 @@ public class VisualManagerImpl<T, S extends Storage<T>> implements VisualManager
|
|||
.then(storage.tickPlan());
|
||||
}
|
||||
|
||||
public boolean lightSectionsDirty() {
|
||||
return getStorage().smoothLitStorage()
|
||||
.sectionsDirty();
|
||||
public void onLightUpdate(long section) {
|
||||
getStorage().lightUpdatedVisuals()
|
||||
.onLightUpdate(section);
|
||||
}
|
||||
|
||||
public LongSet lightSections() {
|
||||
return getStorage().smoothLitStorage()
|
||||
public boolean areGpuLightSectionsDirty() {
|
||||
return getStorage().shaderLightVisuals()
|
||||
.isDirty();
|
||||
}
|
||||
|
||||
public LongSet gpuLightSections() {
|
||||
return getStorage().shaderLightVisuals()
|
||||
.sections();
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
getStorage().invalidate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import dev.engine_room.flywheel.lib.task.SimplyComposedPlan;
|
|||
import dev.engine_room.flywheel.lib.task.Synchronizer;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
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.LongSet;
|
||||
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.
|
||||
*/
|
||||
public class LightUpdatedStorage {
|
||||
public class LightUpdatedVisualStorage {
|
||||
private static final long NEVER_UPDATED = Long.MIN_VALUE;
|
||||
private static final long INITIAL_UPDATE_ID = NEVER_UPDATED + 1;
|
||||
|
||||
private final Map<LightUpdatedVisual, LongSet> visuals2Sections = new WeakHashMap<>();
|
||||
private final Long2ObjectMap<List<Updater>> sections2Visuals = new Long2ObjectOpenHashMap<>();
|
||||
private final Map<LightUpdatedVisual, LongSet> visual2Sections = new WeakHashMap<>();
|
||||
private final Long2ObjectMap<List<Updater>> section2Updaters = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
private final Queue<MovedVisual> movedVisuals = new ConcurrentLinkedQueue<>();
|
||||
private final LongSet sectionsUpdatedThisFrame = new LongOpenHashSet();
|
||||
private final Queue<MovedVisual> movedVisuals = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private long updateId = INITIAL_UPDATE_ID;
|
||||
|
||||
|
@ -54,9 +53,9 @@ public class LightUpdatedStorage {
|
|||
Updater.Context updaterContext = new Updater.Context(updateId, context.partialTick());
|
||||
|
||||
for (long section : sectionsUpdatedThisFrame) {
|
||||
var visuals = sections2Visuals.get(section);
|
||||
if (visuals != null && !visuals.isEmpty()) {
|
||||
taskExecutor.execute(() -> Distribute.tasks(taskExecutor, updaterContext, sync, visuals, Updater::updateLight));
|
||||
var updaters = section2Updaters.get(section);
|
||||
if (updaters != null && !updaters.isEmpty()) {
|
||||
taskExecutor.execute(() -> Distribute.tasks(taskExecutor, updaterContext, sync, updaters, Updater::updateLight));
|
||||
} else {
|
||||
sync.decrementAndEventuallyRun();
|
||||
}
|
||||
|
@ -69,7 +68,7 @@ public class LightUpdatedStorage {
|
|||
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 (remove(moved.visual)) {
|
||||
updateTracking(moved.tracker, moved.visual);
|
||||
addInner(moved.visual, moved.tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,43 +85,32 @@ public class LightUpdatedStorage {
|
|||
return out;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return visuals2Sections.isEmpty();
|
||||
}
|
||||
|
||||
public void add(SectionCollectorImpl tracker, LightUpdatedVisual visual) {
|
||||
var moved = new MovedVisual(tracker, visual);
|
||||
public void add(LightUpdatedVisual visual, SectionTracker tracker) {
|
||||
var moved = new MovedVisual(visual, tracker);
|
||||
tracker.addListener(() -> movedVisuals.add(moved));
|
||||
|
||||
updateTracking(tracker, visual);
|
||||
addInner(visual, tracker);
|
||||
}
|
||||
|
||||
public void updateTracking(SectionCollectorImpl tracker, LightUpdatedVisual visual) {
|
||||
if (tracker.sections.isEmpty()) {
|
||||
private void addInner(LightUpdatedVisual visual, SectionTracker tracker) {
|
||||
if (tracker.sections().isEmpty()) {
|
||||
// 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.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a copy of the array, so we know what section to remove the visual from later.
|
||||
var sections = new LongArraySet(tracker.sections);
|
||||
|
||||
visuals2Sections.put(visual, sections);
|
||||
|
||||
var sections = tracker.sections();
|
||||
visual2Sections.put(visual, sections);
|
||||
var updater = createUpdater(visual, sections.size());
|
||||
|
||||
for (long section : sections) {
|
||||
sections2Visuals.computeIfAbsent(section, $ -> new ObjectArrayList<>())
|
||||
section2Updaters.computeIfAbsent(section, $ -> new ObjectArrayList<>())
|
||||
.add(updater);
|
||||
}
|
||||
}
|
||||
|
||||
public void enqueueLightUpdateSection(long section) {
|
||||
sectionsUpdatedThisFrame.add(section);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the visual from this storage.
|
||||
*
|
||||
|
@ -130,31 +118,36 @@ public class LightUpdatedStorage {
|
|||
* @return {@code true} if the visual was removed, {@code false} otherwise.
|
||||
*/
|
||||
public boolean remove(LightUpdatedVisual visual) {
|
||||
var sections = visuals2Sections.remove(visual);
|
||||
var sections = visual2Sections.remove(visual);
|
||||
|
||||
if (sections == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (long section : sections) {
|
||||
List<Updater> listeners = sections2Visuals.get(section);
|
||||
if (listeners != null) {
|
||||
listeners.remove(indexOfUpdater(listeners, visual));
|
||||
List<Updater> updaters = section2Updaters.get(section);
|
||||
if (updaters != null) {
|
||||
updaters.remove(indexOfUpdater(updaters, visual));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
visuals2Sections.clear();
|
||||
sections2Visuals.clear();
|
||||
sectionsUpdatedThisFrame.clear();
|
||||
public void onLightUpdate(long section) {
|
||||
sectionsUpdatedThisFrame.add(section);
|
||||
}
|
||||
|
||||
private static int indexOfUpdater(List<Updater> listeners, LightUpdatedVisual visual) {
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
if (listeners.get(i)
|
||||
public void clear() {
|
||||
visual2Sections.clear();
|
||||
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) {
|
||||
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.
|
||||
sealed interface Updater {
|
||||
private sealed interface Updater {
|
||||
void updateLight(Context ctx);
|
||||
|
||||
LightUpdatedVisual visual();
|
||||
|
@ -201,6 +194,6 @@ public class LightUpdatedStorage {
|
|||
}
|
||||
}
|
||||
|
||||
private record MovedVisual(SectionCollectorImpl tracker, LightUpdatedVisual visual) {
|
||||
private record MovedVisual(LightUpdatedVisual visual, SectionTracker tracker) {
|
||||
}
|
||||
}
|
|
@ -3,20 +3,27 @@ package dev.engine_room.flywheel.impl.visualization.storage;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import dev.engine_room.flywheel.api.visual.SectionTrackedVisual;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSets;
|
||||
|
||||
public class SectionCollectorImpl implements SectionTrackedVisual.SectionCollector {
|
||||
public final LongSet sections = new LongArraySet();
|
||||
|
||||
public class SectionTracker implements SectionTrackedVisual.SectionCollector {
|
||||
private final List<Runnable> listeners = new ArrayList<>(2);
|
||||
|
||||
@Unmodifiable
|
||||
private LongSet sections = LongSet.of();
|
||||
|
||||
@Unmodifiable
|
||||
public LongSet sections() {
|
||||
return sections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sections(LongSet sections) {
|
||||
this.sections.clear();
|
||||
this.sections.addAll(sections);
|
||||
|
||||
this.sections = LongSets.unmodifiable(new LongArraySet(sections));
|
||||
listeners.forEach(Runnable::run);
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -25,14 +25,14 @@ import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
|||
|
||||
public abstract class Storage<T> {
|
||||
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<TickableVisual, TickableVisual.Context> tickableVisuals = new PlanMap<>();
|
||||
protected final List<SimpleDynamicVisual> simpleDynamicVisuals = new ArrayList<>();
|
||||
protected final List<SimpleTickableVisual> simpleTickableVisuals = new ArrayList<>();
|
||||
protected final LightUpdatedStorage litVisuals = new LightUpdatedStorage();
|
||||
protected final ShaderLightStorage smoothLitVisuals = new ShaderLightStorage();
|
||||
|
||||
private final Map<T, Visual> visuals = new Reference2ObjectOpenHashMap<>();
|
||||
protected final LightUpdatedVisualStorage lightUpdatedVisuals = new LightUpdatedVisualStorage();
|
||||
protected final ShaderLightVisualStorage shaderLightVisuals = new ShaderLightVisualStorage();
|
||||
|
||||
public Storage(Supplier<VisualizationContext> visualizationContextSupplier) {
|
||||
this.visualizationContextSupplier = visualizationContextSupplier;
|
||||
|
@ -42,6 +42,29 @@ public abstract class Storage<T> {
|
|||
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) {
|
||||
Visual visual = visuals.get(obj);
|
||||
|
||||
|
@ -57,13 +80,6 @@ public abstract class Storage<T> {
|
|||
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 SimpleDynamicVisual simpleDynamic) {
|
||||
simpleDynamicVisuals.remove(simpleDynamic);
|
||||
|
@ -71,12 +87,20 @@ public abstract class Storage<T> {
|
|||
dynamicVisuals.remove(dynamic);
|
||||
}
|
||||
}
|
||||
if (visual instanceof LightUpdatedVisual lit) {
|
||||
litVisuals.remove(lit);
|
||||
if (visual instanceof TickableVisual tickable) {
|
||||
if (visual instanceof SimpleTickableVisual simpleTickable) {
|
||||
simpleTickableVisuals.remove(simpleTickable);
|
||||
} else {
|
||||
tickableVisuals.remove(tickable);
|
||||
}
|
||||
}
|
||||
if (visual instanceof ShaderLightVisual smoothLit) {
|
||||
smoothLitVisuals.remove(smoothLit);
|
||||
if (visual instanceof LightUpdatedVisual lightUpdated) {
|
||||
lightUpdatedVisuals.remove(lightUpdated);
|
||||
}
|
||||
if (visual instanceof ShaderLightVisual shaderLight) {
|
||||
shaderLightVisuals.remove(shaderLight);
|
||||
}
|
||||
|
||||
visual.delete();
|
||||
}
|
||||
|
||||
|
@ -91,12 +115,13 @@ public abstract class Storage<T> {
|
|||
}
|
||||
|
||||
public void recreateAll(float partialTick) {
|
||||
tickableVisuals.clear();
|
||||
dynamicVisuals.clear();
|
||||
simpleTickableVisuals.clear();
|
||||
tickableVisuals.clear();
|
||||
simpleDynamicVisuals.clear();
|
||||
litVisuals.clear();
|
||||
smoothLitVisuals.clear();
|
||||
simpleTickableVisuals.clear();
|
||||
lightUpdatedVisuals.clear();
|
||||
shaderLightVisuals.clear();
|
||||
|
||||
visuals.replaceAll((obj, visual) -> {
|
||||
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) {
|
||||
var visual = createRaw(obj, partialTick);
|
||||
|
||||
|
@ -131,27 +147,7 @@ public abstract class Storage<T> {
|
|||
@Nullable
|
||||
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) {
|
||||
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 SimpleDynamicVisual simpleDynamic) {
|
||||
simpleDynamicVisuals.add(simpleDynamic);
|
||||
|
@ -160,30 +156,39 @@ public abstract class Storage<T> {
|
|||
}
|
||||
}
|
||||
|
||||
if (visual instanceof SectionTrackedVisual tracked) {
|
||||
SectionCollectorImpl sectionProperty = new SectionCollectorImpl();
|
||||
if (visual instanceof TickableVisual tickable) {
|
||||
if (visual instanceof SimpleTickableVisual simpleTickable) {
|
||||
simpleTickableVisuals.add(simpleTickable);
|
||||
} else {
|
||||
tickableVisuals.add(tickable, tickable.planTick());
|
||||
}
|
||||
}
|
||||
|
||||
// Give the visual a chance to fill in the property.
|
||||
tracked.setSectionCollector(sectionProperty);
|
||||
if (visual instanceof SectionTrackedVisual tracked) {
|
||||
SectionTracker tracker = new SectionTracker();
|
||||
|
||||
// Give the visual a chance to invoke the collector.
|
||||
tracked.setSectionCollector(tracker);
|
||||
|
||||
if (visual instanceof LightUpdatedVisual lightUpdated) {
|
||||
litVisuals.add(sectionProperty, lightUpdated);
|
||||
lightUpdatedVisuals.add(lightUpdated, tracker);
|
||||
}
|
||||
|
||||
if (visual instanceof ShaderLightVisual shaderLight) {
|
||||
smoothLitVisuals.add(sectionProperty, shaderLight);
|
||||
shaderLightVisuals.add(shaderLight, tracker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ShaderLightStorage smoothLitStorage() {
|
||||
return smoothLitVisuals;
|
||||
public void invalidate() {
|
||||
dynamicVisuals.clear();
|
||||
tickableVisuals.clear();
|
||||
simpleDynamicVisuals.clear();
|
||||
simpleTickableVisuals.clear();
|
||||
lightUpdatedVisuals.clear();
|
||||
shaderLightVisuals.clear();
|
||||
visuals.values()
|
||||
.forEach(Visual::delete);
|
||||
visuals.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ public class MinecartVisual<T extends AbstractMinecart> extends SimpleEntityVisu
|
|||
body.setTransform(stack)
|
||||
.setChanged();
|
||||
|
||||
// TODO: Use LitVisual if possible.
|
||||
// TODO: Use LightUpdatedVisual/ShaderLightVisual if possible.
|
||||
updateLight(partialTick);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue