diff --git a/gradle.properties b/gradle.properties index b44a7ab59..b121f29ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs = -Xmx3G org.gradle.daemon = false # mod version info -mod_version = 0.6.0 +mod_version = 0.6.1 mc_update_version = 1.18 minecraft_version = 1.18.1 loader_version = 0.12.12 diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index ba6a96e96..a01e46690 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -3,18 +3,42 @@ package com.jozufozu.flywheel; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.Loader; +import com.jozufozu.flywheel.backend.RenderWork; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.config.FlwConfig; +import com.jozufozu.flywheel.core.Contexts; +import com.jozufozu.flywheel.core.PartialModel; +import com.jozufozu.flywheel.core.QuadConverter; +import com.jozufozu.flywheel.core.compile.ProgramCompiler; +import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; +import com.jozufozu.flywheel.event.EntityWorldHandler; +import com.jozufozu.flywheel.event.ForgeEvents; +import com.jozufozu.flywheel.fabric.event.FlywheelEvents; +import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; +import com.jozufozu.flywheel.vanilla.VanillaInstances; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.SemanticVersion; import net.fabricmc.loader.api.Version; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; -public class Flywheel { +public class Flywheel implements ClientModInitializer { public static final String ID = "flywheel"; public static final Logger LOGGER = LogManager.getLogger(Flywheel.class); - public static SemanticVersion VERSION; + private static SemanticVersion version; - static void initVersion() { + @Override + public void onInitializeClient() { Version version = FabricLoader.getInstance() .getModContainer(ID) .orElseThrow(() -> new IllegalStateException("Could not get the mod container for Flywheel!")) @@ -23,7 +47,43 @@ public class Flywheel { if (!(version instanceof SemanticVersion semver)) { throw new IllegalStateException("Got non-semantic version for Flywheel!"); } - VERSION = semver; + Flywheel.version = semver; + + FlwConfig.init(); + + Backend.init(); + + FlywheelEvents.GATHER_CONTEXT.register(Contexts::flwInit); + ModelLoadingRegistry.INSTANCE.registerModelProvider(PartialModel::onModelRegistry); + ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(PartialModel.ResourceReloadListener.INSTANCE); + + FlywheelEvents.RELOAD_RENDERERS.register(ProgramCompiler::invalidateAll); + + ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(Loader.ResourceReloadListener.INSTANCE); + + WorldRenderEvents.END.register(RenderWork::onRenderWorldLast); + ClientTickEvents.END_CLIENT_TICK.register(InstancedRenderDispatcher::tick); + FlywheelEvents.BEGIN_FRAME.register(InstancedRenderDispatcher::onBeginFrame); + FlywheelEvents.RENDER_LAYER.register(InstancedRenderDispatcher::renderLayer); + FlywheelEvents.RELOAD_RENDERERS.register(InstancedRenderDispatcher::onReloadRenderers); + FlywheelEvents.RELOAD_RENDERERS.register(QuadConverter::onRendererReload); + FlywheelEvents.RELOAD_RENDERERS.register(CrumblingRenderer::onReloadRenderers); + ClientEntityEvents.ENTITY_LOAD.register(EntityWorldHandler::onEntityJoinWorld); + ClientEntityEvents.ENTITY_UNLOAD.register(EntityWorldHandler::onEntityLeaveWorld); + ClientTickEvents.END_CLIENT_TICK.register(ForgeEvents::tickLight); + + VanillaInstances.init(); + + // https://github.com/Jozufozu/Flywheel/issues/69 + // Weird issue with accessor loading. + // Only thing I've seen that's close to a fix is to force the class to load before trying to use it. + // From the SpongePowered discord: + // https://discord.com/channels/142425412096491520/626802111455297538/675007581168599041 + LOGGER.debug("Successfully loaded {}", PausedPartialTickAccessor.class.getName()); + } + + public static SemanticVersion getVersion() { + return version; } public static ResourceLocation rl(String path) { diff --git a/src/main/java/com/jozufozu/flywheel/FlywheelClient.java b/src/main/java/com/jozufozu/flywheel/FlywheelClient.java deleted file mode 100644 index 98adf752e..000000000 --- a/src/main/java/com/jozufozu/flywheel/FlywheelClient.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.jozufozu.flywheel; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.Loader; -import com.jozufozu.flywheel.backend.RenderWork; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.config.FlwConfig; -import com.jozufozu.flywheel.core.Contexts; -import com.jozufozu.flywheel.core.PartialModel; -import com.jozufozu.flywheel.core.QuadConverter; -import com.jozufozu.flywheel.core.compile.ProgramCompiler; -import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; -import com.jozufozu.flywheel.event.EntityWorldHandler; -import com.jozufozu.flywheel.event.ForgeEvents; -import com.jozufozu.flywheel.fabric.event.FlywheelEvents; -import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor; -import com.jozufozu.flywheel.vanilla.VanillaInstances; - -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.fabricmc.fabric.api.resource.ResourceManagerHelper; -import net.minecraft.server.packs.PackType; - -public class FlywheelClient implements ClientModInitializer { - - @Override - public void onInitializeClient() { - Flywheel.initVersion(); - - Backend.init(); - - FlywheelEvents.GATHER_CONTEXT.register(Contexts::flwInit); - ModelLoadingRegistry.INSTANCE.registerModelProvider(PartialModel::onModelRegistry); - ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(PartialModel.ResourceReloadListener.INSTANCE); - - FlywheelEvents.RELOAD_RENDERERS.register(ProgramCompiler::invalidateAll); - - VanillaInstances.init(); - - ResourceManagerHelper.get(PackType.CLIENT_RESOURCES).registerReloadListener(Loader.ResourceReloadListener.INSTANCE); - - WorldRenderEvents.END.register(RenderWork::onRenderWorldLast); - ClientTickEvents.END_CLIENT_TICK.register(InstancedRenderDispatcher::tick); - FlywheelEvents.BEGIN_FRAME.register(InstancedRenderDispatcher::onBeginFrame); - FlywheelEvents.RENDER_LAYER.register(InstancedRenderDispatcher::renderLayer); - FlywheelEvents.RELOAD_RENDERERS.register(InstancedRenderDispatcher::onReloadRenderers); - FlywheelEvents.RELOAD_RENDERERS.register(QuadConverter::onRendererReload); - FlywheelEvents.RELOAD_RENDERERS.register(CrumblingRenderer::onReloadRenderers); - ClientEntityEvents.ENTITY_LOAD.register(EntityWorldHandler::onEntityJoinWorld); - ClientEntityEvents.ENTITY_UNLOAD.register(EntityWorldHandler::onEntityLeaveWorld); - ClientTickEvents.END_CLIENT_TICK.register(ForgeEvents::tickLight); - - FlwConfig.init(); - - // https://github.com/Jozufozu/Flywheel/issues/69 - // Weird issue with accessor loading. - // Only thing I've seen that's close to a fix is to force the class to load before trying to use it. - // From the SpongePowered discord: - // https://discord.com/channels/142425412096491520/626802111455297538/675007581168599041 - Flywheel.LOGGER.info("Successfully loaded {}", PausedPartialTickAccessor.class.getName()); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index f0ffd7935..254e7f081 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -21,8 +21,6 @@ public class Backend { private static FlwEngine engine; - public static GlCompat compat; - private static final Loader loader = new Loader(); public static FlwEngine getEngine() { @@ -43,9 +41,7 @@ public class Backend { } public static void refresh() { - compat = new GlCompat(); - - engine = chooseEngine(compat); + engine = chooseEngine(); } public static boolean isOn() { @@ -77,7 +73,7 @@ public class Backend { RenderWork.enqueue(Minecraft.getInstance().levelRenderer::allChanged); } - private static FlwEngine chooseEngine(GlCompat compat) { + private static FlwEngine chooseEngine() { FlwEngine preferredChoice = FlwConfig.get() .getEngine(); @@ -85,7 +81,7 @@ public class Backend { boolean canUseEngine = switch (preferredChoice) { case OFF -> true; case BATCHING -> !usingShaders; - case INSTANCING -> !usingShaders && compat.instancedArraysSupported(); + case INSTANCING -> !usingShaders && GlCompat.getInstance().instancedArraysSupported(); }; return canUseEngine ? preferredChoice : FlwEngine.OFF; diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 10aafd3b4..3dbde897e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -47,7 +47,7 @@ public class Loader { public static final String PROGRAM_DIR = "flywheel/programs/"; private static final Gson GSON = new GsonBuilder().create(); - private final Map programSpecRegistry = new HashMap<>(); + private final Map programs = new HashMap<>(); private boolean firstLoad = true; @@ -57,7 +57,7 @@ public class Loader { @Nullable public ProgramSpec get(ResourceLocation name) { - return programSpecRegistry.get(name); + return programs.get(name); } public void onResourceManagerReload(ResourceManager manager) { @@ -88,6 +88,8 @@ public class Loader { } private void loadProgramSpecs(ResourceManager manager) { + programs.clear(); + Collection programSpecs = manager.listResources(PROGRAM_DIR, s -> s.endsWith(".json")); for (ResourceLocation location : programSpecs) { @@ -106,24 +108,17 @@ public class Loader { spec.setName(specName); - register(spec); + if (programs.containsKey(specName)) { + throw new IllegalStateException("Program spec '" + specName + "' already registered."); + } + programs.put(specName, spec); + } catch (Exception e) { Backend.LOGGER.error(e); } } } - /** - * Register a shader program. - */ - private void register(ProgramSpec spec) { - ResourceLocation name = spec.name; - if (programSpecRegistry.containsKey(name)) { - throw new IllegalStateException("Program spec '" + name + "' already registered."); - } - programSpecRegistry.put(name, spec); - } - public static class ResourceReloadListener implements ResourceManagerReloadListener, IdentifiableResourceReloadListener { public static final ResourceReloadListener INSTANCE = new ResourceReloadListener(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java index 346db8814..55d8d7037 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java @@ -4,8 +4,8 @@ import java.nio.ByteBuffer; import org.lwjgl.opengl.GL20; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; +import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; public abstract class GlBuffer extends GlObject { @@ -21,7 +21,8 @@ public abstract class GlBuffer extends GlObject { * @return A buffer that will be persistent if the driver supports it. */ public static GlBuffer requestPersistent(GlBufferType type) { - if (Backend.compat.bufferStorageSupported()) { + if (GlCompat.getInstance() + .bufferStorageSupported()) { return new PersistentGlBuffer(type); } else { return new MappedGlBuffer(type); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java index 6c4b474cd..ff81679c2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java @@ -8,10 +8,10 @@ import java.nio.ByteBuffer; import org.lwjgl.opengl.GL30; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlFence; import com.jozufozu.flywheel.backend.gl.error.GlError; import com.jozufozu.flywheel.backend.gl.error.GlException; +import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; public class PersistentGlBuffer extends GlBuffer implements Mappable { @@ -46,7 +46,7 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable { fence.clear(); - Backend.compat.bufferStorage.bufferStorage(type, size, flags); + GlCompat.getInstance().bufferStorage.bufferStorage(type, size, flags); ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, flags); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java index 0d550124f..0aaffe754 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java @@ -20,11 +20,20 @@ import net.minecraft.Util; */ public class GlCompat { + private static GlCompat instance; + + public static GlCompat getInstance() { + if (instance == null) { + instance = new GlCompat(); + } + return instance; + } + public final InstancedArrays instancedArrays; public final BufferStorage bufferStorage; public final boolean amd; - public GlCompat() { + private GlCompat() { GLCapabilities caps = GL.createCapabilities(); instancedArrays = getLatest(InstancedArrays.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 38936f656..7f741cd8c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -14,13 +14,16 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; +import com.jozufozu.flywheel.backend.instancing.ratelimit.BandedPrimeLimiter; +import com.jozufozu.flywheel.backend.instancing.ratelimit.DistanceUpdateLimiter; +import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter; +import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.light.LightUpdater; import com.mojang.math.Vector3f; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Camera; import net.minecraft.core.BlockPos; -import net.minecraft.util.Mth; public abstract class InstanceManager implements InstancingEngine.OriginShiftListener { @@ -33,8 +36,8 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift protected final Object2ObjectOpenHashMap tickableInstances; protected final Object2ObjectOpenHashMap dynamicInstances; - protected int frame; - protected int tick; + protected DistanceUpdateLimiter frame; + protected DistanceUpdateLimiter tick; public InstanceManager(MaterialManager materialManager) { this.materialManager = materialManager; @@ -44,6 +47,17 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift this.dynamicInstances = new Object2ObjectOpenHashMap<>(); this.tickableInstances = new Object2ObjectOpenHashMap<>(); + + frame = createUpdateLimiter(); + tick = createUpdateLimiter(); + } + + protected DistanceUpdateLimiter createUpdateLimiter() { + if (FlwConfig.get().limitUpdates()) { + return new BandedPrimeLimiter(); + } else { + return new NonLimiter(); + } } /** @@ -86,7 +100,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift *

