From 7230489b19b25f344c9cb2ac646fa444c0816c78 Mon Sep 17 00:00:00 2001 From: zelophed Date: Sat, 27 Mar 2021 00:41:09 +0100 Subject: [PATCH 1/4] placement helper oversight - shout-out to d'arcy :P --- .../relays/advanced/SpeedControllerBlock.java | 24 +++--- .../relays/elementary/CogwheelBlockItem.java | 76 ++++++++++--------- 2 files changed, 52 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerBlock.java index 90d8667f6..14df97837 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerBlock.java @@ -1,15 +1,5 @@ package com.simibubi.create.content.contraptions.relays.advanced; -import com.simibubi.create.AllBlocks; -import com.simibubi.create.AllShapes; -import com.simibubi.create.AllTileEntities; -import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock; -import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock; -import com.simibubi.create.content.contraptions.relays.elementary.CogwheelBlockItem; -import com.simibubi.create.foundation.block.ITE; -import com.simibubi.create.foundation.utility.placement.IPlacementHelper; -import com.simibubi.create.foundation.utility.placement.PlacementHelpers; -import com.simibubi.create.foundation.utility.placement.PlacementOffset; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -29,6 +19,16 @@ import net.minecraft.world.IBlockReader; import net.minecraft.world.World; import java.util.function.Predicate; +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllShapes; +import com.simibubi.create.AllTileEntities; +import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock; +import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock; +import com.simibubi.create.content.contraptions.relays.elementary.CogwheelBlockItem; +import com.simibubi.create.foundation.block.ITE; +import com.simibubi.create.foundation.utility.placement.IPlacementHelper; +import com.simibubi.create.foundation.utility.placement.PlacementHelpers; +import com.simibubi.create.foundation.utility.placement.PlacementOffset; public class SpeedControllerBlock extends HorizontalAxisKineticBlock implements ITE { @@ -100,8 +100,8 @@ public class SpeedControllerBlock extends HorizontalAxisKineticBlock implements Axis newAxis = state.get(HORIZONTAL_AXIS) == Axis.X ? Axis.Z : Axis.X; - if (CogwheelBlockItem.DiagonalCogHelper.hasLargeCogwheelNeighbor(world, newPos, newAxis) - || CogwheelBlockItem.DiagonalCogHelper.hasSmallCogwheelNeighbor(world, newPos, newAxis)) + if (CogwheelBlockItem.hasLargeCogwheelNeighbor(world, newPos, newAxis) + || CogwheelBlockItem.hasSmallCogwheelNeighbor(world, newPos, newAxis)) return PlacementOffset.fail(); return PlacementOffset.success(newPos, s -> s.with(CogWheelBlock.AXIS, newAxis)); diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/CogwheelBlockItem.java b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/CogwheelBlockItem.java index fef1d1459..6d8018e30 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/CogwheelBlockItem.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/elementary/CogwheelBlockItem.java @@ -1,15 +1,5 @@ package com.simibubi.create.content.contraptions.relays.elementary; -import com.simibubi.create.AllBlocks; -import com.simibubi.create.AllShapes; -import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock; -import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock; -import com.simibubi.create.content.contraptions.base.IRotate; -import com.simibubi.create.foundation.advancement.AllTriggers; -import com.simibubi.create.foundation.utility.Iterate; -import com.simibubi.create.foundation.utility.placement.IPlacementHelper; -import com.simibubi.create.foundation.utility.placement.PlacementHelpers; -import com.simibubi.create.foundation.utility.placement.PlacementOffset; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; @@ -27,6 +17,16 @@ import net.minecraft.world.World; import java.util.List; import java.util.function.Predicate; +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllShapes; +import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock; +import com.simibubi.create.content.contraptions.base.HorizontalKineticBlock; +import com.simibubi.create.content.contraptions.base.IRotate; +import com.simibubi.create.foundation.advancement.AllTriggers; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.placement.IPlacementHelper; +import com.simibubi.create.foundation.utility.placement.PlacementHelpers; +import com.simibubi.create.foundation.utility.placement.PlacementOffset; import static com.simibubi.create.content.contraptions.base.RotatedPillarKineticBlock.AXIS; @@ -169,6 +169,10 @@ public class CogwheelBlockItem extends BlockItem { for (Direction dir : directions) { BlockPos newPos = pos.offset(dir) .offset(side); + + if (hasLargeCogwheelNeighbor(world, newPos, dir.getAxis()) || hasSmallCogwheelNeighbor(world, newPos, dir.getAxis())) + continue; + if (!world.getBlockState(newPos) .getMaterial() .isReplaceable()) @@ -225,30 +229,6 @@ public class CogwheelBlockItem extends BlockItem { .subtract(ray.getHitVec() .align(Iterate.axisSet))); } - - static public boolean hasLargeCogwheelNeighbor(World world, BlockPos pos, Direction.Axis axis) { - for (Direction dir : Iterate.directions) { - if (dir.getAxis() == axis) - continue; - - if (AllBlocks.LARGE_COGWHEEL.has(world.getBlockState(pos.offset(dir)))) - return true; - } - - return false; - } - - static public boolean hasSmallCogwheelNeighbor(World world, BlockPos pos, Direction.Axis axis) { - for (Direction dir : Iterate.directions) { - if (dir.getAxis() == axis) - continue; - - if (AllBlocks.COGWHEEL.has(world.getBlockState(pos.offset(dir)))) - return true; - } - - return false; - } } @MethodsReturnNonnullByDefault @@ -294,8 +274,8 @@ public class CogwheelBlockItem extends BlockItem { .isReplaceable()) continue; - if (DiagonalCogHelper.hasLargeCogwheelNeighbor(world, newPos, newAxis) - || DiagonalCogHelper.hasSmallCogwheelNeighbor(world, newPos, newAxis)) + if (hasLargeCogwheelNeighbor(world, newPos, newAxis) + || hasSmallCogwheelNeighbor(world, newPos, newAxis)) return PlacementOffset.fail(); return PlacementOffset.success(newPos, s -> s.with(CogWheelBlock.AXIS, newAxis)); @@ -303,5 +283,29 @@ public class CogwheelBlockItem extends BlockItem { return PlacementOffset.fail(); } + + } + static public boolean hasLargeCogwheelNeighbor(World world, BlockPos pos, Axis axis) { + for (Direction dir : Iterate.directions) { + if (dir.getAxis() == axis) + continue; + + if (AllBlocks.LARGE_COGWHEEL.has(world.getBlockState(pos.offset(dir)))) + return true; + } + + return false; + } + + static public boolean hasSmallCogwheelNeighbor(World world, BlockPos pos, Axis axis) { + for (Direction dir : Iterate.directions) { + if (dir.getAxis() == axis) + continue; + + if (AllBlocks.COGWHEEL.has(world.getBlockState(pos.offset(dir)))) + return true; + } + + return false; } } From 0b25f662dc0f62049188f706543a56bb0f75d5c2 Mon Sep 17 00:00:00 2001 From: JozsefA Date: Fri, 26 Mar 2021 16:47:37 -0700 Subject: [PATCH 2/4] Frame rate and tick rate limiting with distance. - Significant performance improvement when dealing with massive amounts of dynamic instances, otherwise marginal. --- .../components/deployer/DeployerInstance.java | 2 + .../render/ContraptionKineticRenderer.java | 7 +- .../render/ContraptionRenderDispatcher.java | 15 +--- .../render/RenderedContraption.java | 11 +-- .../foundation/mixin/RenderHooksMixin.java | 5 +- .../foundation/render/KineticRenderer.java | 5 +- .../render/backend/FastRenderDispatcher.java | 8 +- .../backend/instancing/IDynamicInstance.java | 15 +++- .../render/backend/instancing/IInstance.java | 13 ++++ .../backend/instancing/ITickableInstance.java | 15 +++- .../instancing/InstancedTileRenderer.java | 77 ++++++++++++++++--- .../instancing/TileEntityInstance.java | 11 ++- 12 files changed, 141 insertions(+), 43 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/render/backend/instancing/IInstance.java diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerInstance.java index f05361148..bb6b8f6bb 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerInstance.java @@ -51,6 +51,8 @@ public class DeployerInstance extends ShaftInstance implements IDynamicInstance, relight(pos, pole.getInstance()); updateRotation(pole, hand, yRot, zRot, zRotPole); + + beginFrame(); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionKineticRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionKineticRenderer.java index 20d0bc6a5..0fcb9e109 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionKineticRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionKineticRenderer.java @@ -15,6 +15,8 @@ import com.simibubi.create.foundation.render.backend.instancing.*; import com.simibubi.create.foundation.render.backend.instancing.impl.OrientedModel; import com.simibubi.create.foundation.render.backend.instancing.impl.TransformedModel; + +import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.util.math.BlockPos; import net.minecraft.world.gen.feature.template.Template; import org.apache.commons.lang3.tuple.Pair; @@ -44,14 +46,13 @@ public class ContraptionKineticRenderer extends InstancedTileRenderer(this, AllProgramSpecs.C_ACTOR, ActorModel::new)); } - @Override public void tick() { actors.forEach(ActorInstance::tick); } @Override - public void beginFrame(double cameraX, double cameraY, double cameraZ) { - super.beginFrame(cameraX, cameraY, cameraZ); + public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) { + super.beginFrame(info, cameraX, cameraY, cameraZ); actors.forEach(ActorInstance::beginFrame); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java index bed4c8913..64497761d 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java @@ -28,20 +28,11 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BlockModelRenderer; -import net.minecraft.client.renderer.BlockRendererDispatcher; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.IRenderTypeBuffer; -import net.minecraft.client.renderer.LightTexture; -import net.minecraft.client.renderer.Matrix4f; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.RenderTypeLookup; -import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.SectionPos; import net.minecraft.world.ILightReader; import net.minecraft.world.LightType; import net.minecraft.world.World; @@ -94,9 +85,9 @@ public class ContraptionRenderDispatcher { return contraption; } - public static void beginFrame(double camX, double camY, double camZ) { + public static void beginFrame(ActiveRenderInfo info, double camX, double camY, double camZ) { for (RenderedContraption renderer : renderers.values()) { - renderer.beginFrame(camX, camY, camZ); + renderer.beginFrame(info, camX, camY, camZ); } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java index 66e49a9bc..6f23f4a53 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java @@ -20,12 +20,7 @@ import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationW import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BlockModelRenderer; -import net.minecraft.client.renderer.BlockRendererDispatcher; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.Matrix4f; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.RenderTypeLookup; +import net.minecraft.client.renderer.*; import net.minecraft.client.renderer.model.IBakedModel; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; @@ -87,8 +82,8 @@ public class RenderedContraption { } } - public void beginFrame(double camX, double camY, double camZ) { - kinetics.beginFrame(camX, camY, camZ); + public void beginFrame(ActiveRenderInfo info, double camX, double camY, double camZ) { + kinetics.beginFrame(info, camX, camY, camZ); AbstractContraptionEntity entity = contraption.entity; float pt = AnimationTickHolder.getPartialTicks(); diff --git a/src/main/java/com/simibubi/create/foundation/mixin/RenderHooksMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/RenderHooksMixin.java index 0d6e73641..e6a6f30ce 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/RenderHooksMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/RenderHooksMixin.java @@ -4,7 +4,6 @@ import com.simibubi.create.foundation.render.KineticRenderer; import net.minecraft.block.BlockState; import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import org.lwjgl.opengl.GL20; @@ -57,8 +56,8 @@ public class RenderHooksMixin { double camY = cameraPos.getY(); double camZ = cameraPos.getZ(); - CreateClient.kineticRenderer.get(world).beginFrame(camX, camY, camZ); - ContraptionRenderDispatcher.beginFrame(camX, camY, camZ); + CreateClient.kineticRenderer.get(world).beginFrame(info, camX, camY, camZ); + ContraptionRenderDispatcher.beginFrame(info, camX, camY, camZ); } @Inject(at = @At("TAIL"), method = "checkBlockRerender") diff --git a/src/main/java/com/simibubi/create/foundation/render/KineticRenderer.java b/src/main/java/com/simibubi/create/foundation/render/KineticRenderer.java index e02db4d54..250bc904b 100644 --- a/src/main/java/com/simibubi/create/foundation/render/KineticRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/render/KineticRenderer.java @@ -15,6 +15,7 @@ import com.simibubi.create.foundation.render.backend.instancing.RenderMaterial; import com.simibubi.create.foundation.render.backend.instancing.impl.OrientedModel; import com.simibubi.create.foundation.render.backend.instancing.impl.TransformedModel; +import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.RenderType; import net.minecraft.tileentity.TileEntity; @@ -42,7 +43,7 @@ public class KineticRenderer extends InstancedTileRenderer { } @Override - public void beginFrame(double cameraX, double cameraY, double cameraZ) { + public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) { int cX = MathHelper.floor(cameraX); int cY = MathHelper.floor(cameraY); int cZ = MathHelper.floor(cameraZ); @@ -62,7 +63,7 @@ public class KineticRenderer extends InstancedTileRenderer { instancedTiles.forEach(this::add); } - super.beginFrame(cameraX, cameraY, cameraZ); + super.beginFrame(info, cameraX, cameraY, cameraZ); } @Override diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java index 08e31dcc1..f118eeeef 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java @@ -17,6 +17,7 @@ import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.Vector3f; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; import net.minecraft.potion.Effects; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.MathHelper; @@ -37,10 +38,13 @@ public class FastRenderDispatcher { } public static void tick() { - ClientWorld world = Minecraft.getInstance().world; + Minecraft mc = Minecraft.getInstance(); + ClientWorld world = mc.world; KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(world); - kineticRenderer.tick(); + + Entity renderViewEntity = mc.renderViewEntity; + kineticRenderer.tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ()); ConcurrentHashMap.KeySetView map = queuedUpdates.get(world); map diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IDynamicInstance.java b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IDynamicInstance.java index 90c26e0a8..8ee257a3c 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IDynamicInstance.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IDynamicInstance.java @@ -9,9 +9,22 @@ package com.simibubi.create.foundation.render.backend.instancing; *

