kinda separate system capability stuff, should probably clean this up more.

fix lighting glitches on world load.
This commit is contained in:
JozsefA 2021-02-12 16:36:20 -08:00
parent d824304f12
commit a5f3d799d1
12 changed files with 123 additions and 125 deletions

View File

@ -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<C extends AbstractContra
transform(entity, partialTicks, matrixStacks);
Contraption contraption = entity.getContraption();
if (contraption != null) {
if (!FastRenderDispatcher.available()) {
ContraptionRenderer.render(entity.world, contraption, ms, msLocal, buffers);
} else {
if (Backend.canUseVBOs()) {
ContraptionRenderer.renderDynamic(entity.world, contraption, ms, msLocal, buffers);
}
else {
ContraptionRenderer.render(entity.world, contraption, ms, msLocal, buffers);
}
}
ms.pop();

View File

@ -5,6 +5,7 @@ import java.util.Random;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.foundation.render.*;
import com.simibubi.create.foundation.render.backend.Backend;
import com.simibubi.create.foundation.render.backend.FastRenderDispatcher;
import org.apache.commons.lang3.tuple.Pair;
import org.lwjgl.opengl.GL11;
@ -75,7 +76,7 @@ public class ContraptionRenderer {
protected static void renderTileEntities(World world, Contraption c, MatrixStack ms, MatrixStack msLocal,
IRenderTypeBuffer buffer) {
if (FastRenderDispatcher.available()) {
if (Backend.canUseVBOs()) {
ContraptionRenderDispatcher.renderTileEntities(world, c, ms, msLocal, buffer);
} else {
TileEntityRenderHelper.renderTileEntities(world, c.maybeInstancedTileEntities, ms, msLocal, buffer);

View File

@ -34,10 +34,6 @@ import java.util.Map;
public class ContraptionRenderDispatcher {
public static final HashMap<Integer, RenderedContraption> 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 <C extends AbstractContraptionEntity> void updateTransform(C c, AbstractContraptionEntityRenderer<C> 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();

View File

@ -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() {

View File

@ -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();
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>();
private static final Map<ProgramSpec<?>, 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 <P extends GlProgram, S extends ProgramSpec<P>> void loadProgram(IResourceManager manager, S programSpec) {

View File

@ -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<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> addedLastTick = new WorldAttached<>(ConcurrentHashMap::newKeySet);
public static WorldAttached<ConcurrentHashMap<TileEntity, Integer>> 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<TileEntity, Integer> 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;

View File

@ -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;
}
}

View File

@ -37,7 +37,7 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
@SuppressWarnings("unchecked")
@Nullable
public <T extends TileEntity> TileEntityInstance<? super T> 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<P extends BasicProgram> {
TileEntityInstance<? super T> 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<P extends BasicProgram> {
}
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() {