From b63466774b131ff4f4e31f6155058c9a2ebeb8e2 Mon Sep 17 00:00:00 2001 From: JozsefA Date: Thu, 28 Jan 2021 14:39:34 -0800 Subject: [PATCH] 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 --- .../contraptions/base/KineticTileEntity.java | 2 +- .../base/KineticTileInstance.java | 4 +- .../base/SingleRotatingInstance.java | 1 - .../components/fan/FanInstance.java | 1 - .../relays/belt/BeltInstance.java | 28 +++++---- .../relays/encased/SplitShaftInstance.java | 13 ++--- .../relays/gearbox/GearboxInstance.java | 20 ++----- .../mixin/CancelTileEntityRenderMixin.java | 14 +---- .../foundation/render/BufferedModel.java | 17 +++--- .../render/FastRenderDispatcher.java | 27 +++++---- .../render/InstancedTileRenderer.java | 41 +++++++++---- .../contraption/RenderedContraption.java | 2 +- .../create/foundation/render/gl/Backend.java | 7 +++ .../render/gl/backend/MapBuffer.java | 58 +++++++++++++++++++ .../render/instancing/InstancedModel.java | 47 +++++++++------ 15 files changed, 176 insertions(+), 106 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/render/gl/Backend.java create mode 100644 src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java index baae13d13..70a96022a 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java @@ -247,7 +247,7 @@ public abstract class KineticTileEntity extends SmartTileEntity effects.triggerOverStressedEffect(); if (clientPacket) - CreateClient.kineticRenderer.update(this); + FastRenderDispatcher.enqueueUpdate(this); } public float getGeneratedSpeed() { diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileInstance.java b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileInstance.java index ece5c0594..91fff6251 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileInstance.java @@ -27,8 +27,8 @@ public abstract class KineticTileInstance extends T protected final Consumer 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)) diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/SingleRotatingInstance.java b/src/main/java/com/simibubi/create/content/contraptions/base/SingleRotatingInstance.java index 374c2207f..db9b4a71c 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/SingleRotatingInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/SingleRotatingInstance.java @@ -45,7 +45,6 @@ public class SingleRotatingInstance extends KineticTileInstance { public void remove() { shaft.delete(); fan.delete(); - shaft = fan = null; } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltInstance.java b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltInstance.java index 345fd0d30..72292b8c1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltInstance.java @@ -44,14 +44,13 @@ public class BeltInstance extends KineticTileInstance { @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 { 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 { AllBlockPartials beltPartial = BeltRenderer.getBeltPartial(diagonal, start, end, bottom); SpriteShiftEntry spriteShift = BeltRenderer.getSpriteShiftEntry(diagonal, bottom); - InstancedModel beltModel = beltPartial.renderOnBelt(modelManager, blockState); + InstancedModel beltModel = beltPartial.renderOnBelt(modelManager, lastState); Consumer setupFunc = setupFunc(spriteShift); keys.add(beltModel.setupInstance(setupFunc)); @@ -76,7 +75,7 @@ public class BeltInstance extends KineticTileInstance { } if (tile.hasPulley()) { - InstancedModel pulleyModel = getPulleyModel(blockState); + InstancedModel pulleyModel = getPulleyModel(); pulleyKey = pulleyModel.setupInstance(setupFunc(tile.getSpeed(), getRotationAxis())); } @@ -107,7 +106,6 @@ public class BeltInstance extends KineticTileInstance { 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 { return speed; } - private InstancedModel getPulleyModel(BlockState blockState) { - Direction dir = getOrientation(blockState); + private InstancedModel getPulleyModel() { + Direction dir = getOrientation(); Direction.Axis axis = dir.getAxis(); @@ -141,11 +139,11 @@ public class BeltInstance extends KineticTileInstance { 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 { 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) 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 15d3d2771..e1b2d2e9d 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 @@ -35,15 +35,14 @@ public class SplitShaftInstance extends KineticTileInstance(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 half = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, state, dir); + InstancedModel half = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, lastState, dir); float splitSpeed = speed * tile.getRotationSpeedModifier(dir); @@ -53,9 +52,8 @@ public class SplitShaftInstance extends KineticTileInstance 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)) diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxInstance.java b/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxInstance.java index c9f321708..b6cbb530a 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxInstance.java @@ -37,13 +37,10 @@ public class GearboxInstance extends KineticTileInstance { 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 { if (boxAxis == axis) continue; - InstancedModel shaft = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, state, direction); + InstancedModel shaft = AllBlockPartials.SHAFT_HALF.renderOnDirectionalSouthRotating(modelManager, lastState, direction); InstanceKey key = shaft.setupInstance(data -> { data.setBlockLight(blockLight) @@ -79,7 +76,7 @@ public class GearboxInstance extends KineticTileInstance { 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 { @Override public void onUpdate() { updateSourceFacing(); - BlockPos pos = tile.getPos(); for (Map.Entry> key : keys.entrySet()) { key.getValue().modifyInstance(data -> { Direction direction = key.getKey(); @@ -104,7 +100,6 @@ public class GearboxInstance extends KineticTileInstance { @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 { @Override public void remove() { - for (InstanceKey key : keys.values()) { - key.delete(); - } - + keys.values().forEach(InstanceKey::delete); keys.clear(); } } diff --git a/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java index 6b9c41924..f95e3cfa6 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java @@ -27,18 +27,6 @@ public class CancelTileEntityRenderMixin { private void noRenderInstancedTiles(CallbackInfoReturnable> cir) { List tiles = cir.getReturnValue(); - List 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()); } } diff --git a/src/main/java/com/simibubi/create/foundation/render/BufferedModel.java b/src/main/java/com/simibubi/create/foundation/render/BufferedModel.java index 585ec2c0b..74cdd8f55 100644 --- a/src/main/java/com/simibubi/create/foundation/render/BufferedModel.java +++ b/src/main/java/com/simibubi/create/foundation/render/BufferedModel.java @@ -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); - - for (int i = 0; i < vertexCount; i++) { - copyVertex(constant, i); - } - constant.rewind(); - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + Backend.MAP_BUFFER.mapBuffer(GL15.GL_ARRAY_BUFFER, invariantSize, buffer -> { + for (int i = 0; i < vertexCount; i++) { + copyVertex(buffer, i); + } + }); getModelFormat().informAttributes(0); diff --git a/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java index 64e66bee0..d797ffcd5 100644 --- a/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java @@ -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> queuedUpdates = new WorldAttached<>(ConcurrentLinkedQueue::new); - public static WorldAttached> queuedRemovals = new WorldAttached<>(ConcurrentLinkedQueue::new); - public static WorldAttached>> addedLastTick = new WorldAttached<>(ConcurrentLinkedQueue::new); + public static WorldAttached> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet); + public static WorldAttached> queuedRemovals = new WorldAttached<>(ConcurrentHashMap::newKeySet); + public static WorldAttached, 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 void runQueue(@Nullable Queue q, Consumer action) { - if (q == null) return; + private static void runQueue(@Nullable ConcurrentHashMap.KeySetView changed, Consumer 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 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) { diff --git a/src/main/java/com/simibubi/create/foundation/render/InstancedTileRenderer.java b/src/main/java/com/simibubi/create/foundation/render/InstancedTileRenderer.java index 6b86af49b..f382df2c2 100644 --- a/src/main/java/com/simibubi/create/foundation/render/InstancedTileRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/render/InstancedTileRenderer.java @@ -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> renderers = new HashMap<>(); @@ -31,12 +34,17 @@ public class InstancedTileRenderer { return (RenderMaterial) materials.get(materialType); } + @Nullable + public TileEntityInstance getInstance(T tile) { + return getInstance(tile, true); + } + @SuppressWarnings("unchecked") @Nullable - public TileEntityInstance getRenderer(T tile) { + public TileEntityInstance getInstance(T tile, boolean create) { if (renderers.containsKey(tile)) { return (TileEntityInstance) renderers.get(tile); - } else { + } else if (create) { TileEntityInstance renderer = InstancedTileRenderRegistry.instance.create(this, tile); if (renderer != null) { @@ -45,38 +53,49 @@ public class InstancedTileRenderer { } return renderer; + } else { + return null; } } public void onLightUpdate(T tile) { if (tile instanceof IInstanceRendered) { - TileEntityInstance renderer = getRenderer(tile); + TileEntityInstance instance = getInstance(tile); - if (renderer != null) - renderer.updateLight(); + if (instance != null) + instance.updateLight(); } } public void update(T tile) { if (tile instanceof IInstanceRendered) { - TileEntityInstance renderer = getRenderer(tile); + TileEntityInstance instance = getInstance(tile); - if (renderer != null) - renderer.update(); + if (instance != null) + instance.update(); } } public void remove(T tile) { if (tile instanceof IInstanceRendered) { - TileEntityInstance renderer = getRenderer(tile); + TileEntityInstance 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 removed = renderers.keySet().stream().filter(TileEntity::isRemoved).collect(Collectors.toList()); + removed.forEach(renderers::remove); + } + } + public void invalidate() { for (RenderMaterial material : materials.values()) { material.delete(); diff --git a/src/main/java/com/simibubi/create/foundation/render/contraption/RenderedContraption.java b/src/main/java/com/simibubi/create/foundation/render/contraption/RenderedContraption.java index 80122a1e4..8fda2a8b9 100644 --- a/src/main/java/com/simibubi/create/foundation/render/contraption/RenderedContraption.java +++ b/src/main/java/com/simibubi/create/foundation/render/contraption/RenderedContraption.java @@ -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 } } } diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/Backend.java b/src/main/java/com/simibubi/create/foundation/render/gl/Backend.java new file mode 100644 index 000000000..d534209a3 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/gl/Backend.java @@ -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; +} diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java b/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java new file mode 100644 index 000000000..82a3e795b --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java @@ -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 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 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 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 upload) { + throw new UnsupportedOperationException("glMapBuffer not supported"); + } + }; + + + public abstract void mapBuffer(int target, int offset, int length, Consumer upload); + + public final void mapBuffer(int target, int size, Consumer upload) { + mapBuffer(target, 0, size, upload); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedModel.java b/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedModel.java index 19063df7f..dbeb93c1f 100644 --- a/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedModel.java +++ b/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedModel.java @@ -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 extends BufferedMod protected final ArrayList> keys = new ArrayList<>(); protected final ArrayList 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 extends BufferedMod } - public void markDirty() { - minIndexChanged = 0; - } - protected void deleteInternal() { super.deleteInternal(); instanceVBO.delete(); @@ -91,7 +93,8 @@ public abstract class InstancedModel 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 extends BufferedMod edit.accept(data); - setMinIndexChanged(key.index); + markIndexChanged(key.index); } public synchronized InstanceKey setupInstance(Consumer setup) { @@ -114,16 +117,22 @@ public abstract class InstancedModel 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 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++) { - data.get(i).write(buffer); - } - buffer.rewind(); - GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER); + Backend.MAP_BUFFER.mapBuffer(GL15.GL_ARRAY_BUFFER, offset, length, buffer -> { + for (int i = minIndexChanged; i <= maxIndexChanged; i++) { + data.get(i).write(buffer); + } + }); glInstanceCount = data.size(); @@ -184,5 +194,6 @@ public abstract class InstancedModel extends BufferedMod instanceVBO.unbind(GL15.GL_ARRAY_BUFFER); minIndexChanged = -1; + maxIndexChanged = -1; } }