If your goal is offloading work to shaders, but you're unsure exactly how you need * to parameterize the instances, you're encouraged to implement this for prototyping. */ -public interface IDynamicInstance { +public interface IDynamicInstance extends IInstance { /** * Called every frame. */ void beginFrame(); + + /** + * As a further optimization, dynamic instances that are far away are ticked less often. + * This behavior can be disabled by returning false. + * + *
You might want to opt out of this if you want your animations to remain smooth + * even when far away from the camera. It is recommended to keep this as is, however. + * + * @return true if your instance should be slow ticked. + */ + default boolean decreaseFramerateWithDistance() { + return true; + } } diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IInstance.java b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IInstance.java new file mode 100644 index 000000000..88ec0eb22 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/IInstance.java @@ -0,0 +1,13 @@ +package com.simibubi.create.foundation.render.backend.instancing; + +import net.minecraft.util.math.BlockPos; + +/** + * A general interface providing information about any type of thing that could use + * Flywheel's instanced rendering. Right now, that's only {@link InstancedTileRenderer}, + * but there could be an entity equivalent in the future. + */ +public interface IInstance { + + BlockPos getWorldPosition(); +} diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/ITickableInstance.java b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/ITickableInstance.java index 196ef9360..c9280973b 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/ITickableInstance.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/ITickableInstance.java @@ -16,10 +16,23 @@ package com.simibubi.create.foundation.render.backend.instancing; * * */ -public interface ITickableInstance { +public interface ITickableInstance extends IInstance { /** * Called every tick. */ void tick(); + + /** + * As a further optimization, tickable instances that are far away are ticked less often. + * This behavior can be disabled by returning false. + * + *
You might want to opt out of this if you want your animations to remain smooth + * even when far away from the camera. It is recommended to keep this as is, however. + * + * @return true if your instance should be slow ticked. + */ + default boolean decreaseTickRateWithDistance() { + return true; + } } diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java index ba4592852..80574a142 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java @@ -10,11 +10,9 @@ import com.simibubi.create.foundation.render.backend.gl.BasicProgram; import com.simibubi.create.foundation.render.backend.gl.shader.ShaderCallback; import com.simibubi.create.foundation.render.backend.instancing.impl.ModelData; import com.simibubi.create.foundation.render.backend.instancing.impl.OrientedData; -import com.simibubi.create.foundation.utility.AnimationTickHolder; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.Matrix4f; -import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.*; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockReader; @@ -30,6 +28,8 @@ public abstract class InstancedTileRenderer

{ protected Map, RenderMaterial> materials = new HashMap<>(); + protected int frame; + protected InstancedTileRenderer() { registerMaterials(); } @@ -38,15 +38,74 @@ public abstract class InstancedTileRenderer

{ public abstract void registerMaterials(); - public void tick() { - if (tickableInstances.size() > 0) - tickableInstances.values().forEach(ITickableInstance::tick); + public void tick(double cameraX, double cameraY, double cameraZ) { + // integer camera pos + int cX = (int) cameraX; + int cY = (int) cameraY; + int cZ = (int) cameraZ; + + if (tickableInstances.size() > 0) { + for (ITickableInstance instance : tickableInstances.values()) { + if (!instance.decreaseTickRateWithDistance()) { + instance.tick(); + continue; + } + + BlockPos pos = instance.getWorldPosition(); + + int dX = pos.getX() - cX; + int dY = pos.getY() - cY; + int dZ = pos.getZ() - cZ; + + int dSq = dX * dX + dY * dY + dZ * dZ; + + int divisor = (dSq / 1024) + 1; + + if (frame % divisor == 0) + instance.tick(); + } + } } - public void beginFrame(double cameraX, double cameraY, double cameraZ) { + public void beginFrame(ActiveRenderInfo info, double cameraX, double cameraY, double cameraZ) { + frame++; processQueuedAdditions(); - if (dynamicInstances.size() > 0) - dynamicInstances.values().forEach(IDynamicInstance::beginFrame); + + Vector3f look = info.getHorizontalPlane(); + float lookX = look.getX(); + float lookY = look.getY(); + float lookZ = look.getZ(); + + // integer camera pos + int cX = (int) cameraX; + int cY = (int) cameraY; + int cZ = (int) cameraZ; + + if (dynamicInstances.size() > 0) { + for (IDynamicInstance dyn : dynamicInstances.values()) { + if (!dyn.decreaseFramerateWithDistance()) { + dyn.beginFrame(); + continue; + } + + BlockPos pos = dyn.getWorldPosition(); + + int dX = pos.getX() - cX; + int dY = pos.getY() - cY; + int dZ = pos.getZ() - cZ; + + float dot = dX * lookX + dY * lookY + dZ * lookZ; + + if (dot < 0) continue; // is it behind the camera? + + int dSq = dX * dX + dY * dY + dZ * dZ; + + int divisor = (dSq / 1024) + 1; // https://www.desmos.com/calculator/aaycpludsy + + if (frame % divisor == 0) + dyn.beginFrame(); + } + } } public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) { diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/TileEntityInstance.java b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/TileEntityInstance.java index 97da4b18b..768171aee 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/TileEntityInstance.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/TileEntityInstance.java @@ -30,12 +30,13 @@ import java.util.stream.Stream; * * @param The type of {@link TileEntity} your class is an instance of. */ -public abstract class TileEntityInstance { +public abstract class TileEntityInstance implements IInstance { protected final InstancedTileRenderer renderer; protected final T tile; protected final World world; protected final BlockPos pos; + protected final BlockPos instancePos; protected final BlockState blockState; public TileEntityInstance(InstancedTileRenderer renderer, T tile) { @@ -44,6 +45,7 @@ public abstract class TileEntityInstance { this.world = tile.getWorld(); this.pos = tile.getPos(); this.blockState = tile.getBlockState(); + this.instancePos = pos.subtract(renderer.getOriginCoordinate()); } /** @@ -89,7 +91,12 @@ public abstract class TileEntityInstance { * represents should be rendered at to appear in the correct location. */ public BlockPos getInstancePosition() { - return pos.subtract(renderer.getOriginCoordinate()); + return instancePos; + } + + @Override + public BlockPos getWorldPosition() { + return pos; } protected void relight(BlockPos pos, IFlatLight... models) { From d3d338e64b872094abb50a33ae418de8d13d735b Mon Sep 17 00:00:00 2001 From: JozsefA Date: Fri, 26 Mar 2021 22:02:19 -0700 Subject: [PATCH 3/4] STE tick optimization. - SmartTileEntity#tick down to 3.32% from 11.64% cpu time. - Measured by JFR over 2 sessions each >8min. - Test world was the deployer fields. - Iterating over HashMap values is slow. - Collect TileEntityBehaviours into a list when the contents of SmartTileEntity#behaviours changes. --- .../tileEntity/SmartTileEntity.java | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java index 9404b3544..37605d859 100644 --- a/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java +++ b/src/main/java/com/simibubi/create/foundation/tileEntity/SmartTileEntity.java @@ -17,7 +17,9 @@ import net.minecraftforge.items.CapabilityItemHandler; public abstract class SmartTileEntity extends SyncedTileEntity implements ITickableTileEntity { - private Map, TileEntityBehaviour> behaviours; + private final Map, TileEntityBehaviour> behaviours; + // Internally maintained to be identical to behaviorMap.values() in order to improve iteration performance. + private final List behaviourList; private boolean initialized; private boolean firstNbtRead; private int lazyTickRate; @@ -36,6 +38,9 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka ArrayList list = new ArrayList<>(); addBehaviours(list); list.forEach(b -> behaviours.put(b.getType(), b)); + + behaviourList = new ArrayList<>(list.size()); + updateBehaviorList(); } public abstract void addBehaviours(List behaviours); @@ -58,13 +63,11 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka lazyTick(); } - behaviours.values() - .forEach(TileEntityBehaviour::tick); + behaviourList.forEach(TileEntityBehaviour::tick); } public void initialize() { - behaviours.values() - .forEach(TileEntityBehaviour::initialize); + behaviourList.forEach(TileEntityBehaviour::initialize); lazyTick(); } @@ -99,10 +102,11 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka ArrayList list = new ArrayList<>(); addBehavioursDeferred(list); list.forEach(b -> behaviours.put(b.getType(), b)); + + updateBehaviorList(); } super.read(compound); - behaviours.values() - .forEach(tb -> tb.read(compound, clientPacket)); + behaviourList.forEach(tb -> tb.read(compound, clientPacket)); } /** @@ -110,8 +114,7 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka */ protected void write(CompoundNBT compound, boolean clientPacket) { super.write(compound); - behaviours.values() - .forEach(tb -> tb.write(compound, clientPacket)); + behaviourList.forEach(tb -> tb.write(compound, clientPacket)); } @Override @@ -130,19 +133,31 @@ public abstract class SmartTileEntity extends SyncedTileEntity implements ITicka } protected void forEachBehaviour(Consumer action) { - behaviours.values() - .forEach(action); + behaviourList.forEach(action); } protected void attachBehaviourLate(TileEntityBehaviour behaviour) { behaviours.put(behaviour.getType(), behaviour); behaviour.initialize(); + + updateBehaviorList(); } protected void removeBehaviour(BehaviourType type) { TileEntityBehaviour remove = behaviours.remove(type); - if (remove != null) + if (remove != null) { remove.remove(); + updateBehaviorList(); + } + } + + // We don't trust the input to the API will be sane, so we + // update all the contents whenever something changes. It's + // simpler than trying to manipulate the list one element at + // a time. + private void updateBehaviorList() { + behaviourList.clear(); + behaviourList.addAll(behaviours.values()); } @SuppressWarnings("unchecked") From 4675e0ad15a21e37679d0f8102a7f42ac23dbdb9 Mon Sep 17 00:00:00 2001 From: JozsefA Date: Fri, 26 Mar 2021 22:59:47 -0700 Subject: [PATCH 4/4] Stop relying on IDynamicInstance#beginFrame to setup instance state. - Gantry carriages no longer update when not necessary. --- .../components/crank/HandCrankInstance.java | 12 +++-- .../components/flywheel/FlyWheelInstance.java | 16 +++--- .../components/press/PressInstance.java | 12 ++--- .../chassis/StickerInstance.java | 10 +++- .../gantry/GantryCarriageInstance.java | 50 ++++++++++++++----- .../relays/encased/SplitShaftInstance.java | 2 - .../block/depot/EjectorInstance.java | 1 - .../block/mechanicalArm/ArmInstance.java | 25 +++++----- .../block/redstone/AnalogLeverInstance.java | 6 +-- .../block/SchematicannonInstance.java | 2 + 10 files changed, 88 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankInstance.java index f825d1927..8035fb705 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/crank/HandCrankInstance.java @@ -14,11 +14,13 @@ import net.minecraft.util.Direction; public class HandCrankInstance extends SingleRotatingInstance implements IDynamicInstance { + private final HandCrankTileEntity tile; private InstanceKey crank; private Direction facing; - public HandCrankInstance(InstancedTileRenderer modelManager, KineticTileEntity tile) { + public HandCrankInstance(InstancedTileRenderer modelManager, HandCrankTileEntity tile) { super(modelManager, tile); + this.tile = tile; Block block = blockState.getBlock(); AllBlockPartials renderedHandle = null; @@ -30,16 +32,20 @@ public class HandCrankInstance extends SingleRotatingInstance implements IDynami facing = blockState.get(BlockStateProperties.FACING); InstancedModel model = renderedHandle.renderOnDirectionalSouthModel(modelManager, blockState, facing.getOpposite()); crank = model.createInstance(); + + rotateCrank(); } @Override public void beginFrame() { if (crank == null) return; - HandCrankTileEntity crankTile = (HandCrankTileEntity) tile; + rotateCrank(); + } + private void rotateCrank() { Direction.Axis axis = facing.getAxis(); - float angle = (crankTile.independentAngle + AnimationTickHolder.getPartialTicks() * crankTile.chasingVelocity) / 360; + float angle = (tile.independentAngle + AnimationTickHolder.getPartialTicks() * tile.chasingVelocity) / 360; MatrixStack ms = new MatrixStack(); MatrixStacker.of(ms) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/FlyWheelInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/FlyWheelInstance.java index 8b1fc41bf..c3c0d478e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/FlyWheelInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/flywheel/FlyWheelInstance.java @@ -37,11 +37,8 @@ public class FlyWheelInstance extends KineticTileInstance im protected InstanceKey upperSliding; protected InstanceKey lowerSliding; - protected float lastAngle = Float.NaN; - protected boolean firstFrame = true; - public FlyWheelInstance(InstancedTileRenderer modelManager, FlywheelTileEntity tile) { super(modelManager, tile); @@ -70,6 +67,8 @@ public class FlyWheelInstance extends KineticTileInstance im } else { connectors = Collections.emptyList(); } + + animate(tile.angle); } @Override @@ -80,8 +79,14 @@ public class FlyWheelInstance extends KineticTileInstance im float speed = tile.visualSpeed.get(partialTicks) * 3 / 10f; float angle = tile.angle + speed * partialTicks; - if (!firstFrame && Math.abs(angle - lastAngle) < 0.001) return; + if (Math.abs(angle - lastAngle) < 0.001) return; + animate(angle); + + lastAngle = angle; + } + + private void animate(float angle) { MatrixStack ms = new MatrixStack(); MatrixStacker msr = MatrixStacker.of(ms); @@ -121,9 +126,6 @@ public class FlyWheelInstance extends KineticTileInstance im .unCentre(); wheel.getInstance().setTransform(ms); - - lastAngle = angle; - firstFrame = false; } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java index b4a3eff51..e9985adb6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/press/PressInstance.java @@ -14,25 +14,25 @@ import com.simibubi.create.foundation.utility.MatrixStacker; public class PressInstance extends ShaftInstance implements IDynamicInstance { private final InstanceKey pressHead; + private final MechanicalPressTileEntity press; - public PressInstance(InstancedTileRenderer dispatcher, KineticTileEntity tile) { + public PressInstance(InstancedTileRenderer dispatcher, MechanicalPressTileEntity tile) { super(dispatcher, tile); + press = tile; pressHead = AllBlockPartials.MECHANICAL_PRESS_HEAD.renderOnHorizontalModel(dispatcher, blockState).createInstance(); - - transformModels((MechanicalPressTileEntity) tile); + transformModels(); } @Override public void beginFrame() { - MechanicalPressTileEntity press = (MechanicalPressTileEntity) tile; if (!press.running) return; - transformModels(press); + transformModels(); } - private void transformModels(MechanicalPressTileEntity press) { + private void transformModels() { float renderedHeadOffset = getRenderedHeadOffset(press); MatrixStack ms = new MatrixStack(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerInstance.java index 3fd32da92..2d0a9dec7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerInstance.java @@ -28,6 +28,8 @@ public class StickerInstance extends TileEntityInstance imple fakeWorld = tile.getWorld() != Minecraft.getInstance().world; facing = blockState.get(StickerBlock.FACING); offset = blockState.get(StickerBlock.EXTENDED) ? 1 : 0; + + animateHead(offset); } @Override @@ -40,6 +42,12 @@ public class StickerInstance extends TileEntityInstance imple if (MathHelper.epsilonEquals(offset, lastOffset)) return; + animateHead(offset); + + lastOffset = offset; + } + + private void animateHead(float offset) { MatrixStack stack = new MatrixStack(); MatrixStacker.of(stack) .translate(getInstancePosition()) @@ -52,8 +60,6 @@ public class StickerInstance extends TileEntityInstance imple head.getInstance() .setTransform(stack); - - lastOffset = offset; } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/gantry/GantryCarriageInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/gantry/GantryCarriageInstance.java index 1ea699d6c..ff9e962d6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/gantry/GantryCarriageInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/gantry/GantryCarriageInstance.java @@ -15,6 +15,7 @@ import com.simibubi.create.foundation.utility.MatrixStacker; import net.minecraft.client.renderer.Vector3f; import net.minecraft.util.Direction; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; public class GantryCarriageInstance extends ShaftInstance implements IDynamicInstance { @@ -23,8 +24,11 @@ public class GantryCarriageInstance extends ShaftInstance implements IDynamicIns final Direction facing; final Boolean alongFirst; final Direction.Axis rotationAxis; + final float rotationMult; final BlockPos visualPos; + private float lastAngle = Float.NaN; + public GantryCarriageInstance(InstancedTileRenderer dispatcher, KineticTileEntity tile) { super(dispatcher, tile); @@ -36,27 +40,29 @@ public class GantryCarriageInstance extends ShaftInstance implements IDynamicIns alongFirst = blockState.get(GantryCarriageBlock.AXIS_ALONG_FIRST_COORDINATE); rotationAxis = KineticTileEntityRenderer.getRotationAxisOf(tile); + rotationMult = getRotationMultiplier(getGantryAxis(), facing); + visualPos = facing.getAxisDirection() == Direction.AxisDirection.POSITIVE ? tile.getPos() : tile.getPos() .offset(facing.getOpposite()); + + animateCogs(getCogAngle()); } @Override public void beginFrame() { - float angleForTe = GantryCarriageRenderer.getAngleForTe(tile, visualPos, rotationAxis); + float cogAngle = getCogAngle(); - Direction.Axis gantryAxis = Direction.Axis.X; - for (Direction.Axis axis : Iterate.axes) - if (axis != rotationAxis && axis != facing.getAxis()) - gantryAxis = axis; + if (MathHelper.epsilonEquals(cogAngle, lastAngle)) return; - if (gantryAxis == Direction.Axis.Z) - if (facing == Direction.DOWN) - angleForTe *= -1; - if (gantryAxis == Direction.Axis.Y) - if (facing == Direction.NORTH || facing == Direction.EAST) - angleForTe *= -1; + animateCogs(cogAngle); + } + private float getCogAngle() { + return GantryCarriageRenderer.getAngleForTe(tile, visualPos, rotationAxis) * rotationMult; + } + + private void animateCogs(float cogAngle) { MatrixStack ms = new MatrixStack(); MatrixStacker.of(ms) .translate(getInstancePosition()) @@ -65,13 +71,33 @@ public class GantryCarriageInstance extends ShaftInstance implements IDynamicIns .rotateX(facing == Direction.UP ? 0 : facing == Direction.DOWN ? 180 : 90) .rotateY(alongFirst ^ facing.getAxis() == Direction.Axis.Z ? 90 : 0) .translate(0, -9 / 16f, 0) - .multiply(Vector3f.POSITIVE_X.getRadialQuaternion(-angleForTe)) + .multiply(Vector3f.POSITIVE_X.getRadialQuaternion(-cogAngle)) .translate(0, 9 / 16f, 0) .unCentre(); gantryCogs.getInstance().setTransform(ms); } + static float getRotationMultiplier(Direction.Axis gantryAxis, Direction facing) { + float multiplier = 1; + if (gantryAxis == Direction.Axis.Z) + if (facing == Direction.DOWN) + multiplier *= -1; + if (gantryAxis == Direction.Axis.Y) + if (facing == Direction.NORTH || facing == Direction.EAST) + multiplier *= -1; + + return multiplier; + } + + private Direction.Axis getGantryAxis() { + Direction.Axis gantryAxis = Direction.Axis.X; + for (Direction.Axis axis : Iterate.axes) + if (axis != rotationAxis && axis != facing.getAxis()) + gantryAxis = axis; + return gantryAxis; + } + @Override public void updateLight() { relight(pos, gantryCogs.getInstance(), rotatingModel.getInstance()); diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftInstance.java b/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftInstance.java index d55d5f4ce..1a82c8645 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftInstance.java @@ -34,8 +34,6 @@ public class SplitShaftInstance extends KineticTileInstance> clawGrips; private final ArrayList> models; + private final ArmTileEntity arm; private boolean firstTick = true; - public ArmInstance(InstancedTileRenderer modelManager, KineticTileEntity tile) { + public ArmInstance(InstancedTileRenderer modelManager, ArmTileEntity tile) { super(modelManager, tile); RenderMaterial> mat = getTransformMaterial(); @@ -51,33 +51,35 @@ public class ArmInstance extends SingleRotatingInstance implements IDynamicInsta clawGrips = Lists.newArrayList(clawGrip1, clawGrip2); models = Lists.newArrayList(base, lowerBody, upperBody, head, claw, clawGrip1, clawGrip2); + arm = tile; + + animateArm(false); } @Override public void beginFrame() { - ArmTileEntity arm = (ArmTileEntity) tile; boolean settled = arm.baseAngle.settled() && arm.lowerArmAngle.settled() && arm.upperArmAngle.settled() && arm.headAngle.settled(); boolean rave = arm.phase == ArmTileEntity.Phase.DANCING; if (!settled || rave || firstTick) - transformModels(arm, rave); + animateArm(rave); if (settled) firstTick = false; } - private void transformModels(ArmTileEntity arm, boolean rave) { + private void animateArm(boolean rave) { float pt = AnimationTickHolder.getPartialTicks(); int color = 0xFFFFFF; - float baseAngle = arm.baseAngle.get(pt); - float lowerArmAngle = arm.lowerArmAngle.get(pt) - 135; - float upperArmAngle = arm.upperArmAngle.get(pt) - 90; - float headAngle = arm.headAngle.get(pt); + float baseAngle = this.arm.baseAngle.get(pt); + float lowerArmAngle = this.arm.lowerArmAngle.get(pt) - 135; + float upperArmAngle = this.arm.upperArmAngle.get(pt) - 90; + float headAngle = this.arm.headAngle.get(pt); if (rave) { - float renderTick = AnimationTickHolder.getRenderTime(arm.getWorld()) + (tile.hashCode() % 64); + float renderTick = AnimationTickHolder.getRenderTime(this.arm.getWorld()) + (tile.hashCode() % 64); baseAngle = (renderTick * 10) % 360; lowerArmAngle = MathHelper.lerp((MathHelper.sin(renderTick / 4) + 1) / 2, -45, 15); upperArmAngle = MathHelper.lerp((MathHelper.sin(renderTick / 8) + 1) / 4, -45, 95); @@ -85,7 +87,6 @@ public class ArmInstance extends SingleRotatingInstance implements IDynamicInsta color = ColorHelper.rainbowColor(AnimationTickHolder.getTicks() * 100); } - MatrixStack msLocal = new MatrixStack(); MatrixStacker msr = MatrixStacker.of(msLocal); msr.translate(getInstancePosition()); @@ -116,7 +117,7 @@ public class ArmInstance extends SingleRotatingInstance implements IDynamicInsta claw.getInstance() .setTransform(msLocal); - ItemStack item = arm.heldItem; + ItemStack item = this.arm.heldItem; ItemRenderer itemRenderer = Minecraft.getInstance() .getItemRenderer(); boolean hasItem = !item.isEmpty(); diff --git a/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverInstance.java b/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverInstance.java index 8245a1e9f..3be2796a3 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverInstance.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/redstone/AnalogLeverInstance.java @@ -31,16 +31,16 @@ public class AnalogLeverInstance extends TileEntityInstance