Little things.

- Quark magnets don't crash (already fixed).
 - Fix quark magnet leaving behind ghost instances.
 - Fix crash with belt lighting after being placed by contraptions.
 - Simplify tile add/remove mixins and avoid conflict with Performant.
 - Avoid FloatBuffer detour when uploading matrix uniforms.
 - InstancedTileRenderer no longer has to clean up.
 - Properly let go of tickable instances.
This commit is contained in:
JozsefA 2021-03-24 15:48:15 -07:00
parent 7eafbe5757
commit b18993ed26
13 changed files with 300 additions and 290 deletions

View file

@ -548,6 +548,7 @@ public class BeltTileEntity extends KineticTileEntity implements LightUpdateList
} }
private void initializeLight() { private void initializeLight() {
if (beltLength > 0) {
light = new byte[beltLength * 2]; light = new byte[beltLength * 2];
Vec3i vec = getBeltFacing().getDirectionVec(); Vec3i vec = getBeltFacing().getDirectionVec();
@ -562,6 +563,7 @@ public class BeltTileEntity extends KineticTileEntity implements LightUpdateList
pos.move(vec.getX(), verticality, vec.getZ()); pos.move(vec.getX(), verticality, vec.getZ());
} }
} }
}
private void updateBlockLight() { private void updateBlockLight() {
Vec3i vec = getBeltFacing().getDirectionVec(); Vec3i vec = getBeltFacing().getDirectionVec();

View file

@ -1,72 +0,0 @@
package com.simibubi.create.foundation.mixin;
import com.simibubi.create.foundation.render.KineticRenderer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import com.simibubi.create.CreateClient;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.Set;
@OnlyIn(Dist.CLIENT)
@Mixin(value = World.class, priority = 1042)
public class AddRemoveTileMixin {
@Shadow @Final public boolean isRemote;
@Shadow @Final protected Set<TileEntity> tileEntitiesToBeRemoved;
/**
* JUSTIFICATION: This method is called whenever a tile entity is removed due
* to a change in block state, even on the client. By hooking into this method,
* we gain easy access to the information while having no impact on performance.
*/
@Inject(at = @At(
value = "INVOKE_ASSIGN",
target = "Lnet/minecraft/world/World;getTileEntity(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/tileentity/TileEntity;"
),
method = "removeTileEntity",
locals = LocalCapture.CAPTURE_FAILHARD
)
private void onRemoveTile(BlockPos pos, CallbackInfo ci, TileEntity te) {
if (isRemote) {
World thi = (World)(Object) this;
CreateClient.kineticRenderer.get(thi).remove(te);
}
}
@Inject(at = @At("TAIL"), method = "addTileEntity")
private void onAddTile(TileEntity te, CallbackInfoReturnable<Boolean> cir) {
if (isRemote) {
World thi = (World)(Object) this;
CreateClient.kineticRenderer.get(thi).queueAdd(te);
}
}
@Inject(at = @At(
value = "INVOKE",
target = "Ljava/util/Set;clear()V", ordinal = 0
),
method = "tickBlockEntities")
private void onChunkUnload(CallbackInfo ci) {
if (isRemote) {
World thi = (World)(Object) this;
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(thi);
for (TileEntity tile : tileEntitiesToBeRemoved) {
kineticRenderer.remove(tile);
}
}
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.foundation.mixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.simibubi.create.CreateClient;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(World.class)
public class TileAddMixin {
@Shadow @Final public boolean isRemote;
@Inject(at = @At("TAIL"), method = "addTileEntity")
private void onAddTile(TileEntity te, CallbackInfoReturnable<Boolean> cir) {
if (isRemote) {
CreateClient.kineticRenderer.get((World)(Object) this).queueAdd(te);
}
}
}

View file

@ -0,0 +1,25 @@
package com.simibubi.create.foundation.mixin;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.simibubi.create.CreateClient;
@Mixin(TileEntity.class)
public class TileRemoveMixin {
@Shadow @Nullable protected World world;
@Inject(at = @At("TAIL"), method = "remove")
private void onRemove(CallbackInfo ci) {
if (world instanceof ClientWorld)
CreateClient.kineticRenderer.get(this.world).remove((TileEntity) (Object) this);
}
}

View file

@ -11,6 +11,7 @@ import com.simibubi.create.foundation.render.backend.instancing.IFlywheelWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@ -26,16 +27,15 @@ public class Backend {
public static final Boolean SHADER_DEBUG_OUTPUT = true; public static final Boolean SHADER_DEBUG_OUTPUT = true;
public static final Logger log = LogManager.getLogger(Backend.class); public static final Logger log = LogManager.getLogger(Backend.class);
public static final FloatBuffer MATRIX_BUFFER = MemoryUtil.memAllocFloat(16); public static GLCapabilities capabilities;
public static GlFeatureCompat compat;
private static boolean instancingAvailable;
private static boolean enabled;
static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>(); static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>();
static final Map<ProgramSpec<?>, ProgramGroup<?>> programs = new HashMap<>(); static final Map<ProgramSpec<?>, ProgramGroup<?>> programs = new HashMap<>();
private static boolean enabled;
public static GLCapabilities capabilities;
public static GlFeatureCompat compat;
public Backend() { public Backend() {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@ -66,10 +66,7 @@ public class Backend {
} }
public static boolean canUseInstancing() { public static boolean canUseInstancing() {
return enabled && return enabled && instancingAvailable;
compat.vertexArrayObjectsSupported() &&
compat.drawInstancedSupported() &&
compat.instancedArraysSupported();
} }
public static boolean canUseVBOs() { public static boolean canUseVBOs() {
@ -98,7 +95,14 @@ public class Backend {
} }
public static void refresh() { public static void refresh() {
capabilities = GL.createCapabilities();
compat = new GlFeatureCompat(capabilities);
instancingAvailable = compat.vertexArrayObjectsSupported() &&
compat.drawInstancedSupported() &&
compat.instancedArraysSupported();
enabled = AllConfigs.CLIENT.experimentalRendering.get() && !OptifineHandler.usingShaders(); enabled = AllConfigs.CLIENT.experimentalRendering.get() && !OptifineHandler.usingShaders();
} }
} }

View file

@ -3,8 +3,7 @@ package com.simibubi.create.foundation.render.backend;
import net.minecraft.client.renderer.Matrix3f; import net.minecraft.client.renderer.Matrix3f;
import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.Matrix4f;
import java.nio.ByteBuffer; import com.mojang.blaze3d.matrix.MatrixStack;
import java.nio.FloatBuffer;
public class RenderUtil { public class RenderUtil {
public static int nextPowerOf2(int a) { public static int nextPowerOf2(int a) {
@ -17,9 +16,12 @@ public class RenderUtil {
return b == 0 && n != 0; return b == 0 && n != 0;
} }
// GPUs want matrices in column major order. public static float[] writeMatrixStack(MatrixStack stack) {
return writeMatrixStack(stack.peek().getModel(), stack.peek().getNormal());
}
public static float[] bufferMatrices(Matrix4f model, Matrix3f normal) { // GPUs want matrices in column major order.
public static float[] writeMatrixStack(Matrix4f model, Matrix3f normal) {
return new float[] { return new float[] {
model.a00, model.a00,
model.a10, model.a10,
@ -48,4 +50,25 @@ public class RenderUtil {
normal.a22, normal.a22,
}; };
} }
public static float[] writeMatrix(Matrix4f model) {
return new float[]{
model.a00,
model.a10,
model.a20,
model.a30,
model.a01,
model.a11,
model.a21,
model.a31,
model.a02,
model.a12,
model.a22,
model.a32,
model.a03,
model.a13,
model.a23,
model.a33,
};
}
} }

View file

@ -35,9 +35,6 @@ public class ShaderLoader {
static void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) { static void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
if (predicate.test(VanillaResourceType.SHADERS)) { if (predicate.test(VanillaResourceType.SHADERS)) {
Backend.capabilities = GL.createCapabilities();
Backend.compat = new GlFeatureCompat(Backend.capabilities);
OptifineHandler.refresh(); OptifineHandler.refresh();
Backend.refresh(); Backend.refresh();

View file

@ -1,5 +1,6 @@
package com.simibubi.create.foundation.render.backend.gl; package com.simibubi.create.foundation.render.backend.gl;
import com.simibubi.create.foundation.render.backend.RenderUtil;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode; import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
@ -53,9 +54,6 @@ public class BasicProgram extends GlProgram {
} }
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) { protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
Backend.MATRIX_BUFFER.position(0); GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
mat.write(Backend.MATRIX_BUFFER);
Backend.MATRIX_BUFFER.rewind();
GL20.glUniformMatrix4fv(uniform, false, Backend.MATRIX_BUFFER);
} }
} }

View file

@ -2,7 +2,8 @@ package com.simibubi.create.foundation.render.backend.instancing;
public interface IDynamicInstance { public interface IDynamicInstance {
/** /**
* Called every frame, this can be used to make more dynamic animations. * Called every frame. This can be used to smoothly change instance data
* to allow for fancy animations that could not be achieved on the GPU alone.
*/ */
void beginFrame(); void beginFrame();
} }

View file

@ -2,5 +2,9 @@ package com.simibubi.create.foundation.render.backend.instancing;
public interface ITickableInstance { public interface ITickableInstance {
/**
* Called every tick. This is useful for things that don't have to be smooth,
* or to recalculate something that would only change after a game tick.
*/
void tick(); void tick();
} }

View file

@ -39,14 +39,6 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
public abstract void registerMaterials(); public abstract void registerMaterials();
public void tick() { public void tick() {
int ticks = AnimationTickHolder.getTicks();
// 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 (ticks % 10 == 0) {
clean();
}
if (tickableInstances.size() > 0) if (tickableInstances.size() > 0)
tickableInstances.values().forEach(ITickableInstance::tick); tickableInstances.values().forEach(ITickableInstance::tick);
} }
@ -187,16 +179,13 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
return renderer; return renderer;
} }
private void clean() {
instances.keySet().removeIf(TileEntity::isRemoved);
}
public void invalidate() { public void invalidate() {
for (RenderMaterial<?, ?> material : materials.values()) { for (RenderMaterial<?, ?> material : materials.values()) {
material.delete(); material.delete();
} }
instances.clear(); instances.clear();
dynamicInstances.clear(); dynamicInstances.clear();
tickableInstances.clear();
} }
public boolean canCreateInstance(TileEntity tile) { public boolean canCreateInstance(TileEntity tile) {

View file

@ -16,7 +16,7 @@ public class ModelData extends BasicData {
} }
public ModelData setTransform(MatrixStack stack) { public ModelData setTransform(MatrixStack stack) {
matrices = RenderUtil.bufferMatrices(stack.peek().getModel(), stack.peek().getNormal()); matrices = RenderUtil.writeMatrixStack(stack);
return this; return this;
} }

View file

@ -3,9 +3,19 @@
"package": "com.simibubi.create.foundation.mixin", "package": "com.simibubi.create.foundation.mixin",
"compatibilityLevel": "JAVA_8", "compatibilityLevel": "JAVA_8",
"refmap": "create.refmap.json", "refmap": "create.refmap.json",
"client": ["AddRemoveTileMixin", "CancelTileEntityRenderMixin", "FogColorTrackerMixin", "LightUpdateMixin", "NetworkLightUpdateMixin", "RenderHooksMixin", "ShaderCloseMixin"], "mixins": ["StepSoundMixin"],
"client": [
"TileAddMixin",
"CancelTileEntityRenderMixin",
"FogColorTrackerMixin",
"LightUpdateMixin",
"NetworkLightUpdateMixin",
"RenderHooksMixin",
"ShaderCloseMixin",
"TileRemoveMixin"
],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
}, },
"minVersion": "0.8", "mixins": ["StepSoundMixin"] "minVersion": "0.8"
} }