From 2ed323b9f28bec4fb8d85d3af99095ca223b1fae Mon Sep 17 00:00:00 2001 From: xCollateral <> Date: Mon, 11 Sep 2023 11:30:55 +0200 Subject: [PATCH] Entities optimizations --- .../java/net/vulkanmod/config/Config.java | 1 + .../java/net/vulkanmod/config/Options.java | 7 +- .../mixin/chunk/LevelRendererMixin.java | 79 +++---------------- .../mixin/render/entity/EntityRendererM.java | 54 +++++++++++++ .../vulkanmod/render/chunk/WorldRenderer.java | 24 +++++- src/main/resources/vulkanmod.mixins.json | 1 + 6 files changed, 92 insertions(+), 74 deletions(-) create mode 100644 src/main/java/net/vulkanmod/mixin/render/entity/EntityRendererM.java diff --git a/src/main/java/net/vulkanmod/config/Config.java b/src/main/java/net/vulkanmod/config/Config.java index 08fc8d243..b4a3e67e1 100644 --- a/src/main/java/net/vulkanmod/config/Config.java +++ b/src/main/java/net/vulkanmod/config/Config.java @@ -19,6 +19,7 @@ public class Config { public int advCulling = 2; public boolean indirectDraw = false; public boolean uniqueOpaqueLayer = true; + public boolean entityCulling = true; private static Path path; diff --git a/src/main/java/net/vulkanmod/config/Options.java b/src/main/java/net/vulkanmod/config/Options.java index b6e4312a1..a6401f89c 100644 --- a/src/main/java/net/vulkanmod/config/Options.java +++ b/src/main/java/net/vulkanmod/config/Options.java @@ -203,12 +203,17 @@ Enable Gui optimizations (Stats bar, Chat, Debug Hud) Use a culling algorithm that might improve performance by reducing the number of non visible chunk sections rendered. """)), + new SwitchOption("Entity Culling", + value -> config.entityCulling = value, + () -> config.entityCulling) + .setTooltip(Component.nullToEmpty(""" + Enables culling for entities on non visible sections.""")), new SwitchOption("Indirect Draw", value -> config.indirectDraw = value, () -> config.indirectDraw) .setTooltip(Component.nullToEmpty(""" Reduces CPU overhead but increases GPU overhead. - Enabling it might help in CPU limited systems.""")), + Enabling it might help in CPU limited systems.""")) }; } diff --git a/src/main/java/net/vulkanmod/mixin/chunk/LevelRendererMixin.java b/src/main/java/net/vulkanmod/mixin/chunk/LevelRendererMixin.java index ec9d9b917..798b15314 100644 --- a/src/main/java/net/vulkanmod/mixin/chunk/LevelRendererMixin.java +++ b/src/main/java/net/vulkanmod/mixin/chunk/LevelRendererMixin.java @@ -17,6 +17,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.Vec3; +import net.vulkanmod.Initializer; import net.vulkanmod.render.chunk.WorldRenderer; import net.vulkanmod.render.profiling.Profiler2; import net.vulkanmod.vulkan.util.Pair; @@ -46,20 +47,13 @@ public abstract class LevelRendererMixin { @Shadow private boolean generateClouds; @Shadow @Final private EntityRenderDispatcher entityRenderDispatcher; - @Shadow protected abstract boolean shouldShowEntityOutlines(); - - @Shadow public abstract void needsUpdate(); - - @Shadow public abstract void renderLevel(PoseStack poseStack, float f, long l, boolean bl, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f matrix4f); - private WorldRenderer worldRenderer; - @Unique - private static boolean ENTITIES_OPT = true; - @Unique private Object2ReferenceOpenHashMap, ObjectArrayList>> entitiesMap = new Object2ReferenceOpenHashMap<>(); + //TODO clear VBOs + @Inject(method = "", at = @At("RETURN")) private void init(Minecraft minecraft, EntityRenderDispatcher entityRenderDispatcher, BlockEntityRenderDispatcher blockEntityRenderDispatcher, RenderBuffers renderBuffers, CallbackInfo ci) { this.worldRenderer = WorldRenderer.init(this.renderBuffers); @@ -89,8 +83,6 @@ private void renderBlockEntities(PoseStack poseStack, float f, long l, boolean b private void setupRender(Camera camera, Frustum frustum, boolean isCapturedFrustum, boolean spectator) { this.worldRenderer.setupRenderer(camera, frustum, isCapturedFrustum, spectator); - ENTITIES_OPT = true; -// ENTITIES_OPT = false; entitiesMap.clear(); } @@ -200,10 +192,11 @@ private void popProfiler3(PoseStack poseStack, float f, long l, boolean bl, Came */ @Overwrite private void renderEntity(Entity entity, double d, double e, double f, float g, PoseStack poseStack, MultiBufferSource multiBufferSource) { - if(!ENTITIES_OPT) { - double h = Mth.lerp((double)g, entity.xOld, entity.getX()); - double i = Mth.lerp((double)g, entity.yOld, entity.getY()); - double j = Mth.lerp((double)g, entity.zOld, entity.getZ()); + //Entity lists optimization + if(!Initializer.CONFIG.entityCulling) { + double h = Mth.lerp(g, entity.xOld, entity.getX()); + double i = Mth.lerp(g, entity.yOld, entity.getY()); + double j = Mth.lerp(g, entity.zOld, entity.getZ()); float k = Mth.lerp(g, entity.yRotO, entity.getYRot()); this.entityRenderDispatcher.render(entity, h - d, i - e, j - f, k, g, poseStack, multiBufferSource, this.entityRenderDispatcher.getPackedLightCoords(entity, g)); return; @@ -226,7 +219,7 @@ private void renderEntity(Entity entity, double d, double e, double f, float g, shift = At.Shift.AFTER, ordinal = 0) ) private void renderEntities(PoseStack poseStack, float partialTicks, long l, boolean bl, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f matrix4f, CallbackInfo ci) { - if(!ENTITIES_OPT) + if(!Initializer.CONFIG.entityCulling) return; Vec3 cameraPos = WorldRenderer.getCameraPos(); @@ -245,58 +238,4 @@ private void renderEntities(PoseStack poseStack, float partialTicks, long l, boo } } -// @Redirect(method = "renderLevel", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/ClientLevel;entitiesForRendering()Ljava/lang/Iterable;")) -// private Iterable replaceIterator(ClientLevel instance) { -// -// return () -> new Iterator() { -// @Override -// public boolean hasNext() { -// return false; -// } -// -// @Override -// public Entity next() { -// return null; -// } -// }; -// } -// -// @Inject(method = "renderLevel", at = @At(value = "INVOKE", -// target = "Lnet/minecraft/client/multiplayer/ClientLevel;entitiesForRendering()Ljava/lang/Iterable;", -// shift = At.Shift.AFTER), -// locals = LocalCapture.CAPTURE_FAILHARD -// ) -// private void renderEntities(PoseStack poseStack, float f, long l, boolean bl, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f matrix4f, CallbackInfo ci) { -// for(Entity entity : this.level.entitiesForRendering()) { -// if (this.entityRenderDispatcher.shouldRender(entity, frustum, d0, d1, d2) || entity.hasIndirectPassenger(this.minecraft.player)) { -// BlockPos blockpos = entity.blockPosition(); -// if ((this.level.isOutsideBuildHeight(blockpos.getY()) || this.isChunkCompiled(blockpos)) && (entity != p_109604_.getEntity() || p_109604_.isDetached() || p_109604_.getEntity() instanceof LivingEntity && ((LivingEntity)p_109604_.getEntity()).isSleeping()) && (!(entity instanceof LocalPlayer) || p_109604_.getEntity() == entity)) { -// ++this.renderedEntities; -// if (entity.tickCount == 0) { -// entity.xOld = entity.getX(); -// entity.yOld = entity.getY(); -// entity.zOld = entity.getZ(); -// } -// -// MultiBufferSource multibuffersource; -// if (this.shouldShowEntityOutlines() && this.minecraft.shouldEntityAppearGlowing(entity)) { -// flag3 = true; -// OutlineBufferSource outlinebuffersource = this.renderBuffers.outlineBufferSource(); -// multibuffersource = outlinebuffersource; -// int i = entity.getTeamColor(); -// int j = 255; -// int k = i >> 16 & 255; -// int l = i >> 8 & 255; -// int i1 = i & 255; -// outlinebuffersource.setColor(k, l, i1, 255); -// } else { -// multibuffersource = bu; -// } -// -// this.renderEntity(entity, d0, d1, d2, p_109601_, p_109600_, multibuffersource); -// } -// } -// } -// } - } diff --git a/src/main/java/net/vulkanmod/mixin/render/entity/EntityRendererM.java b/src/main/java/net/vulkanmod/mixin/render/entity/EntityRendererM.java new file mode 100644 index 000000000..e4af8f380 --- /dev/null +++ b/src/main/java/net/vulkanmod/mixin/render/entity/EntityRendererM.java @@ -0,0 +1,54 @@ +package net.vulkanmod.mixin.render.entity; + +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.entity.EntityRenderer; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.vulkanmod.Initializer; +import net.vulkanmod.render.chunk.WorldRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(EntityRenderer.class) +public class EntityRendererM { + +// /** +// * @author +// * @reason +// */ +// @Overwrite +// public boolean shouldRender(T entity, Frustum frustum, double d, double e, double f) { +// if (!entity.shouldRender(d, e, f)) { +// return false; +// } else if (entity.noCulling) { +// return true; +// } else { +// AABB aABB = entity.getBoundingBoxForCulling().inflate(0.5); +// if (aABB.hasNaN() || aABB.getSize() == 0.0) { +// aABB = new AABB(entity.getX() - 2.0, entity.getY() - 2.0, entity.getZ() - 2.0, entity.getX() + 2.0, entity.getY() + 2.0, entity.getZ() + 2.0); +// } +// +//// WorldRenderer.getInstance().getSectionGrid().getSectionAtBlockPos((int) entity.getX(), (int) entity.getY(), (int) entity.getZ()); +// WorldRenderer worldRenderer = WorldRenderer.getInstance(); +//// return (worldRenderer.getLastFrame() == worldRenderer.getSectionGrid().getSectionAtBlockPos(entity.getBlockX(), entity.getBlockY(), entity.getBlockZ()).getLastFrame()); +// +// return frustum.isVisible(aABB); +// } +// } + + @Redirect(method = "shouldRender", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/culling/Frustum;isVisible(Lnet/minecraft/world/phys/AABB;)Z")) + private boolean isVisible(Frustum frustum, AABB aABB) { + if(Initializer.CONFIG.entityCulling) { + WorldRenderer worldRenderer = WorldRenderer.getInstance(); + + Vec3 pos = aABB.getCenter(); + + return (worldRenderer.getLastFrame() == worldRenderer.getSectionGrid().getSectionAtBlockPos((int) pos.x(), (int) pos.y(), (int) pos.z()).getLastFrame()); + } else { + return frustum.isVisible(aABB); + } + + } +} diff --git a/src/main/java/net/vulkanmod/render/chunk/WorldRenderer.java b/src/main/java/net/vulkanmod/render/chunk/WorldRenderer.java index 47773784d..b181f920f 100644 --- a/src/main/java/net/vulkanmod/render/chunk/WorldRenderer.java +++ b/src/main/java/net/vulkanmod/render/chunk/WorldRenderer.java @@ -43,7 +43,6 @@ import net.vulkanmod.vulkan.memory.IndirectBuffer; import net.vulkanmod.vulkan.memory.MemoryTypes; import net.vulkanmod.vulkan.shader.Pipeline; -import net.vulkanmod.vulkan.shader.ShaderManager; import org.joml.FrustumIntersection; import org.joml.Matrix4f; @@ -91,6 +90,8 @@ public class WorldRenderer { public RenderRegionCache renderRegionCache; int nonEmptyChunks; + private final List onAllChangedCallbacks = new ObjectArrayList<>(); + private WorldRenderer(RenderBuffers renderBuffers) { this.minecraft = Minecraft.getInstance(); this.renderBuffers = renderBuffers; @@ -258,6 +259,7 @@ private void initializeQueueForFullUpdate(Camera camera) { RenderSection renderSection1 = this.sectionGrid.getSectionAtBlockPos(new BlockPos(k + SectionPos.sectionToBlockCoord(i1, 8), j, l + SectionPos.sectionToBlockCoord(j1, 8))); if (renderSection1 != null) { renderSection1.setGraphInfo(null, (byte) 0); + renderSection1.setLastFrame(this.lastFrame); list.add(renderSection1); } @@ -275,6 +277,7 @@ private void initializeQueueForFullUpdate(Camera camera) { } else { renderSection.setGraphInfo(null, (byte) 0); + renderSection.setLastFrame(this.lastFrame); this.chunkQueue.add(renderSection); } @@ -492,6 +495,8 @@ public void allChanged() { this.sectionGrid = new SectionGrid(this.level, this.minecraft.options.getEffectiveRenderDistance()); this.chunkAreaQueue = new AreaSetQueue(this.sectionGrid.chunkAreaManager.size); + this.onAllChangedCallbacks.forEach(Runnable::run); + Entity entity = this.minecraft.getCameraEntity(); if (entity != null) { this.sectionGrid.repositionCamera(entity.getX(), entity.getZ()); @@ -511,7 +516,7 @@ public void setLevel(@Nullable ClientLevel level) { this.level = level; if (level != null) { this.allChanged(); - } else { + } else { if (this.sectionGrid != null) { this.sectionGrid.releaseAllBuffers(); this.sectionGrid = null; @@ -524,6 +529,14 @@ public void setLevel(@Nullable ClientLevel level) { } + public void addOnAllChangedCallback(Runnable runnable) { + this.onAllChangedCallbacks.add(runnable); + } + + public void clearOnAllChangedCallbacks() { + this.onAllChangedCallbacks.clear(); + } + public void renderChunkLayer(RenderType renderType, PoseStack poseStack, double camX, double camY, double camZ, Matrix4f projection) { //debug // Profiler p = Profiler.getProfiler("chunks"); @@ -572,7 +585,7 @@ else if(layerName.equals("translucent")) VRenderSystem.applyMVP(poseStack.last().pose(), projection); Renderer renderer = Renderer.getInstance(); - Pipeline pipeline = ShaderManager.getInstance().getTerrainShader(renderType); + Pipeline pipeline = TerrainShaderManager.getInstance().getTerrainShader(renderType); renderer.bindPipeline(pipeline); Renderer.getDrawer().bindAutoIndexBuffer(Renderer.getCommandBuffer(), 7); @@ -730,4 +743,9 @@ public String getChunkStatistics() { return String.format("Chunks: %d(%d)/%d D: %d, %s", this.nonEmptyChunks, j, i, this.lastViewDistance, tasksInfo); } + public void cleanUp() { + if(indirectBuffers != null) + Arrays.stream(indirectBuffers).forEach(Buffer::freeBuffer); + } + } diff --git a/src/main/resources/vulkanmod.mixins.json b/src/main/resources/vulkanmod.mixins.json index ad01a6d4f..6f874367a 100644 --- a/src/main/resources/vulkanmod.mixins.json +++ b/src/main/resources/vulkanmod.mixins.json @@ -30,6 +30,7 @@ "profiling.GuiMixin", "profiling.KeyboardHandlerM", + "render.entity.EntityRendererM", "render.model.ModelPartCubeM", "render.model.ModelPartM", "render.vertex.FaceBakeryM",