diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java index cbb614bbb..be02832b9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntityRenderer.java @@ -1,6 +1,7 @@ package com.simibubi.create.content.contraptions.components.structureMovement; import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.foundation.render.backend.Backend; import com.simibubi.create.foundation.render.backend.FastRenderDispatcher; import com.simibubi.create.foundation.utility.AnimationTickHolder; import net.minecraft.client.renderer.IRenderTypeBuffer; @@ -56,11 +57,12 @@ public abstract class AbstractContraptionEntityRenderer renderers = new HashMap<>(); - public static void markForRendering(Contraption c, MatrixStack model) { - getRenderer(c.entity.world, c).setRenderSettings(model.peek().getModel()); - } - public static void notifyLightUpdate(ILightReader world, LightType type, SectionPos pos) { for (RenderedContraption renderer : renderers.values()) { renderer.getLighter().lightVolume.notifyLightUpdate(world, type, pos); @@ -73,7 +69,8 @@ public class ContraptionRenderDispatcher { private static void updateTransform(C c, AbstractContraptionEntityRenderer entityRenderer) { MatrixStack stack = entityRenderer.makeTransformMatrix(c, AnimationTickHolder.getPartialTicks()); - markForRendering(c.getContraption(), stack); + Contraption c1 = c.getContraption(); + getRenderer(c1.entity.world, c1).setRenderSettings(stack.peek().getModel()); } public static void tick() { @@ -104,15 +101,19 @@ public class ContraptionRenderDispatcher { GL11.glEnable(GL13.GL_TEXTURE_3D); GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4 - ContraptionProgram structureShader = Backend.getProgram(AllProgramSpecs.CONTRAPTION_STRUCTURE); - structureShader.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode()); - for (RenderedContraption renderer : renderers.values()) { - renderer.doRenderLayer(layer, structureShader); + if (Backend.canUseVBOs()) { + ContraptionProgram structureShader = Backend.getProgram(AllProgramSpecs.CONTRAPTION_STRUCTURE); + structureShader.bind(viewProjection, camX, camY, camZ, FastRenderDispatcher.getDebugMode()); + for (RenderedContraption renderer : renderers.values()) { + renderer.doRenderLayer(layer, structureShader); + } } - for (RenderedContraption renderer : renderers.values()) { - renderer.kinetics.render(layer, viewProjection, camX, camY, camZ, renderer::setup); - renderer.teardown(); + if (Backend.canUseInstancing()) { + for (RenderedContraption renderer : renderers.values()) { + renderer.kinetics.render(layer, viewProjection, camX, camY, camZ, renderer::setup); + renderer.teardown(); + } } layer.endDrawing(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java index a45f5a75e..1d8108e4e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/RenderedContraption.java @@ -6,6 +6,7 @@ import com.simibubi.create.content.contraptions.base.KineticRenderMaterials; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; +import com.simibubi.create.foundation.render.backend.Backend; import com.simibubi.create.foundation.render.backend.instancing.*; import com.simibubi.create.content.contraptions.components.actors.ContraptionActorData; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionLighter; @@ -52,8 +53,10 @@ public class RenderedContraption { this.renderWorld = setupRenderWorld(world, contraption); buildLayers(); - buildInstancedTiles(); - buildActors(); + if (Backend.canUseInstancing()) { + buildInstancedTiles(); + buildActors(); + } } public int getEntityId() { diff --git a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java index 75f97dcce..b6fa8ca1a 100644 --- a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java +++ b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java @@ -84,8 +84,9 @@ public class ConfigureConfigPacket extends SimplePacketBase { @OnlyIn(Dist.CLIENT) private static void experimentalRendering(String value) { - boolean last = AllConfigs.CLIENT.experimentalRendering.get(); - AllConfigs.CLIENT.experimentalRendering.set(Boolean.parseBoolean(value)); + if (!"".equals(value)) { + AllConfigs.CLIENT.experimentalRendering.set(Boolean.parseBoolean(value)); + } FastRenderDispatcher.refresh(); } diff --git a/src/main/java/com/simibubi/create/foundation/config/CClient.java b/src/main/java/com/simibubi/create/foundation/config/CClient.java index a7a0cb259..c4b2d7a1f 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CClient.java +++ b/src/main/java/com/simibubi/create/foundation/config/CClient.java @@ -15,8 +15,8 @@ public class CClient extends ConfigBase { public ConfigBool rainbowDebug = b(true, "enableRainbowDebug", "Show colourful debug information while the F3-Menu is open."); - public ConfigRender experimentalRendering = - new ConfigRender("experimentalRendering", true, "Use modern OpenGL features to drastically increase performance."); + public ConfigBool experimentalRendering = + b(true, "experimentalRendering", "Use modern OpenGL features to drastically increase performance."); public ConfigInt overlayOffsetX = i(20, Integer.MIN_VALUE, Integer.MAX_VALUE, "overlayOffsetX", "Offset the overlay from goggle- and hover- information by this many pixels on the X axis; Use /create overlay"); public ConfigInt overlayOffsetY = i(0, Integer.MIN_VALUE, Integer.MAX_VALUE, "overlayOffsetY", "Offset the overlay from goggle- and hover- information by this many pixels on the Y axis; Use /create overlay"); @@ -25,27 +25,4 @@ public class CClient extends ConfigBase { public String getName() { return "client"; } - - public class ConfigRender extends ConfigBool { - - public ConfigRender(String name, boolean def, String... comment) { - super(name, def, comment); - } - - /** - * Gets the configured value and checks if the rendering is actually supported. - * @return True if fast rendering is enabled and the current system/configuration is capable. - */ - @Override - public Boolean get() { - return super.get() && Backend.canUse(); - } - - @Override - public void set(Boolean value) { - super.set(value); - Backend.enabled = get(); - } - } - } diff --git a/src/main/java/com/simibubi/create/foundation/mixin/LightUpdateMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/LightUpdateMixin.java index 779c8e6d0..efc086daa 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/LightUpdateMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/LightUpdateMixin.java @@ -1,10 +1,13 @@ package com.simibubi.create.foundation.mixin; -import com.simibubi.create.foundation.render.backend.FastRenderDispatcher; +import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; +import com.simibubi.create.foundation.render.backend.light.ILightListener; import net.minecraft.client.multiplayer.ClientChunkProvider; import net.minecraft.util.math.SectionPos; +import net.minecraft.world.ILightReader; import net.minecraft.world.LightType; import net.minecraft.world.chunk.AbstractChunkProvider; +import net.minecraft.world.chunk.Chunk; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.spongepowered.asm.mixin.Mixin; @@ -12,6 +15,8 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.Map; + @OnlyIn(Dist.CLIENT) @Mixin(ClientChunkProvider.class) public abstract class LightUpdateMixin extends AbstractChunkProvider { @@ -24,6 +29,23 @@ public abstract class LightUpdateMixin extends AbstractChunkProvider { */ @Inject(at = @At("HEAD"), method = "markLightChanged") private void onLightUpdate(LightType type, SectionPos pos, CallbackInfo ci) { - FastRenderDispatcher.notifyLightUpdate(((ClientChunkProvider) (Object) this), type, pos); + ClientChunkProvider thi = ((ClientChunkProvider) (Object) this); + + Chunk chunk = thi.getChunk(pos.getSectionX(), pos.getSectionZ(), false); + + int sectionY = pos.getSectionY(); + + if (chunk != null) { + chunk.getTileEntityMap() + .entrySet() + .stream() + .filter(entry -> SectionPos.toChunk(entry.getKey().getY()) == sectionY) + .map(Map.Entry::getValue) + .filter(tile -> tile instanceof ILightListener) + .map(tile -> (ILightListener) tile) + .forEach(ILightListener::onChunkLightUpdate); + } + + ContraptionRenderDispatcher.notifyLightUpdate((ILightReader) thi.getWorld(), type, pos); } } diff --git a/src/main/java/com/simibubi/create/foundation/mixin/RenderInLayerMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/RenderInLayerMixin.java index a52cbe181..d0fcb46ba 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/RenderInLayerMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/RenderInLayerMixin.java @@ -1,11 +1,16 @@ package com.simibubi.create.foundation.mixin; import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; +import com.simibubi.create.foundation.render.backend.Backend; import com.simibubi.create.foundation.render.backend.FastRenderDispatcher; +import com.simibubi.create.foundation.render.backend.OptifineHandler; +import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.WorldRenderer; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import org.lwjgl.opengl.GL20; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -21,7 +26,21 @@ public class RenderInLayerMixin { * This should probably be a forge event. */ @Inject(at = @At(value = "TAIL"), method = "renderLayer") - private void renderLayer(RenderType type, MatrixStack stack, double cameraX, double cameraY, double cameraZ, CallbackInfo ci) { - FastRenderDispatcher.renderLayer(type, stack, (float) cameraX, (float) cameraY, (float) cameraZ); + private void renderLayer(RenderType type, MatrixStack stack, double camX, double camY, double camZ, CallbackInfo ci) { + if (!Backend.available()) return; + + float cameraX = (float) camX; + float cameraY = (float) camY; + float cameraZ = (float) camZ; + + Matrix4f viewProjection = Matrix4f.translate(-cameraX, -cameraY, -cameraZ); + viewProjection.multiplyBackward(stack.peek().getModel()); + viewProjection.multiplyBackward(FastRenderDispatcher.getProjectionMatrix()); + + FastRenderDispatcher.renderLayer(type, viewProjection, cameraX, cameraY, cameraZ); + + ContraptionRenderDispatcher.renderLayer(type, viewProjection, cameraX, cameraY, cameraZ); + + GL20.glUseProgram(0); } } diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/Backend.java b/src/main/java/com/simibubi/create/foundation/render/backend/Backend.java index d60f6dfc2..597b22279 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/Backend.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/Backend.java @@ -17,7 +17,6 @@ import net.minecraftforge.resource.ISelectiveResourceReloadListener; import net.minecraftforge.resource.VanillaResourceType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.lwjgl.opengl.ARBVertexProgram; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.system.MemoryUtil; @@ -42,10 +41,9 @@ public class Backend { private static final Map> registry = new HashMap<>(); private static final Map, GlProgram> programs = new HashMap<>(); - public static boolean enabled; + private static boolean enabled; public static GLCapabilities capabilities; - private static SystemCapability capability; private static MapBuffer mapBuffer; public Backend() { @@ -102,16 +100,24 @@ public class Backend { return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().orElse(last); } - public static boolean canUse() { - return isCapable() && !OptifineHandler.usingShaders(); + public static boolean canUseInstancing() { + return enabled && gl33(); } - public static SystemCapability getCapability() { - return capability; + public static boolean canUseVBOs() { + return enabled && gl20(); } - public static boolean isCapable() { - return capability.isCapable(); + public static boolean available() { + return enabled && gl20(); + } + + public static boolean gl33() { + return capabilities.OpenGL33; + } + + public static boolean gl20() { + return capabilities.OpenGL20; } public static void init() { @@ -132,9 +138,10 @@ public class Backend { capabilities = GL.createCapabilities(); mapBuffer = getLatest(MapBuffer.class); + OptifineHandler.refresh(); refresh(); - if (isCapable()) { + if (gl20()) { programs.values().forEach(GlProgram::delete); programs.clear(); @@ -146,13 +153,7 @@ public class Backend { } public static void refresh() { - if (capabilities.OpenGL33) { - capability = SystemCapability.CAPABLE; - } else { - capability = SystemCapability.INCAPABLE; - } - - enabled = AllConfigs.CLIENT.experimentalRendering.get(); + enabled = AllConfigs.CLIENT.experimentalRendering.get() && !OptifineHandler.usingShaders(); } private static

> void loadProgram(IResourceManager manager, S programSpec) { diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java index 3f268de7f..883ee29bd 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/FastRenderDispatcher.java @@ -30,6 +30,7 @@ import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; +import org.lwjgl.system.CallbackI; import javax.annotation.Nullable; import java.util.ArrayList; @@ -40,7 +41,7 @@ import java.util.function.Consumer; public class FastRenderDispatcher { public static WorldAttached> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet); - public static WorldAttached> addedLastTick = new WorldAttached<>(ConcurrentHashMap::newKeySet); + public static WorldAttached> addedLastTick = new WorldAttached<>(ConcurrentHashMap::new); private static Matrix4f projectionMatrixThisFrame = null; @@ -55,18 +56,36 @@ public class FastRenderDispatcher { public static void tick() { ClientWorld world = Minecraft.getInstance().world; - runQueue(addedLastTick.get(world), CreateClient.kineticRenderer::onLightUpdate); - CreateClient.kineticRenderer.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. + int ticks = AnimationTickHolder.getTicks(); + + ConcurrentHashMap map = addedLastTick.get(world); + map + .entrySet() + .stream() + .filter(it -> ticks - it.getValue() > 10) + .map(Map.Entry::getKey) + .forEach(te -> { + map.remove(te); + + CreateClient.kineticRenderer.onLightUpdate(te); + }); + + + if (ticks % 10 == 0) { + CreateClient.kineticRenderer.clean(); + } runQueue(queuedUpdates.get(world), CreateClient.kineticRenderer::update); } public static boolean available() { - return Backend.enabled; + return Backend.canUseInstancing(); } public static boolean available(World world) { - return Backend.enabled && !(world instanceof SchematicWorld); + return Backend.canUseInstancing() && !(world instanceof SchematicWorld); } public static int getDebugMode() { @@ -77,6 +96,7 @@ public class FastRenderDispatcher { RenderWork.enqueue(() -> { CreateClient.kineticRenderer.invalidate(); OptifineHandler.refresh(); + Backend.refresh(); Minecraft.getInstance().worldRenderer.loadRenderers(); ClientWorld world = Minecraft.getInstance().world; if (world != null) world.loadedTileEntityList.forEach(CreateClient.kineticRenderer::add); @@ -97,12 +117,8 @@ public class FastRenderDispatcher { } } - public static void renderLayer(RenderType layer, MatrixStack stack, float cameraX, float cameraY, float cameraZ) { - if (!available()) return; - - Matrix4f viewProjection = Matrix4f.translate(-cameraX, -cameraY, -cameraZ); - viewProjection.multiplyBackward(stack.peek().getModel()); - viewProjection.multiplyBackward(getProjectionMatrix()); + public static void renderLayer(RenderType layer, Matrix4f viewProjection, float cameraX, float cameraY, float cameraZ) { + if (!Backend.canUseInstancing()) return; layer.startDrawing(); @@ -113,31 +129,9 @@ public class FastRenderDispatcher { RenderSystem.disableCull(); //RenderSystem.disableDepthTest(); - ContraptionRenderDispatcher.renderLayer(layer, viewProjection, cameraX, cameraY, cameraZ); - if (!OptifineHandler.usingShaders()) - GL20.glUseProgram(0); layer.endDrawing(); } - public static void notifyLightUpdate(ClientChunkProvider world, LightType type, SectionPos pos) { - ContraptionRenderDispatcher.notifyLightUpdate((ILightReader) world.getWorld(), type, pos); - - Chunk chunk = world.getChunk(pos.getSectionX(), pos.getSectionZ(), false); - - int sectionY = pos.getSectionY(); - - if (chunk != null) { - chunk.getTileEntityMap() - .entrySet() - .stream() - .filter(entry -> SectionPos.toChunk(entry.getKey().getY()) == sectionY) - .map(Map.Entry::getValue) - .filter(tile -> tile instanceof ILightListener) - .map(tile -> (ILightListener) tile) - .forEach(ILightListener::onChunkLightUpdate); - } - } - // copied from GameRenderer.renderWorld public static Matrix4f getProjectionMatrix() { if (projectionMatrixThisFrame != null) return projectionMatrixThisFrame; diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/SystemCapability.java b/src/main/java/com/simibubi/create/foundation/render/backend/SystemCapability.java deleted file mode 100644 index 9a4678e6f..000000000 --- a/src/main/java/com/simibubi/create/foundation/render/backend/SystemCapability.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.simibubi.create.foundation.render.backend; - -public enum SystemCapability { - /** - * The current system does not support enough - * OpenGL features to enable fast rendering. - */ - INCAPABLE, - - /** - * The current system supports OpenGL 3.3. - */ - CAPABLE, - ; - - public boolean isCapable() { - return this == CAPABLE; - } -} diff --git a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java index 4e9bd2fee..35507966f 100644 --- a/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/render/backend/instancing/InstancedTileRenderer.java @@ -37,7 +37,7 @@ public abstract class InstancedTileRenderer

{ @SuppressWarnings("unchecked") @Nullable public TileEntityInstance getInstance(T tile, boolean create) { - if (!Backend.enabled) return null; + if (!Backend.canUseInstancing()) return null; TileEntityInstance instance = instances.get(tile); @@ -47,7 +47,7 @@ public abstract class InstancedTileRenderer

{ TileEntityInstance renderer = InstancedTileRenderRegistry.instance.create(this, tile); if (renderer != null) { - FastRenderDispatcher.addedLastTick.get(tile.getWorld()).add(tile); + FastRenderDispatcher.addedLastTick.get(tile.getWorld()).put(tile, AnimationTickHolder.getTicks()); instances.put(tile, renderer); } @@ -93,11 +93,7 @@ public abstract class InstancedTileRenderer

{ } 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.getTicks() % 10 == 0) { - instances.keySet().stream().filter(TileEntity::isRemoved).forEach(instances::remove); - } + instances.keySet().stream().filter(TileEntity::isRemoved).forEach(instances::remove); } public void invalidate() {