mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-15 21:33:41 +01:00
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:
parent
04ccf6e738
commit
b63466774b
15 changed files with 176 additions and 106 deletions
|
@ -247,7 +247,7 @@ public abstract class KineticTileEntity extends SmartTileEntity
|
|||
effects.triggerOverStressedEffect();
|
||||
|
||||
if (clientPacket)
|
||||
CreateClient.kineticRenderer.update(this);
|
||||
FastRenderDispatcher.enqueueUpdate(this);
|
||||
}
|
||||
|
||||
public float getGeneratedSpeed() {
|
||||
|
|
|
@ -27,8 +27,8 @@ public abstract class KineticTileInstance<T extends KineticTileEntity> extends T
|
|||
|
||||
protected final Consumer<RotatingData> setupFunc(float speed, Direction.Axis axis) {
|
||||
return data -> {
|
||||
data.setBlockLight(tile.getWorld().getLightLevel(LightType.BLOCK, pos))
|
||||
.setSkyLight(tile.getWorld().getLightLevel(LightType.SKY, pos))
|
||||
data.setBlockLight(world.getLightLevel(LightType.BLOCK, pos))
|
||||
.setSkyLight(world.getLightLevel(LightType.SKY, pos))
|
||||
.setTileEntity(tile)
|
||||
.setRotationalSpeed(speed)
|
||||
.setRotationOffset(getRotationOffset(axis))
|
||||
|
|
|
@ -45,7 +45,6 @@ public class SingleRotatingInstance extends KineticTileInstance<KineticTileEntit
|
|||
@Override
|
||||
public void remove() {
|
||||
rotatingModelKey.delete();
|
||||
rotatingModelKey = null;
|
||||
}
|
||||
|
||||
protected BlockState getRenderedBlockState() {
|
||||
|
|
|
@ -112,6 +112,5 @@ public class FanInstance extends KineticTileInstance<EncasedFanTileEntity> {
|
|||
public void remove() {
|
||||
shaft.delete();
|
||||
fan.delete();
|
||||
shaft = fan = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,14 +44,13 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
|
||||
@Override
|
||||
protected void init() {
|
||||
BlockState blockState = tile.getBlockState();
|
||||
if (!AllBlocks.BELT.has(blockState))
|
||||
if (!AllBlocks.BELT.has(lastState))
|
||||
return;
|
||||
|
||||
keys = new ArrayList<>(2);
|
||||
|
||||
beltSlope = blockState.get(BeltBlock.SLOPE);
|
||||
facing = blockState.get(BeltBlock.HORIZONTAL_FACING);
|
||||
beltSlope = lastState.get(BeltBlock.SLOPE);
|
||||
facing = lastState.get(BeltBlock.HORIZONTAL_FACING);
|
||||
upward = beltSlope == BeltSlope.UPWARD;
|
||||
diagonal = beltSlope.isDiagonal();
|
||||
sideways = beltSlope == BeltSlope.SIDEWAYS;
|
||||
|
@ -59,7 +58,7 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
alongX = facing.getAxis() == Direction.Axis.X;
|
||||
alongZ = facing.getAxis() == Direction.Axis.Z;
|
||||
|
||||
BeltPart part = blockState.get(BeltBlock.PART);
|
||||
BeltPart part = lastState.get(BeltBlock.PART);
|
||||
boolean start = part == BeltPart.START;
|
||||
boolean end = part == BeltPart.END;
|
||||
|
||||
|
@ -67,7 +66,7 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
AllBlockPartials beltPartial = BeltRenderer.getBeltPartial(diagonal, start, end, 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);
|
||||
|
||||
keys.add(beltModel.setupInstance(setupFunc));
|
||||
|
@ -76,7 +75,7 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
}
|
||||
|
||||
if (tile.hasPulley()) {
|
||||
InstancedModel<RotatingData> pulleyModel = getPulleyModel(blockState);
|
||||
InstancedModel<RotatingData> pulleyModel = getPulleyModel();
|
||||
|
||||
pulleyKey = pulleyModel.setupInstance(setupFunc(tile.getSpeed(), getRotationAxis()));
|
||||
}
|
||||
|
@ -107,7 +106,6 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
keys.forEach(InstanceKey::delete);
|
||||
keys.clear();
|
||||
if (pulleyKey != null) pulleyKey.delete();
|
||||
pulleyKey = null;
|
||||
}
|
||||
|
||||
private float getScrollSpeed() {
|
||||
|
@ -122,8 +120,8 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
return speed;
|
||||
}
|
||||
|
||||
private InstancedModel<RotatingData> getPulleyModel(BlockState blockState) {
|
||||
Direction dir = getOrientation(blockState);
|
||||
private InstancedModel<RotatingData> getPulleyModel() {
|
||||
Direction dir = getOrientation();
|
||||
|
||||
Direction.Axis axis = dir.getAxis();
|
||||
|
||||
|
@ -141,11 +139,11 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
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) {
|
||||
Direction dir = blockState.get(BeltBlock.HORIZONTAL_FACING)
|
||||
private Direction getOrientation() {
|
||||
Direction dir = lastState.get(BeltBlock.HORIZONTAL_FACING)
|
||||
.rotateY();
|
||||
if (beltSlope == BeltSlope.SIDEWAYS)
|
||||
dir = Direction.UP;
|
||||
|
@ -161,8 +159,8 @@ public class BeltInstance extends KineticTileInstance<BeltTileEntity> {
|
|||
|
||||
BlockPos pos = tile.getPos();
|
||||
data.setTileEntity(tile)
|
||||
.setBlockLight(tile.getWorld().getLightLevel(LightType.BLOCK, pos))
|
||||
.setSkyLight(tile.getWorld().getLightLevel(LightType.SKY, pos))
|
||||
.setBlockLight(world.getLightLevel(LightType.BLOCK, pos))
|
||||
.setSkyLight(world.getLightLevel(LightType.SKY, pos))
|
||||
.setRotation(rotX, rotY, rotZ)
|
||||
.setRotationalSpeed(getScrollSpeed())
|
||||
.setRotationOffset(0)
|
||||
|
|
|
@ -35,15 +35,14 @@ public class SplitShaftInstance extends KineticTileInstance<SplitShaftTileEntity
|
|||
protected void init() {
|
||||
keys = new ArrayList<>(2);
|
||||
|
||||
BlockState state = tile.getBlockState();
|
||||
Block block = state.getBlock();
|
||||
final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(state);
|
||||
Block block = lastState.getBlock();
|
||||
final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(lastState);
|
||||
|
||||
float speed = tile.getSpeed();
|
||||
|
||||
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);
|
||||
|
||||
|
@ -53,9 +52,8 @@ public class SplitShaftInstance extends KineticTileInstance<SplitShaftTileEntity
|
|||
|
||||
@Override
|
||||
public void onUpdate() {
|
||||
BlockState state = tile.getBlockState();
|
||||
Block block = state.getBlock();
|
||||
final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(state);
|
||||
Block block = lastState.getBlock();
|
||||
final Direction.Axis boxAxis = ((IRotate) block).getRotationAxis(lastState);
|
||||
|
||||
Direction[] directions = Iterate.directionsInAxis(boxAxis);
|
||||
|
||||
|
@ -80,7 +78,6 @@ public class SplitShaftInstance extends KineticTileInstance<SplitShaftTileEntity
|
|||
protected void updateRotation(InstanceKey<RotatingData> key, Direction dir) {
|
||||
key.modifyInstance(data -> {
|
||||
Direction.Axis axis = dir.getAxis();
|
||||
final BlockPos pos = tile.getPos();
|
||||
|
||||
data.setRotationalSpeed(tile.getSpeed() * tile.getRotationSpeedModifier(dir))
|
||||
.setRotationOffset(getRotationOffset(axis))
|
||||
|
|
|
@ -37,13 +37,10 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
|
|||
protected void init() {
|
||||
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);
|
||||
|
||||
BlockPos pos = tile.getPos();
|
||||
int blockLight = tile.getWorld().getLightLevel(LightType.BLOCK, pos);
|
||||
int skyLight = tile.getWorld().getLightLevel(LightType.SKY, pos);
|
||||
int blockLight = world.getLightLevel(LightType.BLOCK, pos);
|
||||
int skyLight = world.getLightLevel(LightType.SKY, pos);
|
||||
updateSourceFacing();
|
||||
|
||||
for (Direction direction : Iterate.directions) {
|
||||
|
@ -51,7 +48,7 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
|
|||
if (boxAxis == axis)
|
||||
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 -> {
|
||||
data.setBlockLight(blockLight)
|
||||
|
@ -79,7 +76,7 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
|
|||
|
||||
protected void updateSourceFacing() {
|
||||
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());
|
||||
} else {
|
||||
sourceFacing = null;
|
||||
|
@ -89,7 +86,6 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
|
|||
@Override
|
||||
public void onUpdate() {
|
||||
updateSourceFacing();
|
||||
BlockPos pos = tile.getPos();
|
||||
for (Map.Entry<Direction, InstanceKey<RotatingData>> key : keys.entrySet()) {
|
||||
key.getValue().modifyInstance(data -> {
|
||||
Direction direction = key.getKey();
|
||||
|
@ -104,7 +100,6 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
|
|||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
BlockPos pos = tile.getPos();
|
||||
int blockLight = tile.getWorld().getLightLevel(LightType.BLOCK, pos);
|
||||
int skyLight = tile.getWorld().getLightLevel(LightType.SKY, pos);
|
||||
|
||||
|
@ -115,10 +110,7 @@ public class GearboxInstance extends KineticTileInstance<GearboxTileEntity> {
|
|||
|
||||
@Override
|
||||
public void remove() {
|
||||
for (InstanceKey<RotatingData> key : keys.values()) {
|
||||
key.delete();
|
||||
}
|
||||
|
||||
keys.values().forEach(InstanceKey::delete);
|
||||
keys.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,18 +27,6 @@ public class CancelTileEntityRenderMixin {
|
|||
private void noRenderInstancedTiles(CallbackInfoReturnable<List<TileEntity>> cir) {
|
||||
List<TileEntity> tiles = cir.getReturnValue();
|
||||
|
||||
List<TileEntity> out = new ArrayList<>(tiles.size());
|
||||
|
||||
for (TileEntity tile : tiles) {
|
||||
if (tile instanceof IInstanceRendered) {
|
||||
IInstanceRendered instanceRendered = (IInstanceRendered) tile;
|
||||
|
||||
if (!instanceRendered.shouldRenderAsTE()) continue;
|
||||
}
|
||||
|
||||
out.add(tile);
|
||||
}
|
||||
|
||||
cir.setReturnValue(out);
|
||||
tiles.removeIf(tile -> tile instanceof IInstanceRendered && !((IInstanceRendered) tile).shouldRenderAsTE());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
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.GlVertexArray;
|
||||
import com.simibubi.create.foundation.render.instancing.VertexFormat;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL40;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
@ -35,7 +36,7 @@ public abstract class BufferedModel extends TemplateBuffer {
|
|||
|
||||
int numAttributes = getTotalShaderAttributeCount();
|
||||
for (int i = 0; i <= numAttributes; i++) {
|
||||
GL40.glEnableVertexAttribArray(i);
|
||||
GL20.glEnableVertexAttribArray(i);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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++) {
|
||||
copyVertex(constant, i);
|
||||
copyVertex(buffer, i);
|
||||
}
|
||||
constant.rewind();
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
});
|
||||
|
||||
getModelFormat().informAttributes(0);
|
||||
|
||||
|
|
|
@ -27,16 +27,16 @@ import net.minecraft.world.chunk.Chunk;
|
|||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FastRenderDispatcher {
|
||||
|
||||
public static WorldAttached<ConcurrentLinkedQueue<TileEntity>> queuedUpdates = new WorldAttached<>(ConcurrentLinkedQueue::new);
|
||||
public static WorldAttached<ConcurrentLinkedQueue<TileEntity>> queuedRemovals = new WorldAttached<>(ConcurrentLinkedQueue::new);
|
||||
public static WorldAttached<ConcurrentLinkedQueue<TileEntityInstance<?>>> addedLastTick = new WorldAttached<>(ConcurrentLinkedQueue::new);
|
||||
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
|
||||
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedRemovals = new WorldAttached<>(ConcurrentHashMap::newKeySet);
|
||||
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntityInstance<?>, Boolean>> addedLastTick = new WorldAttached<>(ConcurrentHashMap::newKeySet);
|
||||
|
||||
private static Matrix4f projectionMatrixThisFrame = null;
|
||||
|
||||
|
@ -56,18 +56,21 @@ public class FastRenderDispatcher {
|
|||
ClientWorld world = Minecraft.getInstance().world;
|
||||
|
||||
runQueue(addedLastTick.get(world), TileEntityInstance::updateLight);
|
||||
runQueue(queuedUpdates.get(world), CreateClient.kineticRenderer::update);
|
||||
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) {
|
||||
if (q == null) return;
|
||||
private static <T> void runQueue(@Nullable ConcurrentHashMap.KeySetView<T, Boolean> changed, Consumer<T> action) {
|
||||
if (changed == null) return;
|
||||
|
||||
while (!q.isEmpty()) {
|
||||
T t = q.poll();
|
||||
// because of potential concurrency issues, we make a copy of what's in the set at the time we get here
|
||||
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) {
|
||||
|
|
|
@ -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.ShaderHelper;
|
||||
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.RenderType;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InstancedTileRenderer {
|
||||
protected Map<TileEntity, TileEntityInstance<?>> renderers = new HashMap<>();
|
||||
|
@ -31,12 +34,17 @@ public class InstancedTileRenderer {
|
|||
return (RenderMaterial<M>) materials.get(materialType);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends TileEntity> TileEntityInstance<? super T> getInstance(T tile) {
|
||||
return getInstance(tile, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@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)) {
|
||||
return (TileEntityInstance<? super T>) renderers.get(tile);
|
||||
} else {
|
||||
} else if (create) {
|
||||
TileEntityInstance<? super T> renderer = InstancedTileRenderRegistry.instance.create(this, tile);
|
||||
|
||||
if (renderer != null) {
|
||||
|
@ -45,38 +53,49 @@ public class InstancedTileRenderer {
|
|||
}
|
||||
|
||||
return renderer;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends TileEntity> void onLightUpdate(T tile) {
|
||||
if (tile instanceof IInstanceRendered) {
|
||||
TileEntityInstance<? super T> renderer = getRenderer(tile);
|
||||
TileEntityInstance<? super T> instance = getInstance(tile);
|
||||
|
||||
if (renderer != null)
|
||||
renderer.updateLight();
|
||||
if (instance != null)
|
||||
instance.updateLight();
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends TileEntity> void update(T tile) {
|
||||
if (tile instanceof IInstanceRendered) {
|
||||
TileEntityInstance<? super T> renderer = getRenderer(tile);
|
||||
TileEntityInstance<? super T> instance = getInstance(tile);
|
||||
|
||||
if (renderer != null)
|
||||
renderer.update();
|
||||
if (instance != null)
|
||||
instance.update();
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends TileEntity> void remove(T tile) {
|
||||
if (tile instanceof IInstanceRendered) {
|
||||
TileEntityInstance<? super T> renderer = getRenderer(tile);
|
||||
TileEntityInstance<? super T> instance = getInstance(tile, false);
|
||||
|
||||
if (renderer != null) {
|
||||
renderer.remove();
|
||||
if (instance != null) {
|
||||
instance.remove();
|
||||
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() {
|
||||
for (RenderMaterial<?> material : materials.values()) {
|
||||
material.delete();
|
||||
|
|
|
@ -91,7 +91,7 @@ public class RenderedContraption {
|
|||
if (!tileEntities.isEmpty()) {
|
||||
for (TileEntity te : tileEntities) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
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.RenderMath;
|
||||
import com.simibubi.create.foundation.render.gl.Backend;
|
||||
import com.simibubi.create.foundation.render.gl.GlBuffer;
|
||||
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.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<D> data = new ArrayList<>();
|
||||
protected int minIndexChanged = -1;
|
||||
protected int maxIndexChanged = -1;
|
||||
|
||||
public InstancedModel(BufferBuilder buf) {
|
||||
super(buf);
|
||||
|
@ -67,10 +73,6 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
|
|||
|
||||
}
|
||||
|
||||
public void markDirty() {
|
||||
minIndexChanged = 0;
|
||||
}
|
||||
|
||||
protected void deleteInternal() {
|
||||
super.deleteInternal();
|
||||
instanceVBO.delete();
|
||||
|
@ -91,7 +93,8 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
|
|||
keys.get(i).index--;
|
||||
}
|
||||
|
||||
setMinIndexChanged(key.index);
|
||||
markIndexChanged(index - 1);
|
||||
maxIndexChanged = keys.size() - 1;
|
||||
|
||||
key.invalidate();
|
||||
}
|
||||
|
@ -103,7 +106,7 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
|
|||
|
||||
edit.accept(data);
|
||||
|
||||
setMinIndexChanged(key.index);
|
||||
markIndexChanged(key.index);
|
||||
}
|
||||
|
||||
public synchronized InstanceKey<D> setupInstance(Consumer<D> setup) {
|
||||
|
@ -114,16 +117,22 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
|
|||
data.add(instanceData);
|
||||
keys.add(key);
|
||||
|
||||
setMinIndexChanged(key.index);
|
||||
markIndexChanged(key.index);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
protected void setMinIndexChanged(int index) {
|
||||
protected void markIndexChanged(int index) {
|
||||
if (minIndexChanged < 0) {
|
||||
minIndexChanged = index;
|
||||
} else {
|
||||
minIndexChanged = Math.min(minIndexChanged, index);
|
||||
} else if (index < minIndexChanged) {
|
||||
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);
|
||||
glBufferSize = instanceSize;
|
||||
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);
|
||||
for (int i = minIndexChanged; i < data.size(); i++) {
|
||||
Backend.MAP_BUFFER.mapBuffer(GL15.GL_ARRAY_BUFFER, offset, length, buffer -> {
|
||||
for (int i = minIndexChanged; i <= maxIndexChanged; i++) {
|
||||
data.get(i).write(buffer);
|
||||
}
|
||||
buffer.rewind();
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
});
|
||||
|
||||
glInstanceCount = data.size();
|
||||
|
||||
|
@ -184,5 +194,6 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
|
|||
instanceVBO.unbind(GL15.GL_ARRAY_BUFFER);
|
||||
|
||||
minIndexChanged = -1;
|
||||
maxIndexChanged = -1;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue