probably fix the NPEs

start work on adaptive backend
extra sanity stuff for the render manager
massive speedup when rendering a lot of tile entities
This commit is contained in:
JozsefA 2021-01-28 14:39:34 -08:00
parent 04ccf6e738
commit b63466774b
15 changed files with 176 additions and 106 deletions

View file

@ -247,7 +247,7 @@ public abstract class KineticTileEntity extends SmartTileEntity
effects.triggerOverStressedEffect(); effects.triggerOverStressedEffect();
if (clientPacket) if (clientPacket)
CreateClient.kineticRenderer.update(this); FastRenderDispatcher.enqueueUpdate(this);
} }
public float getGeneratedSpeed() { public float getGeneratedSpeed() {

View file

@ -27,8 +27,8 @@ public abstract class KineticTileInstance<T extends KineticTileEntity> extends T
protected final Consumer<RotatingData> setupFunc(float speed, Direction.Axis axis) { protected final Consumer<RotatingData> setupFunc(float speed, Direction.Axis axis) {
return data -> { return data -> {
data.setBlockLight(tile.getWorld().getLightLevel(LightType.BLOCK, pos)) data.setBlockLight(world.getLightLevel(LightType.BLOCK, pos))
.setSkyLight(tile.getWorld().getLightLevel(LightType.SKY, pos)) .setSkyLight(world.getLightLevel(LightType.SKY, pos))
.setTileEntity(tile) .setTileEntity(tile)
.setRotationalSpeed(speed) .setRotationalSpeed(speed)
.setRotationOffset(getRotationOffset(axis)) .setRotationOffset(getRotationOffset(axis))

View file

@ -45,7 +45,6 @@ public class SingleRotatingInstance extends KineticTileInstance<KineticTileEntit
@Override @Override
public void remove() { public void remove() {
rotatingModelKey.delete(); rotatingModelKey.delete();
rotatingModelKey = null;
} }
protected BlockState getRenderedBlockState() { protected BlockState getRenderedBlockState() {

View file

@ -112,6 +112,5 @@ public class FanInstance extends KineticTileInstance<EncasedFanTileEntity> {
public void remove() { public void remove() {
shaft.delete(); shaft.delete();
fan.delete(); fan.delete();
shaft = fan = null;
} }
} }

View file

@ -44,14 +44,13 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
@Override @Override
protected void init() { protected void init() {
BlockState blockState = tile.getBlockState(); if (!AllBlocks.BELT.has(lastState))
if (!AllBlocks.BELT.has(blockState))
return; return;
keys = new ArrayList<>(2); keys = new ArrayList<>(2);
beltSlope = blockState.get(BeltBlock.SLOPE); beltSlope = lastState.get(BeltBlock.SLOPE);
facing = blockState.get(BeltBlock.HORIZONTAL_FACING); facing = lastState.get(BeltBlock.HORIZONTAL_FACING);
upward = beltSlope == BeltSlope.UPWARD; upward = beltSlope == BeltSlope.UPWARD;
diagonal = beltSlope.isDiagonal(); diagonal = beltSlope.isDiagonal();
sideways = beltSlope == BeltSlope.SIDEWAYS; sideways = beltSlope == BeltSlope.SIDEWAYS;
@ -59,7 +58,7 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
alongX = facing.getAxis() == Direction.Axis.X; alongX = facing.getAxis() == Direction.Axis.X;
alongZ = facing.getAxis() == Direction.Axis.Z; alongZ = facing.getAxis() == Direction.Axis.Z;
BeltPart part = blockState.get(BeltBlock.PART); BeltPart part = lastState.get(BeltBlock.PART);
boolean start = part == BeltPart.START; boolean start = part == BeltPart.START;
boolean end = part == BeltPart.END; boolean end = part == BeltPart.END;
@ -67,7 +66,7 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
AllBlockPartials beltPartial = BeltRenderer.getBeltPartial(diagonal, start, end, bottom); AllBlockPartials beltPartial = BeltRenderer.getBeltPartial(diagonal, start, end, bottom);
SpriteShiftEntry spriteShift = BeltRenderer.getSpriteShiftEntry(diagonal, bottom); SpriteShiftEntry spriteShift = BeltRenderer.getSpriteShiftEntry(diagonal, bottom);
InstancedModel<BeltData> beltModel = beltPartial.renderOnBelt(modelManager, blockState); InstancedModel<BeltData> beltModel = beltPartial.renderOnBelt(modelManager, lastState);
Consumer<BeltData> setupFunc = setupFunc(spriteShift); Consumer<BeltData> setupFunc = setupFunc(spriteShift);
keys.add(beltModel.setupInstance(setupFunc)); keys.add(beltModel.setupInstance(setupFunc));
@ -76,7 +75,7 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
} }
if (tile.hasPulley()) { if (tile.hasPulley()) {
InstancedModel<RotatingData> pulleyModel = getPulleyModel(blockState); InstancedModel<RotatingData> pulleyModel = getPulleyModel();
pulleyKey = pulleyModel.setupInstance(setupFunc(tile.getSpeed(), getRotationAxis())); pulleyKey = pulleyModel.setupInstance(setupFunc(tile.getSpeed(), getRotationAxis()));
} }
@ -107,7 +106,6 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
keys.forEach(InstanceKey::delete); keys.forEach(InstanceKey::delete);
keys.clear(); keys.clear();
if (pulleyKey != null) pulleyKey.delete(); if (pulleyKey != null) pulleyKey.delete();
pulleyKey = null;
} }
private float getScrollSpeed() { private float getScrollSpeed() {
@ -122,8 +120,8 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
return speed; return speed;
} }
private InstancedModel<RotatingData> getPulleyModel(BlockState blockState) { private InstancedModel<RotatingData> getPulleyModel() {
Direction dir = getOrientation(blockState); Direction dir = getOrientation();
Direction.Axis axis = dir.getAxis(); Direction.Axis axis = dir.getAxis();
@ -141,11 +139,11 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
return modelTransform; return modelTransform;
}; };
return rotatingMaterial().getModel(AllBlockPartials.BELT_PULLEY, blockState, dir, ms); return rotatingMaterial().getModel(AllBlockPartials.BELT_PULLEY, lastState, dir, ms);
} }
private Direction getOrientation(BlockState blockState) { private Direction getOrientation() {
Direction dir = blockState.get(BeltBlock.HORIZONTAL_FACING) Direction dir = lastState.get(BeltBlock.HORIZONTAL_FACING)
.rotateY(); .rotateY();
if (beltSlope == BeltSlope.SIDEWAYS) if (beltSlope == BeltSlope.SIDEWAYS)
dir = Direction.UP; dir = Direction.UP;
@ -161,8 +159,8 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
BlockPos pos = tile.getPos(); BlockPos pos = tile.getPos();
data.setTileEntity(tile) data.setTileEntity(tile)
.setBlockLight(tile.getWorld().getLightLevel(LightType.BLOCK, pos)) .setBlockLight(world.getLightLevel(LightType.BLOCK, pos))
.setSkyLight(tile.getWorld().getLightLevel(LightType.SKY, pos)) .setSkyLight(world.getLightLevel(LightType.SKY, pos))
.setRotation(rotX, rotY, rotZ) .setRotation(rotX, rotY, rotZ)
.setRotationalSpeed(getScrollSpeed()) .setRotationalSpeed(getScrollSpeed())
.setRotationOffset(0) .setRotationOffset(0)

View file

@ -35,15 +35,14 @@ public class SplitShaftInstance extends KineticTileInstance<SplitShaftTileEntity
protected void init() { protected void init() {
keys = new ArrayList<>(2); keys = new ArrayList<>(2);
BlockState state = tile.getBlockState(); Block block = lastState.getBlock();
Block block = state.getBlock(); final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(lastState);
final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(state);
float speed = tile.getSpeed(); float speed = tile.getSpeed();
for (Direction dir : Iterate.directionsInAxis(boxAxis)) { for (Direction dir : Iterate.directionsInAxis(boxAxis)) {
InstancedModel<RotatingData> half = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, state, dir); InstancedModel<RotatingData> half = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, lastState, dir);
float splitSpeed = speed * tile.getRotationSpeedModifier(dir); float splitSpeed = speed * tile.getRotationSpeedModifier(dir);
@ -53,9 +52,8 @@ public class SplitShaftInstance extends KineticTileInstance<SplitShaftTileEntity
@Override @Override
public void onUpdate() { public void onUpdate() {
BlockState state = tile.getBlockState(); Block block = lastState.getBlock();
Block block = state.getBlock(); final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(lastState);
final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(state);
Direction[] directions = Iterate.directionsInAxis(boxAxis); Direction[] directions = Iterate.directionsInAxis(boxAxis);
@ -80,7 +78,6 @@ public class SplitShaftInstance extends KineticTileInstance<SplitShaftTileEntity
protected void updateRotation(InstanceKey<RotatingData> key, Direction dir) { protected void updateRotation(InstanceKey<RotatingData> key, Direction dir) {
key.modifyInstance(data -> { key.modifyInstance(data -> {
Direction.Axis axis = dir.getAxis(); Direction.Axis axis = dir.getAxis();
final BlockPos pos = tile.getPos();
data.setRotationalSpeed(tile.getSpeed() * tile.getRotationSpeedModifier(dir)) data.setRotationalSpeed(tile.getSpeed() * tile.getRotationSpeedModifier(dir))
.setRotationOffset(getRotationOffset(axis)) .setRotationOffset(getRotationOffset(axis))

View file

@ -37,13 +37,10 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
protected void init() { protected void init() {
keys = new EnumMap<>(Direction.class); keys = new EnumMap<>(Direction.class);
BlockState state = tile.getBlockState(); final Direction.Axis boxAxis = lastState.get(BlockStateProperties.AXIS);
final Direction.Axis boxAxis = state.get(BlockStateProperties.AXIS); int blockLight = world.getLightLevel(LightType.BLOCK, pos);
int skyLight = world.getLightLevel(LightType.SKY, pos);
BlockPos pos = tile.getPos();
int blockLight = tile.getWorld().getLightLevel(LightType.BLOCK, pos);
int skyLight = tile.getWorld().getLightLevel(LightType.SKY, pos);
updateSourceFacing(); updateSourceFacing();
for (Direction direction : Iterate.directions) { for (Direction direction : Iterate.directions) {
@ -51,7 +48,7 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
if (boxAxis == axis) if (boxAxis == axis)
continue; continue;
InstancedModel<RotatingData> shaft = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, state, direction); InstancedModel<RotatingData> shaft = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, lastState, direction);
InstanceKey<RotatingData> key = shaft.setupInstance(data -> { InstanceKey<RotatingData> key = shaft.setupInstance(data -> {
data.setBlockLight(blockLight) data.setBlockLight(blockLight)
@ -79,7 +76,7 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
protected void updateSourceFacing() { protected void updateSourceFacing() {
if (tile.hasSource()) { if (tile.hasSource()) {
BlockPos source = tile.source.subtract(tile.getPos()); BlockPos source = tile.source.subtract(pos);
sourceFacing = Direction.getFacingFromVector(source.getX(), source.getY(), source.getZ()); sourceFacing = Direction.getFacingFromVector(source.getX(), source.getY(), source.getZ());
} else { } else {
sourceFacing = null; sourceFacing = null;
@ -89,7 +86,6 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
@Override @Override
public void onUpdate() { public void onUpdate() {
updateSourceFacing(); updateSourceFacing();
BlockPos pos = tile.getPos();
for (Map.Entry<Direction, InstanceKey<RotatingData>> key : keys.entrySet()) { for (Map.Entry<Direction, InstanceKey<RotatingData>> key : keys.entrySet()) {
key.getValue().modifyInstance(data -> { key.getValue().modifyInstance(data -> {
Direction direction = key.getKey(); Direction direction = key.getKey();
@ -104,7 +100,6 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
@Override @Override
public void updateLight() { public void updateLight() {
BlockPos pos = tile.getPos();
int blockLight = tile.getWorld().getLightLevel(LightType.BLOCK, pos); int blockLight = tile.getWorld().getLightLevel(LightType.BLOCK, pos);
int skyLight = tile.getWorld().getLightLevel(LightType.SKY, pos); int skyLight = tile.getWorld().getLightLevel(LightType.SKY, pos);
@ -115,10 +110,7 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
@Override @Override
public void remove() { public void remove() {
for (InstanceKey<RotatingData> key : keys.values()) { keys.values().forEach(InstanceKey::delete);
key.delete();
}
keys.clear(); keys.clear();
} }
} }

View file

@ -27,18 +27,6 @@ public class CancelTileEntityRenderMixin {
private void noRenderInstancedTiles(CallbackInfoReturnable<List<TileEntity>> cir) { private void noRenderInstancedTiles(CallbackInfoReturnable<List<TileEntity>> cir) {
List<TileEntity> tiles = cir.getReturnValue(); List<TileEntity> tiles = cir.getReturnValue();
List<TileEntity> out = new ArrayList<>(tiles.size()); tiles.removeIf(tile -> tile instanceof IInstanceRendered && !((IInstanceRendered) tile).shouldRenderAsTE());
for (TileEntity tile : tiles) {
if (tile instanceof IInstanceRendered) {
IInstanceRendered instanceRendered = (IInstanceRendered) tile;
if (!instanceRendered.shouldRenderAsTE()) continue;
}
out.add(tile);
}
cir.setReturnValue(out);
} }
} }

View file

@ -1,11 +1,12 @@
package com.simibubi.create.foundation.render; package com.simibubi.create.foundation.render;
import com.simibubi.create.foundation.render.gl.Backend;
import com.simibubi.create.foundation.render.gl.GlBuffer; import com.simibubi.create.foundation.render.gl.GlBuffer;
import com.simibubi.create.foundation.render.gl.GlVertexArray; import com.simibubi.create.foundation.render.gl.GlVertexArray;
import com.simibubi.create.foundation.render.instancing.VertexFormat; import com.simibubi.create.foundation.render.instancing.VertexFormat;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL40; import org.lwjgl.opengl.GL20;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -35,7 +36,7 @@ public abstract class BufferedModel extends TemplateBuffer {
int numAttributes = getTotalShaderAttributeCount(); int numAttributes = getTotalShaderAttributeCount();
for (int i = 0; i <= numAttributes; i++) { for (int i = 0; i <= numAttributes; i++) {
GL40.glEnableVertexAttribArray(i); GL20.glEnableVertexAttribArray(i);
} }
invariantVBO.bind(GL15.GL_ARRAY_BUFFER); invariantVBO.bind(GL15.GL_ARRAY_BUFFER);
@ -44,13 +45,11 @@ public abstract class BufferedModel extends TemplateBuffer {
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, invariantSize, GL15.GL_STATIC_DRAW);
// mirror it in system memory so we can write to it // mirror it in system memory so we can write to it
ByteBuffer constant = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY); Backend.MAP_BUFFER.mapBuffer(GL15.GL_ARRAY_BUFFER, invariantSize, buffer -> {
for (int i = 0; i < vertexCount; i++) { for (int i = 0; i < vertexCount; i++) {
copyVertex(constant, i); copyVertex(buffer, i);
} }
constant.rewind(); });
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
getModelFormat().informAttributes(0); getModelFormat().informAttributes(0);

View file

@ -27,16 +27,16 @@ import net.minecraft.world.chunk.Chunk;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer; import java.util.function.Consumer;
public class FastRenderDispatcher { public class FastRenderDispatcher {
public static WorldAttached<ConcurrentLinkedQueue<TileEntity>> queuedUpdates = new WorldAttached<>(ConcurrentLinkedQueue::new); public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
public static WorldAttached<ConcurrentLinkedQueue<TileEntity>> queuedRemovals = new WorldAttached<>(ConcurrentLinkedQueue::new); public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedRemovals = new WorldAttached<>(ConcurrentHashMap::newKeySet);
public static WorldAttached<ConcurrentLinkedQueue<TileEntityInstance<?>>> addedLastTick = new WorldAttached<>(ConcurrentLinkedQueue::new); public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntityInstance<?>, Boolean>> addedLastTick = new WorldAttached<>(ConcurrentHashMap::newKeySet);
private static Matrix4f projectionMatrixThisFrame = null; private static Matrix4f projectionMatrixThisFrame = null;
@ -56,18 +56,21 @@ public class FastRenderDispatcher {
ClientWorld world = Minecraft.getInstance().world; ClientWorld world = Minecraft.getInstance().world;
runQueue(addedLastTick.get(world), TileEntityInstance::updateLight); runQueue(addedLastTick.get(world), TileEntityInstance::updateLight);
runQueue(queuedUpdates.get(world), CreateClient.kineticRenderer::update);
runQueue(queuedRemovals.get(world), CreateClient.kineticRenderer::remove); runQueue(queuedRemovals.get(world), CreateClient.kineticRenderer::remove);
CreateClient.kineticRenderer.clean();
runQueue(queuedUpdates.get(world), CreateClient.kineticRenderer::update);
} }
private static <T> void runQueue(@Nullable Queue<T> q, Consumer<T> action) { private static <T> void runQueue(@Nullable ConcurrentHashMap.KeySetView<T, Boolean> changed, Consumer<T> action) {
if (q == null) return; if (changed == null) return;
while (!q.isEmpty()) { // because of potential concurrency issues, we make a copy of what's in the set at the time we get here
T t = q.poll(); ArrayList<T> tiles = new ArrayList<>(changed);
action.accept(t); tiles.forEach(action);
}
changed.removeAll(tiles);
} }
public static void renderLayer(RenderType type, MatrixStack stack, double cameraX, double cameraY, double cameraZ) { public static void renderLayer(RenderType type, MatrixStack stack, double cameraX, double cameraY, double cameraZ) {

View file

@ -4,13 +4,16 @@ import com.simibubi.create.foundation.render.gl.shader.Shader;
import com.simibubi.create.foundation.render.gl.shader.ShaderCallback; import com.simibubi.create.foundation.render.gl.shader.ShaderCallback;
import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; import com.simibubi.create.foundation.render.gl.shader.ShaderHelper;
import com.simibubi.create.foundation.render.instancing.*; import com.simibubi.create.foundation.render.instancing.*;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
public class InstancedTileRenderer { public class InstancedTileRenderer {
protected Map<TileEntity, TileEntityInstance<?>> renderers = new HashMap<>(); protected Map<TileEntity, TileEntityInstance<?>> renderers = new HashMap<>();
@ -31,12 +34,17 @@ public class InstancedTileRenderer {
return (RenderMaterial<M>) materials.get(materialType); return (RenderMaterial<M>) materials.get(materialType);
} }
@Nullable
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile) {
return getInstance(tile, true);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Nullable @Nullable
public <T extends TileEntity> TileEntityInstance<? super T> getRenderer(T tile) { public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile, boolean create) {
if (renderers.containsKey(tile)) { if (renderers.containsKey(tile)) {
return (TileEntityInstance<? super T>) renderers.get(tile); return (TileEntityInstance<? super T>) renderers.get(tile);
} else { } else if (create) {
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile); TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
if (renderer != null) { if (renderer != null) {
@ -45,38 +53,49 @@ public class InstancedTileRenderer {
} }
return renderer; return renderer;
} else {
return null;
} }
} }
public <T extends TileEntity> void onLightUpdate(T tile) { public <T extends TileEntity> void onLightUpdate(T tile) {
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> renderer = getRenderer(tile); TileEntityInstance<? super T> instance = getInstance(tile);
if (renderer != null) if (instance != null)
renderer.updateLight(); instance.updateLight();
} }
} }
public <T extends TileEntity> void update(T tile) { public <T extends TileEntity> void update(T tile) {
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> renderer = getRenderer(tile); TileEntityInstance<? super T> instance = getInstance(tile);
if (renderer != null) if (instance != null)
renderer.update(); instance.update();
} }
} }
public <T extends TileEntity> void remove(T tile) { public <T extends TileEntity> void remove(T tile) {
if (tile instanceof IInstanceRendered) { if (tile instanceof IInstanceRendered) {
TileEntityInstance<? super T> renderer = getRenderer(tile); TileEntityInstance<? super T> instance = getInstance(tile, false);
if (renderer != null) { if (instance != null) {
renderer.remove(); instance.remove();
renderers.remove(tile); renderers.remove(tile);
} }
} }
} }
public void clean() {
// Clean up twice a second. This doesn't have to happen every tick,
// but this does need to be run to ensure we don't miss anything.
if (AnimationTickHolder.ticks % 10 == 0) {
List<TileEntity> removed = renderers.keySet().stream().filter(TileEntity::isRemoved).collect(Collectors.toList());
removed.forEach(renderers::remove);
}
}
public void invalidate() { public void invalidate() {
for (RenderMaterial<?> material : materials.values()) { for (RenderMaterial<?> material : materials.values()) {
material.delete(); material.delete();

View file

@ -91,7 +91,7 @@ public class RenderedContraption {
if (!tileEntities.isEmpty()) { if (!tileEntities.isEmpty()) {
for (TileEntity te : tileEntities) { for (TileEntity te : tileEntities) {
if (te instanceof IInstanceRendered) { if (te instanceof IInstanceRendered) {
kinetics.getRenderer(te); // this is enough to instantiate the model instance kinetics.getInstance(te); // this is enough to instantiate the model instance
} }
} }
} }

View file

@ -0,0 +1,7 @@
package com.simibubi.create.foundation.render.gl;
import com.simibubi.create.foundation.render.gl.backend.MapBuffer;
public class Backend {
public static final MapBuffer MAP_BUFFER = MapBuffer.GL30_RANGE;
}

View file

@ -0,0 +1,58 @@
package com.simibubi.create.foundation.render.gl.backend;
import org.lwjgl.opengl.ARBMapBufferRange;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import java.nio.ByteBuffer;
import java.util.function.Consumer;
public enum MapBuffer {
GL30_RANGE {
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT);
upload.accept(buffer);
buffer.rewind();
GL30.glUnmapBuffer(target);
}
},
ARB_RANGE {
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT);
upload.accept(buffer);
buffer.rewind();
GL30.glUnmapBuffer(target);
}
},
GL15_MAP {
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY);
buffer.position(offset);
upload.accept(buffer);
buffer.rewind();
GL15.glUnmapBuffer(target);
}
},
UNSUPPORTED {
@Override
public void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload) {
throw new UnsupportedOperationException("glMapBuffer not supported");
}
};
public abstract void mapBuffer(int target, int offset, int length, Consumer<ByteBuffer> upload);
public final void mapBuffer(int target, int size, Consumer<ByteBuffer> upload) {
mapBuffer(target, 0, size, upload);
}
}

View file

@ -1,11 +1,16 @@
package com.simibubi.create.foundation.render.instancing; package com.simibubi.create.foundation.render.instancing;
import com.google.common.collect.Range;
import com.simibubi.create.foundation.render.BufferedModel; import com.simibubi.create.foundation.render.BufferedModel;
import com.simibubi.create.foundation.render.RenderMath; import com.simibubi.create.foundation.render.RenderMath;
import com.simibubi.create.foundation.render.gl.Backend;
import com.simibubi.create.foundation.render.gl.GlBuffer; import com.simibubi.create.foundation.render.gl.GlBuffer;
import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.BufferBuilder;
import org.lwjgl.opengl.*; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL33;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -23,6 +28,7 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
protected final ArrayList<InstanceKey<D>> keys = new ArrayList<>(); protected final ArrayList<InstanceKey<D>> keys = new ArrayList<>();
protected final ArrayList<D> data = new ArrayList<>(); protected final ArrayList<D> data = new ArrayList<>();
protected int minIndexChanged = -1; protected int minIndexChanged = -1;
protected int maxIndexChanged = -1;
public InstancedModel(BufferBuilder buf) { public InstancedModel(BufferBuilder buf) {
super(buf); super(buf);
@ -67,10 +73,6 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
} }
public void markDirty() {
minIndexChanged = 0;
}
protected void deleteInternal() { protected void deleteInternal() {
super.deleteInternal(); super.deleteInternal();
instanceVBO.delete(); instanceVBO.delete();
@ -91,7 +93,8 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
keys.get(i).index--; keys.get(i).index--;
} }
setMinIndexChanged(key.index); markIndexChanged(index - 1);
maxIndexChanged = keys.size() - 1;
key.invalidate(); key.invalidate();
} }
@ -103,7 +106,7 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
edit.accept(data); edit.accept(data);
setMinIndexChanged(key.index); markIndexChanged(key.index);
} }
public synchronized InstanceKey<D> setupInstance(Consumer<D> setup) { public synchronized InstanceKey<D> setupInstance(Consumer<D> setup) {
@ -114,16 +117,22 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
data.add(instanceData); data.add(instanceData);
keys.add(key); keys.add(key);
setMinIndexChanged(key.index); markIndexChanged(key.index);
return key; return key;
} }
protected void setMinIndexChanged(int index) { protected void markIndexChanged(int index) {
if (minIndexChanged < 0) { if (minIndexChanged < 0) {
minIndexChanged = index; minIndexChanged = index;
} else { } else if (index < minIndexChanged) {
minIndexChanged = Math.min(minIndexChanged, index); minIndexChanged = index;
}
if (maxIndexChanged < 0) {
maxIndexChanged = index;
} else if (index > maxIndexChanged) {
maxIndexChanged = index;
} }
} }
@ -161,16 +170,17 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, instanceSize, GL15.GL_STATIC_DRAW); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, instanceSize, GL15.GL_STATIC_DRAW);
glBufferSize = instanceSize; glBufferSize = instanceSize;
minIndexChanged = 0; minIndexChanged = 0;
maxIndexChanged = data.size() - 1;
} }
ByteBuffer buffer = GL15.glMapBuffer(GL15.GL_ARRAY_BUFFER, GL15.GL_WRITE_ONLY); int offset = minIndexChanged * stride;
int length = (1 + maxIndexChanged - minIndexChanged) * stride;
buffer.position(stride * minIndexChanged); Backend.MAP_BUFFER.mapBuffer(GL15.GL_ARRAY_BUFFER, offset, length, buffer -> {
for (int i = minIndexChanged; i < data.size(); i++) { for (int i = minIndexChanged; i <= maxIndexChanged; i++) {
data.get(i).write(buffer); data.get(i).write(buffer);
} }
buffer.rewind(); });
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
glInstanceCount = data.size(); glInstanceCount = data.size();
@ -184,5 +194,6 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
instanceVBO.unbind(GL15.GL_ARRAY_BUFFER); instanceVBO.unbind(GL15.GL_ARRAY_BUFFER);
minIndexChanged = -1; minIndexChanged = -1;
maxIndexChanged = -1;
} }
} }