Contraptions and engines

- Sort of get the batching engine working for contraptions but this feels wrong
 - TaskEngine gets passed in methods
 - Better naming for TaskEngines
 - Do BufferSource ourselves
 - Change BufferBuilderMixin to allow for injection into BufferBuilder objects
This commit is contained in:
Jozufozu 2021-12-23 14:41:10 -08:00
parent 3cb84a5db5
commit 0a4311b51e
16 changed files with 143 additions and 84 deletions

View file

@ -7,9 +7,9 @@ import net.minecraft.client.renderer.RenderType;
public class ContraptionGroup<P extends ContraptionProgram> extends InstancedMaterialGroup<P> {
private final RenderedContraption contraption;
private final FlwContraption contraption;
public ContraptionGroup(RenderedContraption contraption, InstancingEngine<P> owner, RenderType type) {
public ContraptionGroup(FlwContraption contraption, InstancingEngine<P> owner, RenderType type) {
super(owner, type);
this.contraption = contraption;
@ -20,7 +20,7 @@ public class ContraptionGroup<P extends ContraptionProgram> extends InstancedMat
contraption.setup(program);
}
public static <P extends ContraptionProgram> InstancingEngine.GroupFactory<P> forContraption(RenderedContraption c) {
public static <P extends ContraptionProgram> InstancingEngine.GroupFactory<P> forContraption(FlwContraption c) {
return (materialManager, type) -> new ContraptionGroup<>(c, materialManager, type);
}
}

View file

@ -1,6 +1,5 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import javax.annotation.Nullable;
@ -8,11 +7,12 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.tuple.Pair;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.backend.instancing.ImmediateExecutor;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationWorld;
import net.minecraft.client.Camera;
import net.minecraft.core.BlockPos;
@ -22,11 +22,11 @@ public class ContraptionInstanceManager extends TileInstanceManager {
protected ArrayList<ActorInstance> actors = new ArrayList<>();
private final WeakReference<RenderedContraption> contraption;
private final PlacementSimulationWorld renderWorld;
ContraptionInstanceManager(RenderedContraption contraption, MaterialManager materialManager) {
super(ImmediateExecutor.INSTANCE, materialManager);
this.contraption = new WeakReference<>(contraption);
ContraptionInstanceManager(MaterialManager materialManager, PlacementSimulationWorld contraption) {
super(materialManager);
this.renderWorld = contraption;
}
public void tick() {
@ -34,8 +34,8 @@ public class ContraptionInstanceManager extends TileInstanceManager {
}
@Override
public void beginFrame(Camera info) {
super.beginFrame(info);
public void beginFrame(TaskEngine taskEngine, Camera info) {
super.beginFrame(taskEngine, info);
actors.forEach(ActorInstance::beginFrame);
}
@ -53,7 +53,7 @@ public class ContraptionInstanceManager extends TileInstanceManager {
MovementBehaviour movementBehaviour = AllMovementBehaviours.of(blockInfo.state);
if (movementBehaviour != null && movementBehaviour.hasSpecialInstancedRendering()) {
ActorInstance instance = movementBehaviour.createInstance(materialManager, getContraption().renderWorld, context);
ActorInstance instance = movementBehaviour.createInstance(materialManager, renderWorld, context);
actors.add(instance);
@ -62,9 +62,5 @@ public class ContraptionInstanceManager extends TileInstanceManager {
return null;
}
public RenderedContraption getContraption() {
return contraption.get();
}
}

View file

@ -43,7 +43,7 @@ import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(Dist.CLIENT)
public class ContraptionRenderDispatcher {
private static WorldAttached<ContraptionRenderManager<?>> WORLDS = new WorldAttached<>(SBBContraptionManager::new);
private static WorldAttached<ContraptionRenderingWorld<?>> WORLDS = new WorldAttached<>(SBBContraptionManager::new);
/**
* Reset a contraption's renderer.
@ -113,8 +113,7 @@ public class ContraptionRenderDispatcher {
// Skip individual lighting updates to prevent lag with large contraptions
renderWorld.setBlock(info.pos, info.state, Block.UPDATE_SUPPRESS_LIGHT);
renderWorld.updateLightSources();
renderWorld.lighter.runUpdates(Integer.MAX_VALUE, false, false);
renderWorld.runLightingEngine();
return renderWorld;
}
@ -176,7 +175,7 @@ public class ContraptionRenderDispatcher {
}
public static void reset() {
WORLDS.empty(ContraptionRenderManager::delete);
WORLDS.empty(ContraptionRenderingWorld::delete);
if (Backend.isOn()) {
WORLDS = new WorldAttached<>(FlwContraptionManager::new);

View file

@ -1,5 +1,6 @@
package com.simibubi.create.content.contraptions.components.structureMovement.render;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;

View file

@ -17,7 +17,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo> {
public abstract class ContraptionRenderingWorld<C extends ContraptionRenderInfo> {
protected final Level world;
private int removalTimer;
@ -25,7 +25,7 @@ public abstract class ContraptionRenderManager<C extends ContraptionRenderInfo>
protected final Int2ObjectMap<C> renderInfos = new Int2ObjectOpenHashMap<>();
protected final List<C> visible = new ObjectArrayList<>();
public ContraptionRenderManager(LevelAccessor world) {
public ContraptionRenderingWorld(LevelAccessor world) {
this.world = (Level) world;
}

View file

@ -7,13 +7,18 @@ import java.util.Map;
import java.util.function.Supplier;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.instancing.batching.BatchingEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.backend.model.ArrayModelRenderer;
import com.jozufozu.flywheel.backend.model.ModelRenderer;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.model.WorldModel;
import com.jozufozu.flywheel.event.BeginFrameEvent;
import com.jozufozu.flywheel.event.RenderLayerEvent;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity;
@ -30,29 +35,23 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
public class RenderedContraption extends ContraptionRenderInfo {
public class FlwContraption extends ContraptionRenderInfo {
private final ContraptionLighter<?> lighter;
public final InstancingEngine<ContraptionProgram> engine;
public final ContraptionInstanceManager kinetics;
private final Map<RenderType, ModelRenderer> renderLayers = new HashMap<>();
private final Matrix4f modelViewPartial = new Matrix4f();
private final ContraptionInstanceWorld instanceWorld;
private boolean modelViewPartialReady;
// floats because we're uploading this to the gpu
// floats because we upload this to the gpu
private AABB lightBox;
public RenderedContraption(Contraption contraption, PlacementSimulationWorld renderWorld) {
public FlwContraption(Contraption contraption, PlacementSimulationWorld renderWorld) {
super(contraption, renderWorld);
this.lighter = contraption.makeLighter();
this.engine = InstancingEngine.builder(CreateContexts.CWORLD)
.setGroupFactory(ContraptionGroup.forContraption(this))
.setIgnoreOriginCoordinate(true)
.build();
this.kinetics = new ContraptionInstanceManager(this, engine);
this.engine.addListener(this.kinetics);
instanceWorld = new ContraptionInstanceWorld(this);
buildLayers();
if (Backend.isOn()) {
@ -65,7 +64,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
return lighter;
}
public void doRenderLayer(RenderType layer, ContraptionProgram shader) {
public void renderStructureLayer(RenderType layer, ContraptionProgram shader) {
ModelRenderer structure = renderLayers.get(layer);
if (structure != null) {
setup(shader);
@ -73,6 +72,21 @@ public class RenderedContraption extends ContraptionRenderInfo {
}
}
public void renderInstanceLayer(RenderLayerEvent event) {
event.stack.pushPose();
float partialTicks = AnimationTickHolder.getPartialTicks();
AbstractContraptionEntity entity = contraption.entity;
double x = Mth.lerp(partialTicks, entity.xOld, entity.getX());
double y = Mth.lerp(partialTicks, entity.yOld, entity.getY());
double z = Mth.lerp(partialTicks, entity.zOld, entity.getZ());
event.stack.translate(x - event.camX, y - event.camY, z - event.camZ);
ContraptionMatrices.transform(event.stack, getMatrices().getModel());
instanceWorld.engine.render(SerialTaskEngine.INSTANCE, event);
event.stack.popPose();
}
public void beginFrame(BeginFrameEvent event) {
super.beginFrame(event);
@ -81,7 +95,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
if (!isVisible()) return;
kinetics.beginFrame(event.getInfo());
instanceWorld.tileInstanceManager.beginFrame(SerialTaskEngine.INSTANCE, event.getInfo());
Vec3 cameraPos = event.getCameraPos();
@ -113,8 +127,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
lighter.delete();
engine.delete();
kinetics.invalidate();
instanceWorld.delete();
}
private void buildLayers() {
@ -141,7 +154,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
.canInstance(te.getType())) {
Level world = te.getLevel();
te.setLevel(renderWorld);
kinetics.add(te);
instanceWorld.tileInstanceManager.add(te);
te.setLevel(world);
}
}
@ -149,7 +162,7 @@ public class RenderedContraption extends ContraptionRenderInfo {
}
private void buildActors() {
contraption.getActors().forEach(kinetics::createActor);
contraption.getActors().forEach(instanceWorld.tileInstanceManager::createActor);
}
public static void setupModelViewPartial(Matrix4f matrix, Matrix4f modelMatrix, AbstractContraptionEntity entity, double camX, double camY, double camZ, float pt) {
@ -160,4 +173,38 @@ public class RenderedContraption extends ContraptionRenderInfo {
matrix.multiply(modelMatrix);
}
public void tick() {
instanceWorld.tileInstanceManager.tick();
}
public static class ContraptionInstanceWorld {
private final Engine engine;
private final ContraptionInstanceManager tileInstanceManager;
public ContraptionInstanceWorld(FlwContraption parent) {
switch (Backend.getInstance().getEngine()) {
case INSTANCING -> {
InstancingEngine<ContraptionProgram> engine = InstancingEngine.builder(CreateContexts.CWORLD)
.setGroupFactory(ContraptionGroup.forContraption(parent))
.setIgnoreOriginCoordinate(true)
.build();
tileInstanceManager = new ContraptionInstanceManager(engine, parent.renderWorld);
engine.addListener(tileInstanceManager);
this.engine = engine;
}
case BATCHING -> {
engine = new BatchingEngine();
tileInstanceManager = new ContraptionInstanceManager(engine, parent.renderWorld);
}
default -> throw new IllegalArgumentException("Unknown engine type");
}
}
public void delete() {
engine.delete();
tileInstanceManager.invalidate();
}
}
}

View file

@ -17,7 +17,7 @@ import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationW
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.LevelAccessor;
public class FlwContraptionManager extends ContraptionRenderManager<RenderedContraption> {
public class FlwContraptionManager extends ContraptionRenderingWorld<FlwContraption> {
public FlwContraptionManager(LevelAccessor world) {
super(world);
@ -27,8 +27,8 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
public void tick() {
super.tick();
for (RenderedContraption contraption : visible) {
contraption.kinetics.tick();
for (FlwContraption contraption : visible) {
contraption.tick();
}
}
@ -52,8 +52,8 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
structureShader.uploadViewProjection(event.viewProjection);
structureShader.uploadCameraPos(event.camX, event.camY, event.camZ);
for (RenderedContraption renderedContraption : visible) {
renderedContraption.doRenderLayer(layer, structureShader);
for (FlwContraption flwContraption : visible) {
flwContraption.renderStructureLayer(layer, structureShader);
}
GlVertexArray.unbind();
@ -61,8 +61,9 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
if (Backend.isOn()) {
RenderLayer renderLayer = event.getLayer();
if (renderLayer != null) {
for (RenderedContraption renderer : visible) {
renderer.engine.render(event);
for (FlwContraption renderer : visible) {
renderer.renderInstanceLayer(event);
}
}
}
@ -76,9 +77,9 @@ public class FlwContraptionManager extends ContraptionRenderManager<RenderedCont
}
@Override
protected RenderedContraption create(Contraption c) {
protected FlwContraption create(Contraption c) {
PlacementSimulationWorld renderWorld = ContraptionRenderDispatcher.setupRenderWorld(world, c);
return new RenderedContraption(c, renderWorld);
return new FlwContraption(c, renderWorld);
}
@Override

View file

@ -11,7 +11,7 @@ import com.simibubi.create.foundation.utility.worldWrappers.PlacementSimulationW
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.LevelAccessor;
public class SBBContraptionManager extends ContraptionRenderManager<ContraptionRenderInfo> {
public class SBBContraptionManager extends ContraptionRenderingWorld<ContraptionRenderInfo> {
public static final SuperByteBufferCache.Compartment<Pair<Contraption, RenderType>> CONTRAPTION = new SuperByteBufferCache.Compartment<>();
public SBBContraptionManager(LevelAccessor world) {

View file

@ -76,7 +76,7 @@ public class TreeFertilizerItem extends Item {
return super.useOn(context);
}
private class TreesDreamWorld extends PlacementSimulationServerWorld {
private static class TreesDreamWorld extends PlacementSimulationServerWorld {
private final BlockPos saplingPos;
private final BlockState soil;

View file

@ -45,7 +45,7 @@ public class SchematicWorld extends WrappedWorld implements ServerLevelAccessor
protected List<BlockEntity> renderedTileEntities;
protected List<Entity> entities;
protected BoundingBox bounds;
public BlockPos anchor;
public boolean renderMode;
@ -149,12 +149,12 @@ public class SchematicWorld extends WrappedWorld implements ServerLevelAccessor
public int getBrightness(LightLayer p_226658_1_, BlockPos p_226658_2_) {
return 10;
}
@Override
public LevelTickAccess<Block> getBlockTicks() {
return BlackholeTickAccess.emptyLevelList();
}
@Override
public LevelTickAccess<Fluid> getFluidTicks() {
return BlackholeTickAccess.emptyLevelList();
@ -170,7 +170,7 @@ public class SchematicWorld extends WrappedWorld implements ServerLevelAccessor
Predicate<? super T> arg2) {
return Collections.emptyList();
}
@Override
public List<? extends Player> players() {
return Collections.emptyList();

View file

@ -1,5 +1,7 @@
package com.simibubi.create.foundation.utility.worldWrappers;
import javax.annotation.Nullable;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus;
@ -10,7 +12,7 @@ public class DummyStatusListener implements ChunkProgressListener {
public void updateSpawnPos(ChunkPos pCenter) {}
@Override
public void onStatusChange(ChunkPos pChunkPosition, ChunkStatus pNewStatus) {}
public void onStatusChange(ChunkPos pChunkPosition, @Nullable ChunkStatus pNewStatus) {}
@Override
public void start() {}

View file

@ -7,6 +7,8 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.api.FlywheelWorld;
import net.minecraft.core.BlockPos;
@ -19,33 +21,31 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
public class PlacementSimulationWorld extends WrappedWorld implements FlywheelWorld {
public Map<BlockPos, BlockState> blocksAdded;
public Map<BlockPos, BlockEntity> tesAdded;
public Map<BlockPos, BlockState> blocksAdded = new HashMap<>();
public Map<BlockPos, BlockEntity> tesAdded = new HashMap<>();
public Set<SectionPos> spannedSections;
public Set<SectionPos> spannedSections = new HashSet<>();
public LevelLightEngine lighter;
public WrappedChunkProvider chunkProvider;
public final WrappedChunkProvider chunkSource;
private final BlockPos.MutableBlockPos scratch = new BlockPos.MutableBlockPos();
public PlacementSimulationWorld(Level wrapped) {
this(wrapped, new WrappedChunkProvider());
}
public PlacementSimulationWorld(Level wrapped, WrappedChunkProvider chunkProvider) {
super(wrapped, chunkProvider);
this.chunkProvider = chunkProvider.setPlacementWorld(this);
spannedSections = new HashSet<>();
lighter = new LevelLightEngine(chunkProvider, true, false); // blockLight, skyLight
blocksAdded = new HashMap<>();
tesAdded = new HashMap<>();
public PlacementSimulationWorld(Level wrapped, WrappedChunkProvider chunkSource) {
super(wrapped, chunkSource);
// You can't leak this before the super ctor is called.
// You can't create inner classes before super ctor is called.
chunkSource.setPlacementWorld(this);
this.chunkSource = chunkSource;
lighter = new LevelLightEngine(chunkSource, true, false);
}
@Override
public LevelLightEngine getLightEngine() {
return lighter;
}
public void updateLightSources() {
/**
* Run this after you're done using setBlock().
*/
public void runLightingEngine() {
for (Map.Entry<BlockPos, BlockState> entry : blocksAdded.entrySet()) {
BlockPos pos = entry.getKey();
BlockState state = entry.getValue();
@ -54,6 +54,13 @@ public class PlacementSimulationWorld extends WrappedWorld implements FlywheelWo
lighter.onBlockEmissionIncrease(pos, light);
}
}
lighter.runUpdates(Integer.MAX_VALUE, false, false);
}
@Override
public LevelLightEngine getLightEngine() {
return lighter;
}
public void setTileEntities(Collection<BlockEntity> tileEntities) {
@ -87,6 +94,7 @@ public class PlacementSimulationWorld extends WrappedWorld implements FlywheelWo
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos pos) {
return tesAdded.get(pos);
}

View file

@ -11,8 +11,8 @@ import net.minecraft.world.level.material.FluidState;
public class RayTraceWorld implements BlockGetter {
private LevelAccessor template;
private BiFunction<BlockPos, BlockState, BlockState> stateGetter;
private final LevelAccessor template;
private final BiFunction<BlockPos, BlockState, BlockState> stateGetter;
public RayTraceWorld(LevelAccessor template, BiFunction<BlockPos, BlockState, BlockState> stateGetter) {
this.template = template;

View file

@ -29,11 +29,11 @@ public class WrappedChunkProvider extends ChunkSource {
fallbackWorld = world;
return this;
}
public WrappedChunkProvider setPlacementWorld(PlacementSimulationWorld world) {
// VirtualChunkSource is created before VirtualRenderWorld, so we can't initialize it in the ctor.
public void setPlacementWorld(PlacementSimulationWorld world) {
fallbackWorld = this.world = world;
this.chunks = new HashMap<>();
return this;
}
public Stream<BlockPos> getLightSources() {

View file

@ -32,8 +32,6 @@ import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.scores.Scoreboard;
import net.minecraft.world.ticks.LevelTickAccess;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class WrappedWorld extends Level {
protected Level world;
@ -67,17 +65,18 @@ public class WrappedWorld extends Level {
}
@Override
public boolean isStateAtPosition(@Nullable BlockPos p_217375_1_, @Nullable Predicate<BlockState> p_217375_2_) {
public boolean isStateAtPosition(BlockPos p_217375_1_, Predicate<BlockState> p_217375_2_) {
return world.isStateAtPosition(p_217375_1_, p_217375_2_);
}
@Override
public BlockEntity getBlockEntity(@Nullable BlockPos pos) {
@Nullable
public BlockEntity getBlockEntity(BlockPos pos) {
return world.getBlockEntity(pos);
}
@Override
public boolean setBlock(@Nullable BlockPos pos, @Nullable BlockState newState, int flags) {
public boolean setBlock(BlockPos pos, BlockState newState, int flags) {
return world.setBlock(pos, newState, flags);
}
@ -95,7 +94,7 @@ public class WrappedWorld extends Level {
public LevelTickAccess<Block> getBlockTicks() {
return world.getBlockTicks();
}
@Override
public LevelTickAccess<Fluid> getFluidTicks() {
return world.getFluidTicks();

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.simibubi.create.foundation.utility.worldWrappers;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;