*/ public void tick(TaskEngine taskEngine, double cameraX, double cameraY, double cameraZ) { - tick++; + tick.tick(); processQueuedUpdates(); // integer camera pos as a micro-optimization @@ -112,7 +126,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } } - private void tickInstance(int cX, int cY, int cZ, TickableInstance instance) { + protected void tickInstance(int cX, int cY, int cZ, TickableInstance instance) { if (!instance.decreaseTickRateWithDistance()) { instance.tick(); return; @@ -124,11 +138,11 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift int dY = pos.getY() - cY; int dZ = pos.getZ() - cZ; - if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0) instance.tick(); + if (tick.shouldUpdate(dX, dY, dZ)) instance.tick(); } public void beginFrame(TaskEngine taskEngine, Camera info) { - frame++; + frame.tick(); processQueuedAdditions(); Vector3f look = info.getLookVector(); @@ -151,8 +165,7 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift List sub = instances.subList(start, end); taskEngine.submit(() -> { for (DynamicInstance dyn : sub) { - if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ)) - dyn.beginFrame(); + updateInstance(dyn, lookX, lookY, lookZ, cX, cY, cZ); } }); @@ -160,6 +173,28 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } } + protected void updateInstance(DynamicInstance dyn, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { + if (!dyn.decreaseFramerateWithDistance()) { + dyn.beginFrame(); + return; + } + + BlockPos worldPos = dyn.getWorldPosition(); + int dX = worldPos.getX() - cX; + int dY = worldPos.getY() - cY; + int dZ = worldPos.getZ() - cZ; + + // is it more than 2 blocks behind the camera? + int dist = 2; + float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ; + if (dot < 0) { + return; + } + + if (frame.shouldUpdate(dX, dY, dZ)) + dyn.beginFrame(); + } + public void add(T obj) { if (!Backend.isOn()) return; @@ -268,29 +303,6 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } } - protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) { - int dX = worldPos.getX() - cX; - int dY = worldPos.getY() - cY; - int dZ = worldPos.getZ() - cZ; - - // is it more than 2 blocks behind the camera? - int dist = 2; - float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ; - if (dot < 0) return false; - - return (frame % getUpdateDivisor(dX, dY, dZ)) == 0; - } - - // 1 followed by the prime numbers - private static final int[] divisorSequence = new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 }; - protected int getUpdateDivisor(int dX, int dY, int dZ) { - int dSq = dX * dX + dY * dY + dZ * dZ; - - int i = (dSq / 2048); - - return divisorSequence[Mth.clamp(i, 0, divisorSequence.length - 1)]; - } - protected void addInternal(T obj) { if (!Backend.isOn()) return; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 9595069eb..02ec9ceac 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -1,8 +1,5 @@ package com.jozufozu.flywheel.backend.instancing; -import java.util.List; - -import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.backend.Backend; @@ -137,10 +134,4 @@ public class InstanceWorld { .forEach(entityInstanceManager::add); } - public void getDebugString(List debug) { - debug.add(""); - debug.add("Flywheel: " + Flywheel.VERSION); - debug.add("B: " + blockEntityInstanceManager.getObjectCount() + ", E: " + entityInstanceManager.getObjectCount()); - engine.addDebugInfo(debug); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index f32a25d71..dca55bce3 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -2,7 +2,10 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.List; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.config.FlwCommands; +import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.RenderLayerEvent; @@ -62,7 +65,6 @@ public class InstancedRenderDispatcher { } public static void tick(Minecraft mc) { - if (!Backend.isGameActive()) { return; } @@ -104,7 +106,17 @@ public class InstancedRenderDispatcher { } public static void getDebugString(List debug) { - instanceWorlds.get(Minecraft.getInstance().level) - .getDebugString(debug); + debug.add(""); + debug.add("Flywheel: " + Flywheel.getVersion()); + + if (Backend.isOn()) { + InstanceWorld instanceWorld = instanceWorlds.get(Minecraft.getInstance().level); + + debug.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString()); + debug.add("B: " + instanceWorld.blockEntityInstanceManager.getObjectCount() + ", E: " + instanceWorld.entityInstanceManager.getObjectCount()); + instanceWorld.engine.addDebugInfo(debug); + } else { + debug.add("Disabled"); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index ffac02ae3..3f0ff7c87 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -6,11 +6,11 @@ import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; +import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.backend.model.BufferedModel; import com.jozufozu.flywheel.backend.model.ModelAllocator; @@ -198,7 +198,7 @@ public class GPUInstancer extends AbstractInstancer { vao.bindAttributes(attributeBaseIndex, instanceFormat); for (int i = 0; i < instanceFormat.getAttributeCount(); i++) { - Backend.compat.instancedArrays.vertexAttribDivisor(attributeBaseIndex + i, 1); + GlCompat.getInstance().instancedArrays.vertexAttribDivisor(attributeBaseIndex + i, 1); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java index 6c640f716..73ea3e1c5 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedMaterialGroup.java @@ -7,8 +7,8 @@ import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.MaterialGroup; import com.jozufozu.flywheel.api.struct.Instanced; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.RenderLayer; +import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.backend.model.FallbackAllocator; import com.jozufozu.flywheel.backend.model.ModelAllocator; import com.jozufozu.flywheel.backend.model.ModelPool; @@ -40,7 +40,8 @@ public class InstancedMaterialGroup

implements MaterialG public InstancedMaterialGroup(InstancingEngine

owner, RenderType type) { this.owner = owner; this.type = type; - if (Backend.compat.onAMDWindows()) { + if (GlCompat.getInstance() + .onAMDWindows()) { this.allocator = FallbackAllocator.INSTANCE; } else { this.allocator = new ModelPool(Formats.POS_TEX_NORMAL, 2048); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index 8a3c4c376..f0a696f02 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -155,9 +155,9 @@ public class InstancingEngine

implements Engine { @Override public void addDebugInfo(List info) { info.add("GL33 Instanced Arrays"); - info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); info.add("Instances: " + getGroupsToRender(null).mapToInt(InstancedMaterialGroup::getInstanceCount).sum()); info.add("Vertices: " + getGroupsToRender(null).mapToInt(InstancedMaterialGroup::getVertexCount).sum()); + info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); } @FunctionalInterface diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/BandedPrimeLimiter.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/BandedPrimeLimiter.java new file mode 100644 index 000000000..278f5ff1e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/BandedPrimeLimiter.java @@ -0,0 +1,28 @@ +package com.jozufozu.flywheel.backend.instancing.ratelimit; + +import net.minecraft.util.Mth; + +public class BandedPrimeLimiter implements DistanceUpdateLimiter { + // 1 followed by the prime numbers + private static final int[] divisorSequence = new int[] { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 }; + + private int tickCount = 0; + + @Override + public void tick() { + tickCount++; + } + + @Override + public boolean shouldUpdate(int dX, int dY, int dZ) { + return (tickCount % getUpdateDivisor(dX, dY, dZ)) == 0; + } + + protected int getUpdateDivisor(int dX, int dY, int dZ) { + int dSq = dX * dX + dY * dY + dZ * dZ; + + int i = (dSq / 2048); + + return divisorSequence[Mth.clamp(i, 0, divisorSequence.length - 1)]; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/DistanceUpdateLimiter.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/DistanceUpdateLimiter.java new file mode 100644 index 000000000..7fb3ceff6 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/DistanceUpdateLimiter.java @@ -0,0 +1,20 @@ +package com.jozufozu.flywheel.backend.instancing.ratelimit; + +/** + * Interface for rate-limiting updates based on an object's distance from the camera. + */ +public interface DistanceUpdateLimiter { + /** + * Call this before every update. + */ + void tick(); + + /** + * Check to see if an object at the given position relative to the camera should be updated. + * @param dX The X distance from the camera. + * @param dY The Y distance from the camera. + * @param dZ The Z distance from the camera. + * @return {@code true} if the object should be updated, {@code false} otherwise. + */ + boolean shouldUpdate(int dX, int dY, int dZ); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/NonLimiter.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/NonLimiter.java new file mode 100644 index 000000000..ea5afb230 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/ratelimit/NonLimiter.java @@ -0,0 +1,13 @@ +package com.jozufozu.flywheel.backend.instancing.ratelimit; + +public class NonLimiter implements DistanceUpdateLimiter { + @Override + public void tick() { + // noop + } + + @Override + public boolean shouldUpdate(int dX, int dY, int dZ) { + return true; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/config/ConfigCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java similarity index 90% rename from src/main/java/com/jozufozu/flywheel/config/ConfigCommands.java rename to src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 631c23592..d2b9d989b 100644 --- a/src/main/java/com/jozufozu/flywheel/config/ConfigCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -18,7 +18,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; -public final class ConfigCommands { +public final class FlwCommands { public static void init(FlwConfig config) { ConfigCommandBuilder commandBuilder = new ConfigCommandBuilder("flywheel"); @@ -44,6 +44,17 @@ public final class ConfigCommands { } )); + commandBuilder.addOption(config.limitUpdates, (builder, option) -> booleanOptionCommand(builder, config, option, + (source, value) -> { + Component text = new TextComponent("Update limiting is currently: ").append(boolToText(value)); + source.sendFeedback(text); + }, + (source, value) -> { + Component text = boolToText(value).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE)); + source.sendFeedback(text); + } + )); + commandBuilder.build(); } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java index b0df82a42..2f9a66e37 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwConfig.java @@ -38,6 +38,8 @@ public class FlwConfig { public final EnumOption engine = addOption(new EnumOption<>("engine", FlwEngine.INSTANCING)); /** Enable or disable a debug overlay that colors pixels by their normal */ public final BooleanOption debugNormals = addOption(new BooleanOption("debugNormals", false)); + /** Enable or disable instance update limiting with distance. */ + public final BooleanOption limitUpdates = addOption(new BooleanOption("limitUpdates", true)); public FlwConfig(File file) { this.file = file; @@ -49,7 +51,7 @@ public class FlwConfig { public static void init() { INSTANCE.load(); - ConfigCommands.init(INSTANCE); + FlwCommands.init(INSTANCE); } public FlwEngine getEngine() { @@ -60,6 +62,10 @@ public class FlwConfig { return debugNormals.get(); } + public boolean limitUpdates() { + return limitUpdates.get(); + } + public void load() { if (file.exists()) { try (FileReader reader = new FileReader(file)) { diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java index 4931c63fd..43d0d7193 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/ProgramCompiler.java @@ -54,6 +54,7 @@ public class ProgramCompiler

extends Memoizer left) { + public static void addToDebugScreen(List right) { - InstancedRenderDispatcher.getDebugString(left); + InstancedRenderDispatcher.getDebugString(right); } public static void unloadWorld(ClientLevel world) { diff --git a/src/main/java/com/jozufozu/flywheel/fabric/mixin/DebugScreenOverlayMixin.java b/src/main/java/com/jozufozu/flywheel/fabric/mixin/DebugScreenOverlayMixin.java index 7a55f1ce7..ae932936a 100644 --- a/src/main/java/com/jozufozu/flywheel/fabric/mixin/DebugScreenOverlayMixin.java +++ b/src/main/java/com/jozufozu/flywheel/fabric/mixin/DebugScreenOverlayMixin.java @@ -14,8 +14,8 @@ import net.minecraft.client.gui.components.DebugScreenOverlay; @Mixin(DebugScreenOverlay.class) public abstract class DebugScreenOverlayMixin extends GuiComponent { - @Inject(method = "getGameInformation", at = @At("RETURN")) - private void modifyLeftText(CallbackInfoReturnable> cir) { + @Inject(method = "getSystemInformation", at = @At("RETURN")) + private void modifyRightText(CallbackInfoReturnable> cir) { ForgeEvents.addToDebugScreen(cir.getReturnValue()); } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b4e42c927..ee0570f12 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -21,7 +21,7 @@ "environment": "client", "entrypoints": { "client": [ - "com.jozufozu.flywheel.FlywheelClient" + "com.jozufozu.flywheel.Flywheel" ] }, "mixins": [