diff --git a/build.gradle b/build.gradle index b560e07..605cd45 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,10 @@ repositories { } } +loom { + accessWidenerPath = file("src/main/resources/brute_force_rendering_culling.accesswidener") +} + dependencies { // To change the versions see the gradle.properties file minecraft "com.mojang:minecraft:${project.minecraft_version}" @@ -42,6 +46,8 @@ dependencies { implementation("org.anarres:jcpp:1.4.14") modImplementation("io.github.douira:glsl-transformer:2.0.0-pre13") + + modImplementation('maven.modrinth:nvidium:0.2.6-beta') } processResources { diff --git a/gradle.properties b/gradle.properties index 4cc94ec..6444d17 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10 loader_version=0.15.9 # Mod Properties -mod_version=0.4.8 +mod_version=0.5.1 maven_group=rogo.renderingculling archives_base_name=BruteForceRenderingCulling-fabric-1.20.1 diff --git a/src/main/java/rogo/renderingculling/api/ChunkCullingMap.java b/src/main/java/rogo/renderingculling/api/ChunkCullingMap.java deleted file mode 100644 index a52674e..0000000 --- a/src/main/java/rogo/renderingculling/api/ChunkCullingMap.java +++ /dev/null @@ -1,64 +0,0 @@ -package rogo.renderingculling.api; - -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.util.Mth; -import net.minecraft.world.phys.Vec3; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayDeque; -import java.util.HashSet; -import java.util.Queue; -import java.util.Set; - -public class ChunkCullingMap extends CullingMap { - private int renderDistance = 0; - private int spacePartitionSize = 0; - - public ChunkCullingMap(int width, int height) { - super(width, height); - } - - @Override - int configDelayCount() { - return Config.getDepthUpdateDelay(); - } - - @Override - int bindFrameBufferId() { - return CullingHandler.CHUNK_CULLING_MAP_TARGET.frameBufferId; - } - - public void generateIndex(int renderDistance) { - this.renderDistance = renderDistance; - spacePartitionSize = 2 * renderDistance + 1; - } - - public boolean isChunkOffsetCameraVisible(int x, int y, int z) { - Vec3 camera = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); - int cameraX = (int)camera.x >> 4; - int cameraZ = (int)camera.z >> 4; - - if(y < 0) - y -= 9; - - int chunkX = x >> 4; - int chunkY = (int) (y * 0.0625) + CullingHandler.LEVEL_MIN_SECTION_ABS; - int chunkZ = z >> 4; - - int posX = chunkX - cameraX; - int posZ = chunkZ - cameraZ; - - return isChunkVisible(posX, chunkY, posZ); - } - - public boolean isChunkVisible(int posX, int posY, int posZ) { - int index = 1 + ((((posX + renderDistance) * spacePartitionSize * CullingHandler.LEVEL_HEIGHT_OFFSET + (posZ + renderDistance) * CullingHandler.LEVEL_HEIGHT_OFFSET + posY) << 2)); - - if (index > 0 && index < cullingBuffer.limit()) { - return (cullingBuffer.get(index) & 0xFF) > 0; - } - - return false; - } -} diff --git a/src/main/java/rogo/renderingculling/api/Config.java b/src/main/java/rogo/renderingculling/api/Config.java index ba55f31..a66061e 100644 --- a/src/main/java/rogo/renderingculling/api/Config.java +++ b/src/main/java/rogo/renderingculling/api/Config.java @@ -17,6 +17,12 @@ public class Config { private static final PropertyMirror SAMPLING = PropertyMirror.create(ConfigTypes.DOUBLE); + private static final PropertyMirror CULL_ENTITY = PropertyMirror.create(ConfigTypes.BOOLEAN); + private static final PropertyMirror CULL_CHUNK = PropertyMirror.create(ConfigTypes.BOOLEAN); + private static final PropertyMirror ASYNC = PropertyMirror.create(ConfigTypes.BOOLEAN); + private static final PropertyMirror UPDATE_DELAY = PropertyMirror.create(ConfigTypes.INTEGER); + private static final PropertyMirror> ENTITY_SKIP = PropertyMirror.create(ConfigTypes.makeList(ConfigTypes.STRING)); + private static final PropertyMirror> BLOCK_ENTITY_SKIP = PropertyMirror.create(ConfigTypes.makeList(ConfigTypes.STRING)); public static double getSampling() { if(unload()) @@ -30,8 +36,6 @@ public static void setSampling(double value) { save(); } - private static final PropertyMirror CULL_ENTITY = PropertyMirror.create(ConfigTypes.BOOLEAN); - public static boolean getCullEntity() { if(unload() || !CullingHandler.gl33()) return false; @@ -43,56 +47,66 @@ public static void setCullEntity(boolean value) { save(); } - private static final PropertyMirror CULL_CHUNK = PropertyMirror.create(ConfigTypes.BOOLEAN); - public static boolean getCullChunk() { if(unload()) return false; return CULL_CHUNK.getValue(); } + public static boolean shouldCullChunk() { + if (unload()) + return false; + + if (CullingHandler.CHUNK_CULLING_MAP == null || !CullingHandler.CHUNK_CULLING_MAP.isDone()) + return false; + + return getCullChunk(); + } + public static void setCullChunk(boolean value) { CULL_CHUNK.setValue(value); save(); } - private static final PropertyMirror UPDATE_DELAY = PropertyMirror.create(ConfigTypes.INTEGER); + public static boolean getAsyncChunkRebuild() { + if (unload()) + return false; - public static int getDepthUpdateDelay() { - if(unload()) - return 1; - int dynamicWithShader = CullingHandler.INSTANCE.renderingShader() ? 1 : 0; - return UPDATE_DELAY.getValue() + dynamicWithShader; + if(!shouldCullChunk()) + return false; + + return ASYNC.getValue(); } - public static void setDepthUpdateDelay(int value) { - UPDATE_DELAY.setValue(value); + public static void setAsyncChunkRebuild(boolean value) { + if(!shouldCullChunk()) + return; + + ASYNC.setValue(value); save(); } - private static final PropertyMirror CULLING_ENTITY_RATE = PropertyMirror.create(ConfigTypes.INTEGER); + public static int getShaderDynamicDelay() { + return CullingHandler.enabledShader() ? 1 : 0; + } - public static int getCullingEntityRate() { + public static int getDepthUpdateDelay() { if(unload()) - return 20; - return CULLING_ENTITY_RATE.getValue(); + return 1; + return UPDATE_DELAY.getValue() <= 9 ? UPDATE_DELAY.getValue() + getShaderDynamicDelay() : UPDATE_DELAY.getValue(); } - public static void setCullingEntityRate(int value) { - CULLING_ENTITY_RATE.setValue(value); + public static void setDepthUpdateDelay(int value) { + UPDATE_DELAY.setValue(value); save(); } - private static final PropertyMirror> ENTITY_SKIP = PropertyMirror.create(ConfigTypes.makeList(ConfigTypes.STRING)); - public static List getEntitiesSkip() { if(unload()) return ImmutableList.of(); return ENTITY_SKIP.getValue(); } - private static final PropertyMirror> BLOCK_ENTITY_SKIP = PropertyMirror.create(ConfigTypes.makeList(ConfigTypes.STRING)); - public static List getBlockEntitiesSkip() { if(unload()) return ImmutableList.of(); @@ -140,8 +154,8 @@ private static void init() { .beginValue(getTranslatedItem("brute_force_rendering_culling.cull_chunk"), ConfigTypes.BOOLEAN, true) .finishValue(CULL_CHUNK::mirror) - .beginValue(getTranslatedItem("brute_force_rendering_culling.culling_entity_update_rate"), ConfigTypes.INTEGER, 20) - .finishValue(CULLING_ENTITY_RATE::mirror) + .beginValue(getTranslatedItem("brute_force_rendering_culling.async"), ConfigTypes.BOOLEAN, true) + .finishValue(ASYNC::mirror) .beginValue(getTranslatedItem("brute_force_rendering_culling.skip_culling_entities"), ConfigTypes.makeList(ConfigTypes.STRING), entityList) .finishValue(ENTITY_SKIP::mirror) diff --git a/src/main/java/rogo/renderingculling/api/CullingHandler.java b/src/main/java/rogo/renderingculling/api/CullingHandler.java index 3d84cfe..fad4592 100644 --- a/src/main/java/rogo/renderingculling/api/CullingHandler.java +++ b/src/main/java/rogo/renderingculling/api/CullingHandler.java @@ -9,9 +9,6 @@ import com.mojang.blaze3d.vertex.*; import com.mojang.logging.LogUtils; import com.mojang.math.Axis; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.Camera; @@ -33,7 +30,11 @@ import org.lwjgl.opengl.GL; import org.lwjgl.system.Checks; import org.slf4j.Logger; -import rogo.renderingculling.event.WorldUnloadEvent; +import rogo.renderingculling.api.data.ChunkCullingMap; +import rogo.renderingculling.api.data.EntityCullingMap; +import rogo.renderingculling.api.impl.IEntitiesForRender; +import rogo.renderingculling.api.impl.IRenderChunkInfo; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; import rogo.renderingculling.gui.ConfigScreen; import rogo.renderingculling.mixin.AccessorLevelRender; import rogo.renderingculling.mixin.AccessorMinecraft; @@ -41,157 +42,103 @@ import rogo.renderingculling.util.LifeTimer; import rogo.renderingculling.util.ShaderLoader; -import java.io.IOException; import java.lang.reflect.Field; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.function.Consumer; -import java.util.function.Supplier; -import static java.lang.Thread.MAX_PRIORITY; import static org.lwjgl.opengl.GL11.GL_TEXTURE; import static org.lwjgl.opengl.GL30.*; -public class CullingHandler implements ModInitializer { - public static CullingHandler INSTANCE; +public class CullingHandler { + public static boolean SHADER_ENABLED = false; public static final String MOD_ID = "brute_force_rendering_culling"; public static final Logger LOGGER = LogUtils.getLogger(); public static EntityCullingMap ENTITY_CULLING_MAP = null; - public static ChunkCullingMap CHUNK_CULLING_MAP = null; + public static volatile ChunkCullingMap CHUNK_CULLING_MAP = null; public static Matrix4f VIEW_MATRIX = new Matrix4f(); public static Matrix4f PROJECTION_MATRIX = new Matrix4f(); + public static double FOV = 90; static { PROJECTION_MATRIX.identity(); } - public static final int depthSize = 4; + public static final int DEPTH_SIZE = 4; public static int DEPTH_INDEX; public static int MAIN_DEPTH_TEXTURE = 0; - public static RenderTarget[] DEPTH_BUFFER_TARGET = new RenderTarget[depthSize]; - public static RenderTarget MONOCHROME_DEPTH_TARGET; + public static RenderTarget[] DEPTH_BUFFER_TARGET = new RenderTarget[DEPTH_SIZE]; public static RenderTarget CHUNK_CULLING_MAP_TARGET; public static RenderTarget ENTITY_CULLING_MAP_TARGET; public static ShaderInstance CHUNK_CULLING_SHADER; - public static ShaderInstance LINEARIZE_DEPTH_SHADER; public static ShaderInstance COPY_DEPTH_SHADER; + public static ShaderInstance REMOVE_COLOR_SHADER; public static ShaderInstance INSTANCED_ENTITY_CULLING_SHADER; public static Frustum FRUSTUM; public static boolean updatingDepth; public static boolean applyFrustum; - public int DEBUG = 0; - public static int[] DEPTH_TEXTURE = new int[depthSize]; + public static int DEBUG = 0; + public static int[] DEPTH_TEXTURE = new int[DEPTH_SIZE]; public static ShaderLoader SHADER_LOADER = null; public static Class OptiFine = null; - public LifeTimer visibleEntity = new LifeTimer<>(); - public LifeTimer visibleBlock = new LifeTimer<>(); - public HashSet culledEntity = new HashSet<>(); - public HashSet culledBlock = new HashSet<>(); - private boolean[] nextTick = new boolean[20]; - public int fps = 0; - private int tick = 0; - public int clientTickCount = 0; - public int entityCulling = 0; - public int entityCount = 0; - public int blockCulling = 0; - public int blockCount = 0; - public long entityCullingTime = 0; - public long blockCullingTime = 0; - public long chunkCullingTime = 0; - private long preEntityCullingTime = 0; - private long preBlockCullingTime = 0; - private long preChunkCullingTime = 0; - public long preApplyFrustumTime = 0; - public long applyFrustumTime = 0; - public int chunkCulling = 0; - public int chunkCount = 0; - public long chunkCullingInitTime = 0; - public long preChunkCullingInitTime = 0; - public long entityCullingInitTime = 0; - public long preEntityCullingInitTime = 0; - public int cullingInitCount = 0; - public int preCullingInitCount = 0; - public boolean checkCulling = false; - public boolean checkTexture = false; - private boolean usingShader = false; - private String shaderName = ""; - protected static int LEVEL_HEIGHT_OFFSET; - protected static int LEVEL_MIN_SECTION_ABS; + public static final LifeTimer visibleEntity = new LifeTimer<>(); + public static final LifeTimer visibleBlock = new LifeTimer<>(); + private static boolean[] nextTick = new boolean[20]; + public static int fps = 0; + private static int tick = 0; + public static int clientTickCount = 0; + public static int entityCulling = 0; + public static int entityCount = 0; + public static int blockCulling = 0; + public static int blockCount = 0; + public static long entityCullingTime = 0; + public static long blockCullingTime = 0; + public static long chunkCullingTime = 0; + private static long preEntityCullingTime = 0; + private static long preBlockCullingTime = 0; + private static long preChunkCullingTime = 0; + public static long preApplyFrustumTime = 0; + public static long applyFrustumTime = 0; + public static int chunkCulling = 0; + public static int chunkCount = 0; + public static long chunkCullingInitTime = 0; + public static long preChunkCullingInitTime = 0; + public static long entityCullingInitTime = 0; + public static long preEntityCullingInitTime = 0; + public static int cullingInitCount = 0; + public static int preCullingInitCount = 0; + public static boolean checkCulling = false; + public static boolean checkTexture = false; + private static boolean usingShader = false; + private static int fullChunkUpdateCooldown = 0; + private static String shaderName = ""; + public static int LEVEL_SECTION_RANGE; + public static int LEVEL_POS_RANGE; + public static int LEVEL_MIN_SECTION_ABS; + public static int LEVEL_MIN_POS; public static Camera CAMERA; private static final HashMap SHADER_DEPTH_BUFFER_ID = new HashMap<>(); - public static boolean SHADER_ENABLED = false; - private int frame; + private static int frame; + private static int lastVisibleUpdatedFrame; + public static volatile boolean useOcclusionCulling = true; + public static boolean reColorToolTip = false; static { RenderSystem.recordRenderCall(() -> { - for(int i = 0; i < DEPTH_BUFFER_TARGET.length; ++i) { + for (int i = 0; i < DEPTH_BUFFER_TARGET.length; ++i) { DEPTH_BUFFER_TARGET[i] = new TextureTarget(Minecraft.getInstance().getWindow().getWidth(), Minecraft.getInstance().getWindow().getHeight(), false, Minecraft.ON_OSX); DEPTH_BUFFER_TARGET[i].setClearColor(0.0F, 0.0F, 0.0F, 0.0F); } + CHUNK_CULLING_MAP_TARGET = new TextureTarget(Minecraft.getInstance().getWindow().getWidth(), Minecraft.getInstance().getWindow().getHeight(), false, Minecraft.ON_OSX); CHUNK_CULLING_MAP_TARGET.setClearColor(0.0F, 0.0F, 0.0F, 0.0F); - MONOCHROME_DEPTH_TARGET = new TextureTarget(Minecraft.getInstance().getWindow().getWidth(), Minecraft.getInstance().getWindow().getHeight(), false, Minecraft.ON_OSX); - MONOCHROME_DEPTH_TARGET.setClearColor(0.0F, 0.0F, 0.0F, 0.0F); ENTITY_CULLING_MAP_TARGET = new TextureTarget(Minecraft.getInstance().getWindow().getWidth(), Minecraft.getInstance().getWindow().getHeight(), false, Minecraft.ON_OSX); ENTITY_CULLING_MAP_TARGET.setClearColor(0.0F, 0.0F, 0.0F, 0.0F); }); } - @Override - public void onInitialize() { - INSTANCE = this; - callWhenOn(EnvType.CLIENT, () -> () -> { - registerEvents(); - this.registerShader(); - try { - OptiFine = Class.forName("net.optifine.shaders.Shaders"); - } catch (ClassNotFoundException e) { - LOGGER.debug("OptiFine Not Found"); - } - - if (OptiFine != null) { - try { - SHADER_LOADER = Class.forName("rogo.renderingculling.util.OptiFineLoaderImpl").asSubclass(ShaderLoader.class).newInstance(); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { - } - } - - if (FabricLoader.getInstance().getAllMods().stream().anyMatch(modInfo -> modInfo.getMetadata().getId().equals("oculus"))) { - try { - SHADER_LOADER = Class.forName("rogo.renderingculling.util.IrisLoaderImpl").asSubclass(ShaderLoader.class).newInstance(); - } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - }); - } - - public static void callWhenOn(EnvType envType, Supplier supplier) { - if(envType == FabricLoader.getInstance().getEnvironmentType()) { - supplier.get().run(); - } - } - - private void registerShader() { - RenderSystem.recordRenderCall(this::initShader); - } - - private void initShader() { - LOGGER.debug("try init shader chunk_culling"); - try { - SHADER_ENABLED = true; - CHUNK_CULLING_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("chunk_culling"), DefaultVertexFormat.POSITION); - INSTANCED_ENTITY_CULLING_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("instanced_entity_culling"), DefaultVertexFormat.POSITION); - COPY_DEPTH_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("copy_depth"), DefaultVertexFormat.POSITION); - SHADER_ENABLED = false; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - public static String fromID(String s) { return MOD_ID + ":" + s; } @@ -208,44 +155,37 @@ public static String fromID(String s) { GLFW.GLFW_KEY_X, "key.category." + MOD_ID)); - private void registerEvents() { - WorldUnloadEvent.WORLD_UNLOAD.register(this::onWorldUnload); - ClientTickEvents.START_CLIENT_TICK.register(this::onStartClientTick); - } + public static void init() { + try { + OptiFine = Class.forName("net.optifine.shaders.Shaders"); + } catch (ClassNotFoundException e) { + LOGGER.debug("OptiFine Not Found"); + } - private void onWorldUnload(Level world) { - if(world == Minecraft.getInstance().level) { - cleanup(); + if (OptiFine != null) { + try { + SHADER_LOADER = Class.forName("rogo.renderingculling.util.OptiFineLoaderImpl").asSubclass(ShaderLoader.class).newInstance(); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ignored) { + } } - } - private void onStartClientTick(Minecraft client) { - if (client.player != null && client.level != null) { - Config.loadConfig(); - clientTickCount++; - if (clientTickCount > 200 && CHUNK_CULLING_MAP != null && !CHUNK_CULLING_MAP.isDone()) { - CHUNK_CULLING_MAP.setDone(); - LEVEL_HEIGHT_OFFSET = client.level.getMaxSection() - client.level.getMinSection(); - LEVEL_MIN_SECTION_ABS = Math.abs(client.level.getMinSection()); + if (hasIris()) { + try { + SHADER_LOADER = Class.forName("rogo.renderingculling.util.IrisLoaderImpl").asSubclass(ShaderLoader.class).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + throw new RuntimeException(e); } - } else { - cleanup(); } } - private void onKeyPress() { - if (CONFIG_KEY.isDown()) { - Minecraft.getInstance().setScreen(new ConfigScreen(Component.translatable(MOD_ID + ".config"))); - } - if (DEBUG_KEY.isDown()) { - DEBUG++; - if(DEBUG >= 3) - DEBUG = 0; + public static void onWorldUnload(Level world) { + if (world != Minecraft.getInstance().level) { + cleanup(); } } - private void cleanup() { - this.tick = 0; + protected static void cleanup() { + tick = 0; clientTickCount = 0; visibleEntity.clear(); visibleBlock.clear(); @@ -260,43 +200,54 @@ private void cleanup() { SHADER_DEPTH_BUFFER_ID.clear(); } - public boolean shouldRenderChunk(IRenderSectionVisibility section, boolean count) { - if(count) - chunkCount++; - if (!Config.getCullChunk() || CHUNK_CULLING_MAP == null || !CHUNK_CULLING_MAP.isDone()) { - return true; + public static boolean shouldRenderChunk(IRenderSectionVisibility section, boolean count) { + if (section == null) { + return false; + } + + if (Config.getAsyncChunkRebuild()) { + if (!useOcclusionCulling) { + return true; + } + + count = false; } long time = System.nanoTime(); + + if (count) + chunkCount++; + boolean render; boolean actualRender = false; - if (!section.shouldCheckVisibility(frame)) { + if (!section.shouldCheckVisibility(lastVisibleUpdatedFrame)) { render = true; } else { actualRender = CHUNK_CULLING_MAP.isChunkOffsetCameraVisible(section.getPositionX(), section.getPositionY(), section.getPositionZ()); render = actualRender; } - if (checkCulling) render = !render; if (!render && count) { chunkCulling++; - } else if(actualRender) { - section.updateVisibleTick(frame); + } else if (actualRender) { + section.updateVisibleTick(lastVisibleUpdatedFrame); } - if(count) + + if (count) preChunkCullingTime += System.nanoTime() - time; + return render; } - public boolean shouldSkipBlock(BlockEntity blockEntity, AABB aabb, BlockPos pos) { + public static boolean shouldSkipBlockEntity(BlockEntity blockEntity, AABB aabb, BlockPos pos) { blockCount++; //for valkyrien skies - if(CAMERA.getPosition().distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) > + if (CAMERA.getPosition().distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) > Minecraft.getInstance().options.getEffectiveRenderDistance() * Minecraft.getInstance().options.getEffectiveRenderDistance() * 2) { return false; } @@ -325,15 +276,14 @@ public boolean shouldSkipBlock(BlockEntity blockEntity, AABB aabb, BlockPos pos) visible = !visible; if (!visible) { - culledBlock.add(pos); blockCulling++; - } else if(actualVisible) + } else if (actualVisible) visibleBlock.updateUsageTick(pos, clientTickCount); return !visible; } - public boolean shouldSkipEntity(Entity entity) { + public static boolean shouldSkipEntity(Entity entity) { entityCount++; if (entity instanceof Player || entity.isCurrentlyGlowing()) return false; if (entity.distanceToSqr(CAMERA.getPosition()) < 4) return false; @@ -359,58 +309,79 @@ public boolean shouldSkipEntity(Entity entity) { visible = !visible; if (!visible) { - culledEntity.add(entity); entityCulling++; - } else if(actualVisible) + } else if (actualVisible) visibleEntity.updateUsageTick(entity, clientTickCount); return !visible; } - public void onProfilerPopPush(String s) { - if (s.equals("afterRunTick")) { - afterGameRender(); - } else if (s.equals("captureFrustum")) { - AccessorLevelRender levelFrustum = (AccessorLevelRender) Minecraft.getInstance().levelRenderer; - Frustum frustum; - if (levelFrustum.getCapturedFrustum() != null) { - frustum = levelFrustum.getCapturedFrustum(); - } else { - frustum = levelFrustum.getCullingFrustum(); + public static void onProfilerPopPush(String s) { + switch (s) { + case "beforeRunTick" -> { + if (((AccessorLevelRender) Minecraft.getInstance().levelRenderer).getNeedsFullRenderChunkUpdate() && Minecraft.getInstance().level != null) { + fullChunkUpdateCooldown = 20; + + LEVEL_SECTION_RANGE = Minecraft.getInstance().level.getMaxSection() - Minecraft.getInstance().level.getMinSection(); + LEVEL_MIN_SECTION_ABS = Math.abs(Minecraft.getInstance().level.getMinSection()); + LEVEL_MIN_POS = Minecraft.getInstance().level.getMinBuildHeight(); + LEVEL_POS_RANGE = Minecraft.getInstance().level.getMaxBuildHeight() - Minecraft.getInstance().level.getMinBuildHeight(); + } + } + case "afterRunTick" -> { + ++frame; + updateMapData(); + } + case "captureFrustum" -> { + AccessorLevelRender levelFrustum = (AccessorLevelRender) Minecraft.getInstance().levelRenderer; + Frustum frustum; + if (levelFrustum.getCapturedFrustum() != null) { + frustum = levelFrustum.getCapturedFrustum(); + } else { + frustum = levelFrustum.getCullingFrustum(); + } + CullingHandler.FRUSTUM = new Frustum(frustum).offsetToFullyIncludeCameraCube(32); + if (CullingHandler.CHUNK_CULLING_MAP != null) { + CullingHandler.CHUNK_CULLING_MAP.updateCamera(); + } + checkShader(); + } + case "terrain_setup" -> { + applyFrustum = true; + } + case "compilechunks" -> { + applyFrustum = false; + } + case "destroyProgress" -> { + updatingDepth = true; + updateDepthMap(); + readMapData(); + CullingRenderEvent.updateCullingMap(); + updatingDepth = false; + } + case "chunk_render_lists" -> { + chunkCount = 0; + chunkCulling = 0; } - CullingHandler.FRUSTUM = new Frustum(frustum).offsetToFullyIncludeCameraCube(32); - this.beforeRenderingWorld(); - } else if (s.equals("terrain_setup")) { - applyFrustum = true; - } else if (s.equals("compilechunks")) { - applyFrustum = false; - } else if (s.equals("destroyProgress")) { - updatingDepth = true; - this.afterRenderingWorld(); - CullingRenderEvent.onUpdateCullingMap(); - updatingDepth = false; - } else if (s.equals("chunk_render_lists")) { - chunkCount = 0; - chunkCulling = 0; } } - public void onProfilerPush(String s) { + public static void onProfilerPush(String s) { if(s.equals("onKeyboardInput")) { - onKeyPress(); - } else if (Config.getCullChunk() && s.equals("apply_frustum")) { + ModLoader.onKeyPress(); + } if (Config.shouldCullChunk() && s.equals("apply_frustum")) { if (SHADER_LOADER == null || OptiFine != null) { chunkCount = 0; chunkCulling = 0; } } else if (s.equals("center")) { CAMERA = Minecraft.getInstance().gameRenderer.getMainCamera(); - int tick = clientTickCount % 20; + int thisTick = clientTickCount % 20; nextTick = new boolean[20]; - if (this.tick != tick) { - this.tick = tick; - nextTick[tick] = true; + if (tick != thisTick) { + tick = thisTick; + nextTick[thisTick] = true; } entityCulling = 0; @@ -418,52 +389,81 @@ public void onProfilerPush(String s) { blockCulling = 0; blockCount = 0; + if (anyNextTick() && fullChunkUpdateCooldown > 0) { + fullChunkUpdateCooldown--; + } + if (isNextLoop()) { visibleBlock.tick(clientTickCount, 1); visibleEntity.tick(clientTickCount, 1); - if (tick == 0) { - applyFrustumTime = preApplyFrustumTime; - preApplyFrustumTime = 0; - entityCullingTime = preEntityCullingTime; - preEntityCullingTime = 0; + applyFrustumTime = preApplyFrustumTime; + preApplyFrustumTime = 0; - blockCullingTime = preBlockCullingTime; - preBlockCullingTime = 0; + entityCullingTime = preEntityCullingTime; + preEntityCullingTime = 0; - chunkCullingInitTime = preChunkCullingInitTime; - preChunkCullingInitTime = 0; + blockCullingTime = preBlockCullingTime; + preBlockCullingTime = 0; - cullingInitCount = preCullingInitCount; - preCullingInitCount = 0; + chunkCullingInitTime = preChunkCullingInitTime; + preChunkCullingInitTime = 0; - entityCullingInitTime = preEntityCullingInitTime; - preEntityCullingInitTime = 0; + cullingInitCount = preCullingInitCount; + preCullingInitCount = 0; - if (preChunkCullingTime != 0) { - chunkCullingTime = preChunkCullingTime; - preChunkCullingTime = 0; - } + entityCullingInitTime = preEntityCullingInitTime; + preEntityCullingInitTime = 0; + + if (CullingHandler.CHUNK_CULLING_MAP != null) { + CullingHandler.CHUNK_CULLING_MAP.lastQueueUpdateCount = CullingHandler.CHUNK_CULLING_MAP.queueUpdateCount; + CullingHandler.CHUNK_CULLING_MAP.queueUpdateCount = 0; } + + if (preChunkCullingTime != 0) { + chunkCullingTime = preChunkCullingTime; + preChunkCullingTime = 0; + } + } + } + } + + public static void readMapData() { + if (!checkCulling) { + if (Config.getCullChunk()) { + long time = System.nanoTime(); + if (CHUNK_CULLING_MAP != null && CHUNK_CULLING_MAP.isTransferred()) { + CHUNK_CULLING_MAP.readData(); + lastVisibleUpdatedFrame = frame; + } + preChunkCullingInitTime += System.nanoTime() - time; + } + + if (Config.getCullEntity()) { + long time = System.nanoTime(); + if (ENTITY_CULLING_MAP != null && ENTITY_CULLING_MAP.isTransferred()) { + ENTITY_CULLING_MAP.readData(); + lastVisibleUpdatedFrame = frame; + } + preEntityCullingInitTime += System.nanoTime() - time; } } } - public void beforeRenderingWorld() { - ++frame; - if(SHADER_LOADER != null) { + public static void checkShader() { + if (SHADER_LOADER != null) { boolean clear = false; - if(SHADER_LOADER.renderingShader() && !usingShader) { + if (SHADER_LOADER.enabledShader() && !usingShader) { clear = true; usingShader = true; } - if(!SHADER_LOADER.renderingShader() && usingShader) { + if (!SHADER_LOADER.enabledShader() && usingShader) { clear = true; usingShader = false; } - if(SHADER_LOADER.renderingShader() && OptiFine != null) { + if (SHADER_LOADER.enabledShader() && OptiFine != null) { String shaderPack = ""; try { Field field = CullingHandler.OptiFine.getDeclaredField("currentShaderName"); @@ -472,38 +472,20 @@ public void beforeRenderingWorld() { } catch (NoSuchFieldException | IllegalAccessException e) { e.fillInStackTrace(); } - if(!Objects.equals(shaderName, shaderPack)) { + if (!Objects.equals(shaderName, shaderPack)) { shaderName = shaderPack; clear = true; } } - if(clear) + if (clear) { cleanup(); - } - - if (anyCulling()) { - if (!checkCulling) { - if(Config.getCullChunk()) { - long time = System.nanoTime(); - if (CHUNK_CULLING_MAP != null && CHUNK_CULLING_MAP.isTransferred()) { - CHUNK_CULLING_MAP.readData(); - } - preChunkCullingInitTime += System.nanoTime() - time; - } - - if(Config.getCullEntity()) { - long time = System.nanoTime(); - if (ENTITY_CULLING_MAP != null && ENTITY_CULLING_MAP.isTransferred()) { - ENTITY_CULLING_MAP.readData(); - } - preEntityCullingInitTime += System.nanoTime() - time; - } } } } - public void afterRenderingWorld() { + public static void updateDepthMap() { + CullingHandler.PROJECTION_MATRIX = new Matrix4f(RenderSystem.getProjectionMatrix()); if (anyCulling() && !checkCulling && anyNeedTransfer()) { float sampling = (float) (double) Config.getSampling(); Window window = Minecraft.getInstance().getWindow(); @@ -519,8 +501,8 @@ public void afterRenderingWorld() { }); int depthTexture = Minecraft.getInstance().getMainRenderTarget().getDepthTextureId(); - if (SHADER_LOADER != null && SHADER_LOADER.renderingShader()) { - if(!SHADER_DEPTH_BUFFER_ID.containsKey(SHADER_LOADER.getFrameBufferID())) { + if (SHADER_LOADER != null && SHADER_LOADER.enabledShader()) { + if (!SHADER_DEPTH_BUFFER_ID.containsKey(SHADER_LOADER.getFrameBufferID())) { RenderSystem.assertOnRenderThreadOrInit(); GlStateManager._glBindFramebuffer(GL_FRAMEBUFFER, SHADER_LOADER.getFrameBufferID()); @@ -550,29 +532,30 @@ public void afterRenderingWorld() { bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION); bufferbuilder.vertex(-1.0f, -1.0f, 0.0f).endVertex(); bufferbuilder.vertex(1.0f, -1.0f, 0.0f).endVertex(); - bufferbuilder.vertex(1.0f, 1.0f, 0.0f).endVertex(); - bufferbuilder.vertex(-1.0f, 1.0f, 0.0f).endVertex(); + bufferbuilder.vertex(1.0f, 1.0f, 0.0f).endVertex(); + bufferbuilder.vertex(-1.0f, 1.0f, 0.0f).endVertex(); RenderSystem.setShaderTexture(0, depthContext.lastTexture()); tesselator.end(); DEPTH_TEXTURE[depthContext.index()] = depthContext.frame().getColorTextureId(); }); + bindMainFrameTarget(); PoseStack viewMatrix = new PoseStack(); + Vec3 cameraPos = CAMERA.getPosition(); viewMatrix.mulPose(Axis.XP.rotationDegrees(CAMERA.getXRot())); viewMatrix.mulPose(Axis.YP.rotationDegrees(CAMERA.getYRot() + 180.0F)); - Vec3 cameraPos = CAMERA.getPosition(); viewMatrix.translate((float) -cameraPos.x, (float) -cameraPos.y, (float) -cameraPos.z); VIEW_MATRIX = new Matrix4f(viewMatrix.last().pose()); } } - public void afterGameRender() { + public static void updateMapData() { if (anyCulling()) { preCullingInitCount++; - if(Config.getCullChunk()) { + if (Config.getCullChunk()) { int renderingDiameter = Minecraft.getInstance().options.getEffectiveRenderDistance() * 2 + 1; - int maxSize = renderingDiameter * LEVEL_HEIGHT_OFFSET * renderingDiameter; + int maxSize = renderingDiameter * LEVEL_SECTION_RANGE * renderingDiameter; int cullingSize = (int) Math.sqrt(maxSize) + 1; if (CHUNK_CULLING_MAP_TARGET.width != cullingSize || CHUNK_CULLING_MAP_TARGET.height != cullingSize) { @@ -586,6 +569,9 @@ public void afterGameRender() { if (CHUNK_CULLING_MAP == null) { CHUNK_CULLING_MAP = new ChunkCullingMap(CHUNK_CULLING_MAP_TARGET.width, CHUNK_CULLING_MAP_TARGET.height); + if (ENTITY_CULLING_MAP != null) { + ENTITY_CULLING_MAP.syncDelay(CHUNK_CULLING_MAP); + } CHUNK_CULLING_MAP.generateIndex(Minecraft.getInstance().options.getEffectiveRenderDistance()); } @@ -594,15 +580,18 @@ public void afterGameRender() { preChunkCullingInitTime += System.nanoTime() - time; } - if(Config.getCullEntity()) { + if (Config.getCullEntity()) { if (ENTITY_CULLING_MAP == null) { ENTITY_CULLING_MAP = new EntityCullingMap(ENTITY_CULLING_MAP_TARGET.width, ENTITY_CULLING_MAP_TARGET.height); + if (CHUNK_CULLING_MAP != null) { + CHUNK_CULLING_MAP.syncDelay(ENTITY_CULLING_MAP); + } } - int tableCapacity = CullingHandler.ENTITY_CULLING_MAP.getEntityTable().size()/64; - tableCapacity = tableCapacity*64+64; - int cullingSize = (int) Math.sqrt(tableCapacity)+1; - if(CullingHandler.ENTITY_CULLING_MAP_TARGET.width != cullingSize || CullingHandler.ENTITY_CULLING_MAP_TARGET.height != cullingSize) { + int tableCapacity = CullingHandler.ENTITY_CULLING_MAP.getEntityTable().size() / 64; + tableCapacity = tableCapacity * 64 + 64; + int cullingSize = (int) Math.sqrt(tableCapacity) + 1; + if (CullingHandler.ENTITY_CULLING_MAP_TARGET.width != cullingSize || CullingHandler.ENTITY_CULLING_MAP_TARGET.height != cullingSize) { CullingHandler.ENTITY_CULLING_MAP_TARGET.resize(cullingSize, cullingSize, Minecraft.ON_OSX); if (ENTITY_CULLING_MAP != null) { EntityCullingMap temp = ENTITY_CULLING_MAP; @@ -616,12 +605,12 @@ public void afterGameRender() { ENTITY_CULLING_MAP.transferData(); preEntityCullingInitTime += System.nanoTime() - time; - if(Minecraft.getInstance().level != null) { + if (Minecraft.getInstance().level != null) { CullingHandler.ENTITY_CULLING_MAP.getEntityTable().tick(clientTickCount); Iterable entities = Minecraft.getInstance().level.entitiesForRendering(); entities.forEach(entity -> CullingHandler.ENTITY_CULLING_MAP.getEntityTable().addObject(entity)); - for(Object levelrenderer$renderchunkinfo : ((IEntitiesForRender)Minecraft.getInstance().levelRenderer).renderChunksInFrustum()) { - List list = ((IRenderChunkInfo)levelrenderer$renderchunkinfo).getRenderChunk().getCompiledChunk().getRenderableBlockEntities(); + for (Object levelrenderer$renderchunkinfo : ((IEntitiesForRender) Minecraft.getInstance().levelRenderer).renderChunksInFrustum()) { + List list = ((IRenderChunkInfo) levelrenderer$renderchunkinfo).getRenderChunk().getCompiledChunk().getRenderableBlockEntities(); list.forEach(entity -> CullingHandler.ENTITY_CULLING_MAP.getEntityTable().addObject(entity)); } @@ -643,11 +632,11 @@ public void afterGameRender() { } public static void useShader(ShaderInstance instance) { - RenderSystem.setShader(()-> instance); + RenderSystem.setShader(() -> instance); } public static void bindMainFrameTarget() { - if(SHADER_LOADER != null && SHADER_LOADER.renderingShader()) { + if (SHADER_LOADER != null && SHADER_LOADER.renderingShaderPass()) { SHADER_LOADER.bindDefaultFrameBuffer(); } else { Minecraft.getInstance().getMainRenderTarget().bindWrite(true); @@ -657,7 +646,7 @@ public static void bindMainFrameTarget() { public static void runOnDepthFrame(Consumer consumer) { float f = 1.0f; for (DEPTH_INDEX = 0; DEPTH_INDEX < DEPTH_BUFFER_TARGET.length; ++DEPTH_INDEX) { - int lastTexture = DEPTH_INDEX == 0 ? MAIN_DEPTH_TEXTURE : DEPTH_BUFFER_TARGET[DEPTH_INDEX-1].getColorTextureId(); + int lastTexture = DEPTH_INDEX == 0 ? MAIN_DEPTH_TEXTURE : DEPTH_BUFFER_TARGET[DEPTH_INDEX - 1].getColorTextureId(); consumer.accept(new DepthContext(DEPTH_BUFFER_TARGET[DEPTH_INDEX], DEPTH_INDEX, f, lastTexture)); f *= 0.3f; } @@ -669,19 +658,19 @@ public static void callDepthTexture() { }); } - public boolean renderingOculus() { + public static boolean renderingIris() { return renderingShader() && OptiFine == null; } - public boolean renderingShader() { - return SHADER_LOADER != null && SHADER_LOADER.renderingShader(); + public static boolean renderingShader() { + return SHADER_LOADER != null && SHADER_LOADER.renderingShaderPass(); } - public boolean isNextTick(int tick) { - return nextTick[this.tick]; + public static boolean enabledShader() { + return SHADER_LOADER != null && SHADER_LOADER.enabledShader(); } - public boolean anyNextTick() { + public static boolean anyNextTick() { for (int i = 0; i < 20; ++i) { if (nextTick[i]) return true; @@ -689,12 +678,12 @@ public boolean anyNextTick() { return false; } - public boolean isNextLoop() { + public static boolean isNextLoop() { return nextTick[0]; } public static boolean anyCulling() { - return Config.getCullEntity() || Config.getCullChunk(); + return Config.getCullChunk() || Config.getCullEntity(); } public static boolean anyNeedTransfer() { @@ -703,11 +692,36 @@ public static boolean anyNeedTransfer() { } private static int gl33 = -1; + public static boolean gl33() { - if(RenderSystem.isOnRenderThread()) { - if(gl33 < 0) + if (RenderSystem.isOnRenderThread()) { + if (gl33 < 0) gl33 = (GL.getCapabilities().OpenGL33 || Checks.checkFunctions(GL.getCapabilities().glVertexAttribDivisor)) ? 1 : 0; } return gl33 == 1; } + + public static boolean hasMod(String s) { + return FabricLoader.getInstance().getAllMods().stream().anyMatch(modInfo -> modInfo.getMetadata().getId().equals(s)); + } + + public static boolean hasSodium() { + return FabricLoader.getInstance().getAllMods().stream().anyMatch(modInfo -> modInfo.getMetadata().getId().equals("sodium") || modInfo.getMetadata().getId().equals("embeddium") || modInfo.getMetadata().getId().equals("rubidium")); + } + + public static boolean hasIris() { + return FabricLoader.getInstance().getAllMods().stream().anyMatch(modInfo -> modInfo.getMetadata().getId().equals("iris") || modInfo.getMetadata().getId().equals("oculus")); + } + + public static boolean needPauseRebuild() { + //fullChunkUpdateCooldown > 0 + return false; + } + + public static int mapChunkY(double posY) { + double offset = posY - LEVEL_MIN_POS; + double mappingRatio = offset / LEVEL_POS_RANGE; + + return (int) Math.floor(mappingRatio * LEVEL_SECTION_RANGE); + } } diff --git a/src/main/java/rogo/renderingculling/api/CullingRenderEvent.java b/src/main/java/rogo/renderingculling/api/CullingRenderEvent.java index af9a03d..6b4e92f 100644 --- a/src/main/java/rogo/renderingculling/api/CullingRenderEvent.java +++ b/src/main/java/rogo/renderingculling/api/CullingRenderEvent.java @@ -11,17 +11,22 @@ import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.network.chat.Component; import net.minecraft.world.phys.Vec3; +import org.joml.Vector3f; import org.joml.Vector4f; +import rogo.renderingculling.api.impl.ICullingShader; import rogo.renderingculling.instanced.EntityCullingInstanceRenderer; import rogo.renderingculling.mixin.AccessorFrustum; import java.util.ArrayList; import java.util.List; +import static rogo.renderingculling.gui.ConfigScreen.u; +import static rogo.renderingculling.gui.ConfigScreen.v; + public class CullingRenderEvent { public static CullingRenderEvent INSTANCE; - public int fontCount = 0; public static EntityCullingInstanceRenderer ENTITY_CULLING_INSTANCE_RENDERER; + static { RenderSystem.recordRenderCall(() -> ENTITY_CULLING_INSTANCE_RENDERER = new EntityCullingInstanceRenderer()); } @@ -31,181 +36,198 @@ public class CullingRenderEvent { HudRenderCallback.EVENT.register((matrixStack, tickDelta) -> INSTANCE.onOverlayRender(matrixStack, tickDelta)); } - public void onOverlayRender(GuiGraphics guiGraphics, float tickDelta ) { + public void onOverlayRender(GuiGraphics guiGraphics, float tickDelta) { if (Minecraft.getInstance().player == null) { return; } - if (CullingHandler.INSTANCE.DEBUG > 0) { + if (CullingHandler.DEBUG > 0) { Minecraft minecraft = Minecraft.getInstance(); int width = minecraft.getWindow().getGuiScaledWidth() / 2; - int height = 12; + int height = 20; int widthScale = 80; - int heightScale = -minecraft.font.lineHeight * fontCount; - fontCount = 0; - if(CullingHandler.INSTANCE.fps == 0 ) { - CullingHandler.INSTANCE.fps++; + List monitorTexts = new ArrayList<>(); + + if (CullingHandler.fps == 0) { + CullingHandler.fps++; } - if(CullingHandler.INSTANCE.cullingInitCount == 0 ) { - CullingHandler.INSTANCE.cullingInitCount++; + if (CullingHandler.cullingInitCount == 0) { + CullingHandler.cullingInitCount++; } - if(CullingHandler.INSTANCE.chunkCount == 0 ) { - CullingHandler.INSTANCE.chunkCount++; + if (CullingHandler.chunkCount == 0) { + CullingHandler.chunkCount++; } - if(CullingHandler.INSTANCE.DEBUG > 1) { - if (Config.getCullChunk()) { - String cullingInitTime = Component.translatable("brute_force_rendering_culling.chunk_culling_init").getString() + ": " + (CullingHandler.INSTANCE.chunkCullingInitTime /1000/CullingHandler.INSTANCE.cullingInitCount) + " μs"; - drawString(guiGraphics, cullingInitTime, width, height - heightScale); + int index = Minecraft.getInstance().fpsString.indexOf("fps"); + if (index != -1) { + String extractedString = Minecraft.getInstance().fpsString.substring(0, index + 3); + String fps = "FPS: " + extractedString; + addString(monitorTexts, fps); + } - String chunkCullingTime = Component.translatable("brute_force_rendering_culling.chunk_culling_time").getString() + ": " + (CullingHandler.INSTANCE.chunkCullingTime / 1000 / CullingHandler.INSTANCE.chunkCount) + " μs"; - drawString(guiGraphics, chunkCullingTime, width, height - heightScale); - } + String cull = Component.translatable("brute_force_rendering_culling.cull_entity").getString() + ": " + + (Config.getCullEntity() ? Component.translatable("brute_force_rendering_culling.enable").getString() : Component.translatable("brute_force_rendering_culling.disable").getString()); + addString(monitorTexts, cull); - String chunkCulling = Component.translatable("brute_force_rendering_culling.chunk_culling").getString() + ": " + CullingHandler.INSTANCE.chunkCulling + " / " + CullingHandler.INSTANCE.chunkCount; - drawString(guiGraphics, chunkCulling, width, height - heightScale); + String cull_chunk = Component.translatable("brute_force_rendering_culling.cull_chunk").getString() + ": " + + (Config.getCullChunk() ? Component.translatable("brute_force_rendering_culling.enable").getString() : Component.translatable("brute_force_rendering_culling.disable").getString()); + addString(monitorTexts, cull_chunk); + + if (CullingHandler.DEBUG > 1) { + String Sampler = Component.translatable("brute_force_rendering_culling.sampler").getString() + ": " + String.valueOf((Float.parseFloat(String.format("%.0f", Config.getSampling() * 100.0D))) + "%"); + addString(monitorTexts, Sampler); if (Config.getCullEntity()) { - String initTime = Component.translatable("brute_force_rendering_culling.entity_culling_init").getString() + ": " + (CullingHandler.INSTANCE.entityCullingInitTime /1000/CullingHandler.INSTANCE.cullingInitCount) + " μs"; - drawString(guiGraphics, initTime, width, height - heightScale); + String blockCullingTime = Component.translatable("brute_force_rendering_culling.block_culling_time").getString() + ": " + (CullingHandler.blockCullingTime / 1000 / CullingHandler.fps) + " μs"; + addString(monitorTexts, blockCullingTime); - String blockCullingTime = Component.translatable("brute_force_rendering_culling.block_culling_time").getString() + ": " + (CullingHandler.INSTANCE.blockCullingTime/1000/CullingHandler.INSTANCE.fps) + " μs"; - drawString(guiGraphics, blockCullingTime, width, height - heightScale); + String blockCulling = Component.translatable("brute_force_rendering_culling.block_culling").getString() + ": " + CullingHandler.blockCulling + " / " + CullingHandler.blockCount; + addString(monitorTexts, blockCulling); - String blockCulling = Component.translatable("brute_force_rendering_culling.block_culling").getString() + ": " + CullingHandler.INSTANCE.blockCulling + " / " + CullingHandler.INSTANCE.blockCount; - drawString(guiGraphics, blockCulling, width, height - heightScale); + String entityCullingTime = Component.translatable("brute_force_rendering_culling.entity_culling_time").getString() + ": " + (CullingHandler.entityCullingTime / 1000 / CullingHandler.fps) + " μs"; + addString(monitorTexts, entityCullingTime); - String entityCullingTime = Component.translatable("brute_force_rendering_culling.entity_culling_time").getString() + ": " + (CullingHandler.INSTANCE.entityCullingTime/1000/CullingHandler.INSTANCE.fps) + " μs"; - drawString(guiGraphics, entityCullingTime, width, height - heightScale); + String entityCulling = Component.translatable("brute_force_rendering_culling.entity_culling").getString() + ": " + CullingHandler.entityCulling + " / " + CullingHandler.entityCount; + addString(monitorTexts, entityCulling); - String entityCulling = Component.translatable("brute_force_rendering_culling.entity_culling").getString() + ": " + CullingHandler.INSTANCE.entityCulling + " / " + CullingHandler.INSTANCE.entityCount; - drawString(guiGraphics, entityCulling, width, height - heightScale); + String initTime = Component.translatable("brute_force_rendering_culling.entity_culling_init").getString() + ": " + (CullingHandler.entityCullingInitTime / 1000 / CullingHandler.cullingInitCount) + " μs"; + addString(monitorTexts, initTime); } - String Sampler = Component.translatable("brute_force_rendering_culling.sampler").getString() + ": " + String.valueOf((Float.parseFloat(String.format("%.0f", Config.getSampling() * 100.0D))) + "%"); - drawString(guiGraphics, Sampler, width, height - heightScale); - } + String chunkCulling = Component.translatable("brute_force_rendering_culling.chunk_culling").getString() + ": " + CullingHandler.chunkCulling + " / " + CullingHandler.chunkCount; + addString(monitorTexts, chunkCulling); - String cull_chunk = Component.translatable("brute_force_rendering_culling.cull_chunk").getString() + ": " - + (Config.getCullChunk() ? Component.translatable("brute_force_rendering_culling.enable").getString() : Component.translatable("brute_force_rendering_culling.disable").getString()); - drawString(guiGraphics, cull_chunk, width, height - heightScale); + if (Config.getCullChunk()) { + if (CullingHandler.CHUNK_CULLING_MAP != null) { + String chunkCullingCount = Component.translatable("brute_force_rendering_culling.chunk_update_count").getString() + ": " + CullingHandler.CHUNK_CULLING_MAP.lastQueueUpdateCount; + addString(monitorTexts, chunkCullingCount); + } - String cull = Component.translatable("brute_force_rendering_culling.cull_entity").getString() + ": " - + (Config.getCullEntity() ? Component.translatable("brute_force_rendering_culling.enable").getString() : Component.translatable("brute_force_rendering_culling.disable").getString()); - drawString(guiGraphics, cull, width, height - heightScale); + String chunkCullingTime = Component.translatable("brute_force_rendering_culling.chunk_culling_time").getString() + ": " + (CullingHandler.chunkCullingTime / 1000 / CullingHandler.fps) + " μs"; + addString(monitorTexts, chunkCullingTime); - int index = Minecraft.getInstance().fpsString.indexOf("fps"); - if (index != -1) { - String extractedString = Minecraft.getInstance().fpsString.substring(0, index+3); - String fps = "FPS: " + extractedString; - drawString(guiGraphics, fps, width, height - heightScale); + String cullingInitTime = Component.translatable("brute_force_rendering_culling.chunk_culling_init").getString() + ": " + (CullingHandler.chunkCullingInitTime / 1000 / CullingHandler.cullingInitCount) + " μs"; + addString(monitorTexts, cullingInitTime); + } } - height -= heightScale - minecraft.font.lineHeight; - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); + int heightOffset = minecraft.font.lineHeight * monitorTexts.size(); + int top = height; + int bottom = height + heightOffset; + int left = width + widthScale; + int right = width - widthScale; + renderText(guiGraphics, monitorTexts, width, top); + float bgColor = 1.0f; + float bgAlpha = 0.3f; + BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder(); + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); + bufferbuilder.vertex(right - 1, bottom + 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(right - 1), v(bottom + 1)).endVertex(); + bufferbuilder.vertex(left + 1, bottom + 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(left + 1), v(bottom + 1)).endVertex(); + bufferbuilder.vertex(left + 1, top - 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(left + 1), v(top - 1)).endVertex(); + bufferbuilder.vertex(right - 1, top - 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(right - 1), v(top - 1)).endVertex(); + RenderSystem.setShaderTexture(0, Minecraft.getInstance().getMainRenderTarget().getColorTextureId()); + CullingHandler.useShader(CullingHandler.REMOVE_COLOR_SHADER); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 0.1f); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.DST_COLOR); - RenderSystem.setShader(GameRenderer::getPositionColorShader); - BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder(); - bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - bufferbuilder.vertex(width - widthScale, height, 0.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); - bufferbuilder.vertex(width + widthScale, height, 0.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); - bufferbuilder.vertex(width + widthScale, height + heightScale, 0.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); - bufferbuilder.vertex(width - widthScale, height + heightScale, 0.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); BufferUploader.drawWithShader(bufferbuilder.end()); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); + + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_DST_COLOR, GlStateManager.DestFactor.ZERO); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - bufferbuilder.vertex(width - widthScale - 2, height + 2, 0.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); - bufferbuilder.vertex(width + widthScale + 2, height + 2, 0.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); - bufferbuilder.vertex(width + widthScale + 2, height + heightScale - 2, 0.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); - bufferbuilder.vertex(width - widthScale - 2, height + heightScale - 2, 0.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); + bufferbuilder.vertex(right, bottom, 0.0D).color(1.0F, 1.0F, 1.0F, 1.0F).endVertex(); + bufferbuilder.vertex(left, bottom, 0.0D).color(1.0F, 1.0F, 1.0F, 1.0F).endVertex(); + bufferbuilder.vertex(left, top, 0.0D).color(1.0F, 1.0F, 1.0F, 1.0F).endVertex(); + bufferbuilder.vertex(right, top, 0.0D).color(1.0F, 1.0F, 1.0F, 1.0F).endVertex(); + RenderSystem.setShader(GameRenderer::getPositionColorShader); BufferUploader.drawWithShader(bufferbuilder.end()); RenderSystem.defaultBlendFunc(); RenderSystem.disableBlend(); - if(!CullingHandler.INSTANCE.checkTexture) + if (!CullingHandler.checkTexture) return; Tesselator tessellator = Tesselator.getInstance(); float screenScale = 1.0f; double windowScale = 0.4; - for(int i = 0; i < CullingHandler.DEPTH_BUFFER_TARGET.length; ++i) { + RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + RenderSystem.enableBlend(); + RenderSystem.depthMask(false); + RenderSystem.defaultBlendFunc(); + for (int i = 0; i < CullingHandler.DEPTH_BUFFER_TARGET.length; ++i) { int scaledHeight = (int) (minecraft.getWindow().getGuiScaledHeight() * windowScale * screenScale); int scaledWidth = (int) (minecraft.getWindow().getGuiScaledWidth() * windowScale * screenScale); - int offsetHeight = (int) ((1-screenScale)* 2 * minecraft.getWindow().getGuiScaledHeight() * windowScale); - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + int offsetHeight = (int) ((1 - screenScale) * 2 * minecraft.getWindow().getGuiScaledHeight() * windowScale); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - bufferbuilder.vertex(0.0D, minecraft.getWindow().getGuiScaledHeight()-offsetHeight, 0.0D).uv(0.0F, 0.0F).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex(scaledWidth, minecraft.getWindow().getGuiScaledHeight()-offsetHeight, 0.0D).uv(1, 0.0F).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex(scaledWidth, minecraft.getWindow().getGuiScaledHeight()-scaledHeight-offsetHeight, 0.0D).uv(1, 1).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex(0.0D, minecraft.getWindow().getGuiScaledHeight()-scaledHeight-offsetHeight, 0.0D).uv(0.0F, 1).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(0.0D, minecraft.getWindow().getGuiScaledHeight() - offsetHeight, 0.0D).uv(0.0F, 0.0F).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(scaledWidth, minecraft.getWindow().getGuiScaledHeight() - offsetHeight, 0.0D).uv(1, 0.0F).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(scaledWidth, minecraft.getWindow().getGuiScaledHeight() - scaledHeight - offsetHeight, 0.0D).uv(1, 1).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(0.0D, minecraft.getWindow().getGuiScaledHeight() - scaledHeight - offsetHeight, 0.0D).uv(0.0F, 1).color(255, 255, 255, 255).endVertex(); RenderSystem.setShaderTexture(0, CullingHandler.DEPTH_TEXTURE[i]); - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.defaultBlendFunc(); tessellator.end(); - RenderSystem.depthMask(true); - RenderSystem.disableBlend(); screenScale *= 0.5f; } - if(Config.getCullEntity()) { - height = (int) (minecraft.getWindow().getGuiScaledHeight()*0.25f); - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + if (Config.getCullEntity()) { + height = (int) (minecraft.getWindow().getGuiScaledHeight() * 0.25f); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth()-height, height, 0.0D).uv(0.0F, 0.0F).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex((double)minecraft.getWindow().getGuiScaledWidth(), height, 0.0D).uv(1, 0.0F).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex((double)minecraft.getWindow().getGuiScaledWidth(), 0, 0.0D).uv(1, 1).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth()-height, 0, 0.0D).uv(0.0F, 1).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth() - height, height, 0.0D).uv(0.0F, 0.0F).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex((double) minecraft.getWindow().getGuiScaledWidth(), height, 0.0D).uv(1, 0.0F).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex((double) minecraft.getWindow().getGuiScaledWidth(), 0, 0.0D).uv(1, 1).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth() - height, 0, 0.0D).uv(0.0F, 1).color(255, 255, 255, 255).endVertex(); RenderSystem.setShaderTexture(0, CullingHandler.ENTITY_CULLING_MAP_TARGET.getColorTextureId()); - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.defaultBlendFunc(); tessellator.end(); - RenderSystem.depthMask(true); - RenderSystem.disableBlend(); } - if(Config.getCullChunk()) { - height = (int) (minecraft.getWindow().getGuiScaledHeight()*0.25f); - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + if (Config.getCullChunk()) { + height = (int) (minecraft.getWindow().getGuiScaledHeight() * 0.25f); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth()-height, height*2, 0.0D).uv(0.0F, 0.0F).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex((double)minecraft.getWindow().getGuiScaledWidth(), height*2, 0.0D).uv(1, 0.0F).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex((double)minecraft.getWindow().getGuiScaledWidth(), height, 0.0D).uv(1, 1).color(255, 255, 255, 255).endVertex(); - bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth()-height, height, 0.0D).uv(0.0F, 1).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth() - height, height * 2, 0.0D).uv(0.0F, 0.0F).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex((double) minecraft.getWindow().getGuiScaledWidth(), height * 2, 0.0D).uv(1, 0.0F).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex((double) minecraft.getWindow().getGuiScaledWidth(), height, 0.0D).uv(1, 1).color(255, 255, 255, 255).endVertex(); + bufferbuilder.vertex(minecraft.getWindow().getGuiScaledWidth() - height, height, 0.0D).uv(0.0F, 1).color(255, 255, 255, 255).endVertex(); RenderSystem.setShaderTexture(0, CullingHandler.CHUNK_CULLING_MAP_TARGET.getColorTextureId()); - RenderSystem.enableBlend(); - RenderSystem.depthMask(false); - RenderSystem.defaultBlendFunc(); tessellator.end(); - RenderSystem.depthMask(true); - RenderSystem.disableBlend(); } + RenderSystem.depthMask(true); + RenderSystem.disableBlend(); } } - public void drawString(GuiGraphics guiGraphics, String text, int width, int height) { + public void addString(List list, String text) { + list.add(text); + } + + public void renderText(GuiGraphics guiGraphics, List list, int width, int height) { Font font = Minecraft.getInstance().font; - guiGraphics.drawString(font, text, (int) (width - (font.width(text) / 2f)), height - font.lineHeight * fontCount, 16777215); - fontCount++; + for (int i = 0; i < list.size(); ++i) { + String text = list.get(i); + guiGraphics.drawString(font, text, (int) (width - (font.width(text) / 2f)), height + font.lineHeight * i, 16777215); + } } - protected static void onUpdateCullingMap() { - if(!CullingHandler.anyCulling()) + protected static void updateCullingMap() { + if (!CullingHandler.anyCulling()) return; Tesselator tessellator = Tesselator.getInstance(); BufferBuilder bufferbuilder = tessellator.getBuilder(); - if(CullingHandler.INSTANCE.checkCulling) + if (CullingHandler.checkCulling) return; - if(Config.getCullEntity() && CullingHandler.ENTITY_CULLING_MAP != null && CullingHandler.ENTITY_CULLING_MAP.needTransferData()) { + if (Config.getCullEntity() && CullingHandler.ENTITY_CULLING_MAP != null && CullingHandler.ENTITY_CULLING_MAP.needTransferData()) { CullingHandler.ENTITY_CULLING_MAP_TARGET.clear(Minecraft.ON_OSX); CullingHandler.ENTITY_CULLING_MAP_TARGET.bindWrite(false); CullingHandler.callDepthTexture(); @@ -213,15 +235,15 @@ protected static void onUpdateCullingMap() { ENTITY_CULLING_INSTANCE_RENDERER.drawWithShader(CullingHandler.INSTANCED_ENTITY_CULLING_SHADER); } - if(Config.getCullChunk() && CullingHandler.CHUNK_CULLING_MAP != null && CullingHandler.CHUNK_CULLING_MAP.needTransferData()) { + if (Config.getCullChunk() && CullingHandler.CHUNK_CULLING_MAP != null && CullingHandler.CHUNK_CULLING_MAP.needTransferData()) { CullingHandler.useShader(CullingHandler.CHUNK_CULLING_SHADER); CullingHandler.CHUNK_CULLING_MAP_TARGET.clear(Minecraft.ON_OSX); CullingHandler.CHUNK_CULLING_MAP_TARGET.bindWrite(false); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION); bufferbuilder.vertex(-1.0f, -1.0f, 0.0f).endVertex(); bufferbuilder.vertex(1.0f, -1.0f, 0.0f).endVertex(); - bufferbuilder.vertex(1.0f, 1.0f, 0.0f).endVertex(); - bufferbuilder.vertex(-1.0f, 1.0f, 0.0f).endVertex(); + bufferbuilder.vertex(1.0f, 1.0f, 0.0f).endVertex(); + bufferbuilder.vertex(-1.0f, 1.0f, 0.0f).endVertex(); CullingHandler.callDepthTexture(); tessellator.end(); } @@ -231,24 +253,32 @@ protected static void onUpdateCullingMap() { public static void setUniform(ShaderInstance shader) { ICullingShader shaderInstance = (ICullingShader) shader; - if(shaderInstance.getCullingCameraPos() != null) { + if (shaderInstance.getCullingCameraPos() != null) { Vec3 pos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); float[] array = new float[]{(float) pos.x, (float) pos.y, (float) pos.z}; shaderInstance.getCullingCameraPos().set(array); } - if(shaderInstance.getFrustumPos() != null) { + if (shaderInstance.getCullingCameraDir() != null) { + Vector3f pos = Minecraft.getInstance().gameRenderer.getMainCamera().getLookVector(); + float[] array = new float[]{pos.x, pos.y, pos.z}; + shaderInstance.getCullingCameraDir().set(array); + } + if (shaderInstance.getCullingFov() != null) { + shaderInstance.getCullingFov().set((float) CullingHandler.FOV + (float) (170 - CullingHandler.FOV)); + } + if (shaderInstance.getFrustumPos() != null && CullingHandler.FRUSTUM != null) { Vec3 pos = new Vec3( - ((AccessorFrustum)CullingHandler.FRUSTUM).camX(), - ((AccessorFrustum)CullingHandler.FRUSTUM).camY(), - ((AccessorFrustum)CullingHandler.FRUSTUM).camZ()); + ((AccessorFrustum) CullingHandler.FRUSTUM).camX(), + ((AccessorFrustum) CullingHandler.FRUSTUM).camY(), + ((AccessorFrustum) CullingHandler.FRUSTUM).camZ()); float[] array = new float[]{(float) pos.x, (float) pos.y, (float) pos.z}; shaderInstance.getFrustumPos().set(array); } - if(shaderInstance.getCullingViewMat() != null) { + if (shaderInstance.getCullingViewMat() != null) { shaderInstance.getCullingViewMat().set(CullingHandler.VIEW_MATRIX); } - if(shaderInstance.getCullingProjMat() != null) { + if (shaderInstance.getCullingProjMat() != null) { shaderInstance.getCullingProjMat().set(CullingHandler.PROJECTION_MATRIX); } if(shaderInstance.getCullingFrustum() != null) { @@ -266,10 +296,10 @@ public static void setUniform(ShaderInstance shader) { } shaderInstance.getCullingFrustum().set(array); } - if(shaderInstance.getRenderDistance() != null) { + if (shaderInstance.getRenderDistance() != null) { float distance = Minecraft.getInstance().options.getEffectiveRenderDistance(); - if(shader == CullingHandler.COPY_DEPTH_SHADER) { - if(CullingHandler.DEPTH_INDEX > 0) + if (shader == CullingHandler.COPY_DEPTH_SHADER) { + if (CullingHandler.DEPTH_INDEX > 0) distance = 2; else distance = 0; @@ -277,34 +307,35 @@ public static void setUniform(ShaderInstance shader) { shaderInstance.getRenderDistance().set(distance); } - if(shaderInstance.getDepthSize() != null) { - float[] array = new float[CullingHandler.depthSize*2]; - if(shader == CullingHandler.COPY_DEPTH_SHADER) { + if (shaderInstance.getDepthSize() != null) { + float[] array = new float[CullingHandler.DEPTH_SIZE * 2]; + if (shader == CullingHandler.COPY_DEPTH_SHADER) { array[0] = (float) CullingHandler.DEPTH_BUFFER_TARGET[CullingHandler.DEPTH_INDEX].width; array[1] = (float) CullingHandler.DEPTH_BUFFER_TARGET[CullingHandler.DEPTH_INDEX].height; } else { - for(int i = 0; i < CullingHandler.depthSize; ++i) { - int arrayIdx = i*2; + for (int i = 0; i < CullingHandler.DEPTH_SIZE; ++i) { + int arrayIdx = i * 2; array[arrayIdx] = (float) CullingHandler.DEPTH_BUFFER_TARGET[i].width; - array[arrayIdx+1] = (float) CullingHandler.DEPTH_BUFFER_TARGET[i].height; + array[arrayIdx + 1] = (float) CullingHandler.DEPTH_BUFFER_TARGET[i].height; } } shaderInstance.getDepthSize().set(array); } - if(shader == CullingHandler.COPY_DEPTH_SHADER && CullingHandler.DEPTH_INDEX > 0 && shader.SCREEN_SIZE != null) { - shader.SCREEN_SIZE.set((float) CullingHandler.DEPTH_BUFFER_TARGET[CullingHandler.DEPTH_INDEX-1].width, (float) CullingHandler.DEPTH_BUFFER_TARGET[CullingHandler.DEPTH_INDEX-1].height); + if (shader == CullingHandler.COPY_DEPTH_SHADER && CullingHandler.DEPTH_INDEX > 0 && shader.SCREEN_SIZE != null) { + shader.SCREEN_SIZE.set((float) CullingHandler.DEPTH_BUFFER_TARGET[CullingHandler.DEPTH_INDEX - 1].width, (float) CullingHandler.DEPTH_BUFFER_TARGET[CullingHandler.DEPTH_INDEX - 1].height); } - if(shaderInstance.getCullingSize() != null) { + if (shaderInstance.getCullingSize() != null) { shaderInstance.getCullingSize().set((float) CullingHandler.CHUNK_CULLING_MAP_TARGET.width, (float) CullingHandler.CHUNK_CULLING_MAP_TARGET.height); } - if(shaderInstance.getEntityCullingSize() != null) { + if (shaderInstance.getEntityCullingSize() != null) { shaderInstance.getEntityCullingSize().set((float) CullingHandler.ENTITY_CULLING_MAP_TARGET.width, (float) CullingHandler.ENTITY_CULLING_MAP_TARGET.height); } - if(shaderInstance.getLevelHeightOffset() != null) { - shaderInstance.getLevelHeightOffset().set(CullingHandler.LEVEL_HEIGHT_OFFSET); + if (shaderInstance.getLevelHeightOffset() != null) { + shaderInstance.getLevelHeightOffset().set(CullingHandler.LEVEL_SECTION_RANGE); } - if(shaderInstance.getLevelMinSection() != null && Minecraft.getInstance().level != null) { - shaderInstance.getLevelMinSection().set(Minecraft.getInstance().level.getMinSection()); + if (shaderInstance.getLevelMinSection() != null && Minecraft.getInstance().level != null) { + int min = Minecraft.getInstance().level.getMinSection(); + shaderInstance.getLevelMinSection().set(min); } } } diff --git a/src/main/java/rogo/renderingculling/api/IEntitiesForRender.java b/src/main/java/rogo/renderingculling/api/IEntitiesForRender.java deleted file mode 100644 index 83f66f7..0000000 --- a/src/main/java/rogo/renderingculling/api/IEntitiesForRender.java +++ /dev/null @@ -1,7 +0,0 @@ -package rogo.renderingculling.api; - -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -public interface IEntitiesForRender { - ObjectArrayList renderChunksInFrustum(); -} diff --git a/src/main/java/rogo/renderingculling/api/ModLoader.java b/src/main/java/rogo/renderingculling/api/ModLoader.java new file mode 100644 index 0000000..d301974 --- /dev/null +++ b/src/main/java/rogo/renderingculling/api/ModLoader.java @@ -0,0 +1,121 @@ +package rogo.renderingculling.api; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import rogo.renderingculling.event.WorldUnloadEvent; +import rogo.renderingculling.gui.ConfigScreen; +import rogo.renderingculling.util.OcclusionCullerThread; +import rogo.renderingculling.util.ShaderLoader; + +import java.io.IOException; +import java.util.function.Supplier; + +import static java.lang.Thread.MAX_PRIORITY; +import static rogo.renderingculling.api.CullingHandler.*; +import static rogo.renderingculling.api.CullingHandler.fromID; + +public class ModLoader implements ModInitializer { + + static int BG = ((200 & 0xFF) << 24) | + ((0) << 16) | + ((0) << 8) | + ((0)); + + static int B = ((100 & 0xFF) << 24) | + ((0xFF) << 16) | + ((0xFF) << 8) | + ((0xFF)); + + @Override + public void onInitialize() { + callWhenOn(EnvType.CLIENT, () -> () -> { + registerEvents(); + this.registerShader(); + init(); + }); + } + + public static void callWhenOn(EnvType envType, Supplier supplier) { + if(envType == FabricLoader.getInstance().getEnvironmentType()) { + supplier.get().run(); + } + } + + private void registerShader() { + RenderSystem.recordRenderCall(this::initShader); + } + + private void initShader() { + LOGGER.debug("try init shader chunk_culling"); + try { + SHADER_ENABLED = true; + CHUNK_CULLING_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("chunk_culling"), DefaultVertexFormat.POSITION); + INSTANCED_ENTITY_CULLING_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("instanced_entity_culling"), DefaultVertexFormat.POSITION); + COPY_DEPTH_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("copy_depth"), DefaultVertexFormat.POSITION); + REMOVE_COLOR_SHADER = new ShaderInstance(Minecraft.getInstance().getResourceManager(), fromID("remove_color"), DefaultVertexFormat.POSITION_COLOR_TEX); + SHADER_ENABLED = false; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void registerEvents() { + WorldUnloadEvent.WORLD_UNLOAD.register(this::onWorldUnload); + ClientTickEvents.START_CLIENT_TICK.register(this::onStartClientTick); + } + + private void onWorldUnload(Level world) { + if(world == Minecraft.getInstance().level) { + cleanup(); + } + } + + private void onStartClientTick(Minecraft client) { + if (Minecraft.getInstance().player != null && Minecraft.getInstance().level != null) { + Config.loadConfig(); + clientTickCount++; + if (Minecraft.getInstance().player.tickCount > 200 && clientTickCount > 200 && CHUNK_CULLING_MAP != null && !CHUNK_CULLING_MAP.isDone()) { + CHUNK_CULLING_MAP.setDone(); + LEVEL_SECTION_RANGE = Minecraft.getInstance().level.getMaxSection() - Minecraft.getInstance().level.getMinSection(); + LEVEL_MIN_SECTION_ABS = Math.abs(Minecraft.getInstance().level.getMinSection()); + LEVEL_MIN_POS = Minecraft.getInstance().level.getMinBuildHeight(); + LEVEL_POS_RANGE = Minecraft.getInstance().level.getMaxBuildHeight() - Minecraft.getInstance().level.getMinBuildHeight(); + + OcclusionCullerThread occlusionCullerThread = new OcclusionCullerThread(); + occlusionCullerThread.setName("Chunk Depth Occlusion Cull thread"); + occlusionCullerThread.setPriority(MAX_PRIORITY); + occlusionCullerThread.start(); + } + } else { + cleanup(); + } + } + + public static void onKeyPress() { + if (CONFIG_KEY.isDown()) { + Minecraft.getInstance().setScreen(new ConfigScreen(Component.translatable(MOD_ID + ".config"))); + } + if (DEBUG_KEY.isDown()) { + DEBUG++; + if (DEBUG >= 3) + DEBUG = 0; + } + } + + public static int getBG() { + return BG; + } + + public static int getB() { + return B; + } +} diff --git a/src/main/java/rogo/renderingculling/api/data/ChunkCullingMap.java b/src/main/java/rogo/renderingculling/api/data/ChunkCullingMap.java new file mode 100644 index 0000000..b74a707 --- /dev/null +++ b/src/main/java/rogo/renderingculling/api/data/ChunkCullingMap.java @@ -0,0 +1,150 @@ +package rogo.renderingculling.api.data; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import rogo.renderingculling.api.Config; +import rogo.renderingculling.api.CullingHandler; + +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.Queue; +import java.util.Set; + +public class ChunkCullingMap extends CullingMap { + private int renderDistance = 0; + private int spacePartitionSize = 0; + public int queueUpdateCount = 0; + public int lastQueueUpdateCount = 0; + private static final int[][] DIRECTIONS = {{0, 1, 0}, {0, -1, 0}, {-1, 0, 0}, {1, 0, 0}, {0, 0, 1}, {0, 0, -1}}; + private boolean updateVisibleChunks = true; + private Set visibleChunks = new HashSet<>(); + + private int cameraX; + private int cameraZ; + + @NotNull + private static BlockPos getOriginPos() { + Vec3 camera = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + int cameraX = (int) camera.x >> 4; + int cameraZ = (int) camera.z >> 4; + int cameraY = CullingHandler.mapChunkY(camera.y); + + BlockPos origin = new BlockPos(cameraX, cameraY, cameraZ); + if (origin.getY() < 0) { + origin = new BlockPos(cameraX, 0, cameraZ); + } else if (origin.getY() >= CullingHandler.LEVEL_SECTION_RANGE) { + origin = new BlockPos(cameraX, CullingHandler.LEVEL_SECTION_RANGE - 1, cameraZ); + } + return origin; + } + + public ChunkCullingMap(int width, int height) { + super(width, height); + } + + @Override + int configDelayCount() { + return Config.getDepthUpdateDelay(); + } + + @Override + int bindFrameBufferId() { + return CullingHandler.CHUNK_CULLING_MAP_TARGET.frameBufferId; + } + + public void generateIndex(int renderDistance) { + this.renderDistance = renderDistance; + spacePartitionSize = 2 * renderDistance + 1; + } + + public void updateCamera() { + Vec3 camera = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + cameraX = (int) camera.x >> 4; + cameraZ = (int) camera.z >> 4; + } + + public boolean isChunkOffsetCameraVisible(int x, int y, int z) { + return isChunkVisible((x >> 4) - cameraX, CullingHandler.mapChunkY(y), (z >> 4) - cameraZ); + } + + @Override + public void readData() { + super.readData(); + updateVisibleChunks = true; + } + + public boolean isChunkVisible(int posX, int posY, int posZ) { + int index = 1 + (((posX + renderDistance) * spacePartitionSize * CullingHandler.LEVEL_SECTION_RANGE + (posZ + renderDistance) * CullingHandler.LEVEL_SECTION_RANGE + posY) << 2); + if (index > 0 && index < cullingBuffer.limit()) { + return (cullingBuffer.get(index) & 0xFF) > 0; + } + return false; + } + + public void updateVisibleChunks() { + if (updateVisibleChunks) { + bfsSearch(getOriginPos()); + updateVisibleChunks = false; + } + } + + public Set getVisibleChunks() { + return visibleChunks; + } + + private void bfsSearch(BlockPos origin) { + Queue queue = new ArrayDeque<>(); + Set visible = new HashSet<>(); + Set visited = new HashSet<>(); + + visited.add(new BlockPos(0, origin.getY(), 0)); + queue.offer(new BlockPos(0, origin.getY(), 0)); + visible.add(new BlockPos(origin.getX(), origin.getY() - CullingHandler.LEVEL_MIN_SECTION_ABS, origin.getZ())); + + while (!queue.isEmpty()) { + BlockPos chunkPos = queue.poll(); + BlockPos offsetChunkPos = new BlockPos((chunkPos.getX() + origin.getX()) + , (chunkPos.getY() - CullingHandler.LEVEL_MIN_SECTION_ABS) + , (chunkPos.getZ() + origin.getZ())); + BlockPos absolutePos = new BlockPos(offsetChunkPos.getX() << 4, offsetChunkPos.getY() << 4, offsetChunkPos.getZ() << 4); + + if (!CullingHandler.FRUSTUM.isVisible(new AABB(absolutePos.getX(), absolutePos.getY(), absolutePos.getZ(), absolutePos.getX() + 16, absolutePos.getY() + 16, absolutePos.getZ() + 16))) { + continue; + } else { + boolean isChunkVisible = isChunkVisible(chunkPos.getX(), chunkPos.getY(), chunkPos.getZ()); + if (CullingHandler.checkCulling) + isChunkVisible = !isChunkVisible; + + if (isChunkVisible) + visible.add(offsetChunkPos); + else + continue; + } + + for (int[] direction : DIRECTIONS) { + int newX = chunkPos.getX() + direction[0]; + int newY = chunkPos.getY() + direction[1]; + int newZ = chunkPos.getZ() + direction[2]; + + if (renderDistance < Mth.abs(newX) || + renderDistance < Mth.abs(newZ) || + newY < 0 || + newY >= CullingHandler.LEVEL_SECTION_RANGE) + continue; + + BlockPos neighborChunk = new BlockPos(newX, newY, newZ); + + if (!visited.contains(neighborChunk)) { + queue.offer(neighborChunk); + visited.add(neighborChunk); + } + } + } + + visibleChunks = visible; + } +} diff --git a/src/main/java/rogo/renderingculling/api/CullingMap.java b/src/main/java/rogo/renderingculling/api/data/CullingMap.java similarity index 78% rename from src/main/java/rogo/renderingculling/api/CullingMap.java rename to src/main/java/rogo/renderingculling/api/data/CullingMap.java index 07f6d15..f332b34 100644 --- a/src/main/java/rogo/renderingculling/api/CullingMap.java +++ b/src/main/java/rogo/renderingculling/api/data/CullingMap.java @@ -1,10 +1,12 @@ -package rogo.renderingculling.api; +package rogo.renderingculling.api.data; import org.lwjgl.BufferUtils; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL31; +import rogo.renderingculling.api.CullingHandler; + import java.nio.ByteBuffer; import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE; @@ -25,7 +27,7 @@ public CullingMap(int width, int height) { cullingBuffer = BufferUtils.createByteBuffer(width * height * 4); pboId = GL15.glGenBuffers(); GL15.glBindBuffer(GL31.GL_PIXEL_PACK_BUFFER, pboId); - GL15.glBufferData(GL31.GL_PIXEL_PACK_BUFFER, (long) width * height * Float.BYTES * 4, GL15.GL_STREAM_READ); + GL15.glBufferData(GL31.GL_PIXEL_PACK_BUFFER, (long) width * height * 4 * Float.BYTES, GL15.GL_DYNAMIC_READ); GL15.glBindBuffer(GL31.GL_PIXEL_PACK_BUFFER, 0); } @@ -33,34 +35,40 @@ public boolean needTransferData() { return delayCount <= 0; } + public void syncDelay(CullingMap map) { + map.delayCount = this.delayCount; + } + public void transferData() { - if(delayCount <= 0) { + if (delayCount <= 0) { GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, bindFrameBufferId()); GL15.glBindBuffer(GL31.GL_PIXEL_PACK_BUFFER, pboId); GL11.glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, 0); GL15.glBindBuffer(GL31.GL_PIXEL_PACK_BUFFER, 0); - GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0); - delayCount = configDelayCount()+dynamicDelayCount(); + CullingHandler.bindMainFrameTarget(); + delayCount = configDelayCount() + dynamicDelayCount(); } else { delayCount--; } - if(delayCount <= 0) { + if (delayCount <= 0) { setTransferred(true); } } public void readData() { + GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, bindFrameBufferId()); GL15.glBindBuffer(GL31.GL_PIXEL_PACK_BUFFER, pboId); GL15.glGetBufferSubData(GL31.GL_PIXEL_PACK_BUFFER, 0, cullingBuffer); GL15.glBindBuffer(GL31.GL_PIXEL_PACK_BUFFER, 0); + CullingHandler.bindMainFrameTarget(); setTransferred(false); } abstract int configDelayCount(); public int dynamicDelayCount() { - if(CullingHandler.INSTANCE.fps > 200) { - return CullingHandler.INSTANCE.fps / 200; + if (CullingHandler.fps > 200) { + return CullingHandler.fps / 200; } return 0; diff --git a/src/main/java/rogo/renderingculling/api/EntityCullingMap.java b/src/main/java/rogo/renderingculling/api/data/EntityCullingMap.java similarity index 92% rename from src/main/java/rogo/renderingculling/api/EntityCullingMap.java rename to src/main/java/rogo/renderingculling/api/data/EntityCullingMap.java index c5a6717..7935fd2 100644 --- a/src/main/java/rogo/renderingculling/api/EntityCullingMap.java +++ b/src/main/java/rogo/renderingculling/api/data/EntityCullingMap.java @@ -1,9 +1,12 @@ -package rogo.renderingculling.api; +package rogo.renderingculling.api.data; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import rogo.renderingculling.api.Config; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.impl.IAABBObject; import rogo.renderingculling.util.LifeTimer; import java.nio.FloatBuffer; @@ -31,13 +34,13 @@ public boolean isObjectVisible(Object o) { int idx = entityMap.getIndex(o); idx = 1+idx*4; if(entityMap.tempObjectTimer.contains(o)) - entityMap.addTemp(o, CullingHandler.INSTANCE.clientTickCount); + entityMap.addTemp(o, CullingHandler.clientTickCount); if(idx > -1 && idx < cullingBuffer.limit()) { float cullingValue = (float) (cullingBuffer.get(idx) & 0xFF) / 255.0f; return cullingValue > 0.5; } else { - entityMap.addTemp(o, CullingHandler.INSTANCE.clientTickCount); + entityMap.addTemp(o, CullingHandler.clientTickCount); } return true; } diff --git a/src/main/java/rogo/renderingculling/api/IAABBObject.java b/src/main/java/rogo/renderingculling/api/impl/IAABBObject.java similarity index 69% rename from src/main/java/rogo/renderingculling/api/IAABBObject.java rename to src/main/java/rogo/renderingculling/api/impl/IAABBObject.java index d6e7ea5..2922ecb 100644 --- a/src/main/java/rogo/renderingculling/api/IAABBObject.java +++ b/src/main/java/rogo/renderingculling/api/impl/IAABBObject.java @@ -1,4 +1,4 @@ -package rogo.renderingculling.api; +package rogo.renderingculling.api.impl; import net.minecraft.world.phys.AABB; diff --git a/src/main/java/rogo/renderingculling/api/impl/ICollectorAccessor.java b/src/main/java/rogo/renderingculling/api/impl/ICollectorAccessor.java new file mode 100644 index 0000000..d15dd3a --- /dev/null +++ b/src/main/java/rogo/renderingculling/api/impl/ICollectorAccessor.java @@ -0,0 +1,10 @@ +package rogo.renderingculling.api.impl; + +import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; +import me.jellysquid.mods.sodium.client.render.chunk.lists.ChunkRenderList; + +public interface ICollectorAccessor { + void addAsyncToRebuildLists(RenderSection section); + + void addRenderList(ChunkRenderList renderList); +} diff --git a/src/main/java/rogo/renderingculling/api/ICullingShader.java b/src/main/java/rogo/renderingculling/api/impl/ICullingShader.java similarity index 80% rename from src/main/java/rogo/renderingculling/api/ICullingShader.java rename to src/main/java/rogo/renderingculling/api/impl/ICullingShader.java index 1297f2b..605109a 100644 --- a/src/main/java/rogo/renderingculling/api/ICullingShader.java +++ b/src/main/java/rogo/renderingculling/api/impl/ICullingShader.java @@ -1,10 +1,12 @@ -package rogo.renderingculling.api; +package rogo.renderingculling.api.impl; import com.mojang.blaze3d.shaders.Uniform; public interface ICullingShader { Uniform getRenderDistance(); Uniform getCullingCameraPos(); + Uniform getCullingCameraDir(); + Uniform getCullingFov(); Uniform getDepthSize(); Uniform getCullingSize(); Uniform getLevelHeightOffset(); diff --git a/src/main/java/rogo/renderingculling/api/impl/IEntitiesForRender.java b/src/main/java/rogo/renderingculling/api/impl/IEntitiesForRender.java new file mode 100644 index 0000000..f0e4bb1 --- /dev/null +++ b/src/main/java/rogo/renderingculling/api/impl/IEntitiesForRender.java @@ -0,0 +1,13 @@ +package rogo.renderingculling.api.impl; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; + +public interface IEntitiesForRender { + ObjectArrayList renderChunksInFrustum(); + ChunkRenderDispatcher.RenderChunk invokeGetRelativeFrom(BlockPos pos, ChunkRenderDispatcher.RenderChunk chunk, Direction dir); + ChunkRenderDispatcher.RenderChunk invokeGetRenderChunkAt(BlockPos pos); + +} diff --git a/src/main/java/rogo/renderingculling/api/IRenderChunkInfo.java b/src/main/java/rogo/renderingculling/api/impl/IRenderChunkInfo.java similarity index 80% rename from src/main/java/rogo/renderingculling/api/IRenderChunkInfo.java rename to src/main/java/rogo/renderingculling/api/impl/IRenderChunkInfo.java index 16827da..ad98756 100644 --- a/src/main/java/rogo/renderingculling/api/IRenderChunkInfo.java +++ b/src/main/java/rogo/renderingculling/api/impl/IRenderChunkInfo.java @@ -1,4 +1,4 @@ -package rogo.renderingculling.api; +package rogo.renderingculling.api.impl; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; diff --git a/src/main/java/rogo/renderingculling/api/IRenderSectionVisibility.java b/src/main/java/rogo/renderingculling/api/impl/IRenderSectionVisibility.java similarity index 82% rename from src/main/java/rogo/renderingculling/api/IRenderSectionVisibility.java rename to src/main/java/rogo/renderingculling/api/impl/IRenderSectionVisibility.java index 90393ed..21065a3 100644 --- a/src/main/java/rogo/renderingculling/api/IRenderSectionVisibility.java +++ b/src/main/java/rogo/renderingculling/api/impl/IRenderSectionVisibility.java @@ -1,10 +1,13 @@ -package rogo.renderingculling.api; +package rogo.renderingculling.api.impl; public interface IRenderSectionVisibility { boolean shouldCheckVisibility(int frame); + void updateVisibleTick(int frame); int getPositionX(); + int getPositionY(); + int getPositionZ(); } diff --git a/src/main/java/rogo/renderingculling/gui/ConfigScreen.java b/src/main/java/rogo/renderingculling/gui/ConfigScreen.java index 3c04c8e..ee3dfcb 100644 --- a/src/main/java/rogo/renderingculling/gui/ConfigScreen.java +++ b/src/main/java/rogo/renderingculling/gui/ConfigScreen.java @@ -4,24 +4,30 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.util.Mth; import net.minecraft.world.entity.player.Player; import rogo.renderingculling.api.Config; import rogo.renderingculling.api.CullingHandler; import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; public class ConfigScreen extends Screen { private boolean release = false; + int heightScale; public ConfigScreen(Component titleIn) { super(titleIn); + heightScale = (int) (Minecraft.getInstance().font.lineHeight * 2f + 1); } @Override @@ -29,36 +35,61 @@ public boolean isPauseScreen() { return false; } + public static float u(int width) { + return (float) width / Minecraft.getInstance().getWindow().getGuiScaledWidth(); + } + + public static float v(int height) { + return 1.0f - ((float) height / (Minecraft.getInstance().getWindow().getGuiScaledHeight())); + } + @Override public void renderBackground(GuiGraphics guiGraphics) { Minecraft minecraft = Minecraft.getInstance(); - int width = minecraft.getWindow().getGuiScaledWidth()/2; - int widthScale = width/4; - Font font = minecraft.font; - int heightScale = font.lineHeight*8; - int height = minecraft.getWindow().getGuiScaledHeight()/2+heightScale/2; + int width = minecraft.getWindow().getGuiScaledWidth() / 2; + int widthScale = width / 4; + int right = width - widthScale; + int left = width + widthScale; + int bottom = (int) (minecraft.getWindow().getGuiScaledHeight() * 0.8) + 20; + int top = bottom - heightScale * children().size() - 10; - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - RenderSystem.setShader(GameRenderer::getPositionColorShader); + float bgColor = 1.0f; + float bgAlpha = 0.3f; + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 0.1f); + CullingHandler.useShader(CullingHandler.REMOVE_COLOR_SHADER); BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.DST_COLOR); - bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - bufferbuilder.vertex(width-widthScale, height+heightScale, -2.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); - bufferbuilder.vertex(width+widthScale, height+heightScale, -2.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); - bufferbuilder.vertex(width+widthScale, height-heightScale, -2.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); - bufferbuilder.vertex(width-widthScale, height-heightScale, -2.0D).color(0.3F, 0.3F, 0.3F, 0.2f).endVertex(); + bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); + bufferbuilder.vertex(right - 1, bottom + 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(right - 1), v(bottom + 1)).endVertex(); + bufferbuilder.vertex(left + 1, bottom + 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(left + 1), v(bottom + 1)).endVertex(); + bufferbuilder.vertex(left + 1, top - 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(left + 1), v(top - 1)).endVertex(); + bufferbuilder.vertex(right - 1, top - 1, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha) + .uv(u(right - 1), v(top - 1)).endVertex(); + RenderSystem.setShaderTexture(0, Minecraft.getInstance().getMainRenderTarget().getColorTextureId()); BufferUploader.drawWithShader(bufferbuilder.end()); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE); + bgAlpha = 1.0f; + RenderSystem.setShader(GameRenderer::getPositionColorShader); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - bufferbuilder.vertex(width-widthScale-2, height+heightScale+2, -1.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); - bufferbuilder.vertex(width+widthScale+2, height+heightScale+2, -1.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); - bufferbuilder.vertex(width+widthScale+2, height-heightScale-2, -1.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); - bufferbuilder.vertex(width-widthScale-2, height-heightScale-2, -1.0D).color(1.0F, 1.0F, 1.0F, 0.1f).endVertex(); + bufferbuilder.vertex(right, bottom, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha).endVertex(); + bufferbuilder.vertex(left, bottom, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha).endVertex(); + bufferbuilder.vertex(left, top, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha).endVertex(); + bufferbuilder.vertex(right, top, 0.0D) + .color(bgColor, bgColor, bgColor, bgAlpha).endVertex(); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_DST_COLOR, GlStateManager.DestFactor.ZERO); BufferUploader.drawWithShader(bufferbuilder.end()); RenderSystem.defaultBlendFunc(); RenderSystem.disableBlend(); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); } @Override @@ -76,8 +107,8 @@ public boolean keyPressed(int p_96552_, int p_96553_, int p_96554_) { @Override public boolean keyReleased(int p_94715_, int p_94716_, int p_94717_) { - if(CullingHandler.CONFIG_KEY.matches(p_94715_, p_94716_)) { - if(release) { + if (CullingHandler.CONFIG_KEY.matches(p_94715_, p_94716_)) { + if (release) { this.onClose(); return true; } else { @@ -90,64 +121,72 @@ public boolean keyReleased(int p_94715_, int p_94716_, int p_94717_) { @Override protected void init() { Player player = Minecraft.getInstance().player; - if(player == null) { + if (player == null) { onClose(); return; } - int heightScale = (int) (minecraft.font.lineHeight*2f)+1; - NeatSliderButton sampler = new NeatSliderButton(width/2-50, height/2+heightScale+12, 100, 14, Config.getSampling(), - (sliderButton) -> { - Component component = Component.literal((int)(sliderButton.getValue() * 100.0D) + "%"); - return (Component.translatable("brute_force_rendering_culling.sampler")).append(": ").append(component); - }, (value) -> { - double v = Float.parseFloat(String.format("%.2f",value)); - Config.setSampling(v); - }); - NeatSliderButton entityUpdateRate = new NeatSliderButton(width/2-50, height/2+heightScale*2+12, 100, 14, Config.getCullingEntityRate()/20f, - (sliderButton) -> { - Component component = Component.literal( String.valueOf((int)(sliderButton.getValue() * 20.0D))); - return (Component.translatable("brute_force_rendering_culling.culling_entity_update_rate")).append(": ").append(component); - }, (value) -> { - int i = (int) (value*20); - Config.setCullingEntityRate(i); - }); - NeatButton debug = new NeatButton(width/2-50, height/2+heightScale*4+12, 100, 14 - , (button) -> { - CullingHandler.INSTANCE.checkCulling = !CullingHandler.INSTANCE.checkCulling; - }, () -> (CullingHandler.INSTANCE.checkCulling ? Component.translatable("brute_force_rendering_culling.disable").append(" ").append(Component.literal("Debug")) - : Component.translatable("brute_force_rendering_culling.enable").append(" ").append(Component.literal("Debug")))); - NeatButton checkTexture = new NeatButton(width/2-50, height/2+heightScale*3+12, 100, 14 - , (button) -> { - CullingHandler.INSTANCE.checkTexture = !CullingHandler.INSTANCE.checkTexture; - }, () -> (CullingHandler.INSTANCE.checkTexture ? Component.translatable("brute_force_rendering_culling.disable").append(" ").append(Component.literal("Check Texture")) - : Component.translatable("brute_force_rendering_culling.enable").append(" ").append(Component.literal("Check Texture")))); - NeatSliderButton delay = new NeatSliderButton(width/2-50, height/2+12, 100, 14, Config.getDepthUpdateDelay()/10f, - (sliderButton) -> { - Component component = Component.literal(String.valueOf((int)(sliderButton.getValue() * 10.0D))); - return (Component.translatable("brute_force_rendering_culling.culling_map_update_delay")).append(": ").append(component); - }, (value) -> { - int i = (int) (value*10); - Config.setDepthUpdateDelay(i); - }); - NeatButton close = new NeatButton(width/2-50, height/2-heightScale*2+12, 100, 14 , (button) -> { - Config.setCullEntity(!Config.getCullEntity()); - }, () -> (Config.getCullEntity() ? Component.translatable("brute_force_rendering_culling.disable").append(" ").append(Component.translatable("brute_force_rendering_culling.cull_entity")) - : Component.translatable("brute_force_rendering_culling.enable").append(" ").append(Component.translatable("brute_force_rendering_culling.cull_entity")))); - NeatButton chunk = new NeatButton(width/2-50, height/2-heightScale+12, 100, 14 , (button) -> { - Config.setCullChunk(!Config.getCullChunk()); - }, () -> (Config.getCullChunk() ? Component.translatable("brute_force_rendering_culling.disable").append(" ").append(Component.translatable("brute_force_rendering_culling.cull_chunk")) - : Component.translatable("brute_force_rendering_culling.enable").append(" ").append(Component.translatable("brute_force_rendering_culling.cull_chunk")))); - this.addWidget(sampler); - this.addWidget(delay); - this.addWidget(entityUpdateRate); - this.addWidget(close); - this.addWidget(chunk); - this.addWidget(debug); - this.addWidget(checkTexture); + //if (player.getName().getString().equals("Dev")) { + addConfigButton(() -> CullingHandler.checkCulling, (b) -> CullingHandler.checkCulling = b, () -> Component.literal("Debug")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.debug")); + + addConfigButton(() -> CullingHandler.checkTexture, (b) -> CullingHandler.checkTexture = b, () -> Component.literal("Check Texture")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.check_texture")); + //} + + addConfigButton(Config::getSampling, (value) -> { + double format = Mth.floor(value * 20) * 0.05; + format = Double.parseDouble(String.format("%.2f", format)); + Config.setSampling(format); + return format; + }, (value) -> String.valueOf(Mth.floor(value * 100) + "%"), () -> Component.translatable("brute_force_rendering_culling.sampler")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.sampler")); + + addConfigButton(() -> Config.getDepthUpdateDelay() / 10d, (value) -> { + int format = Mth.floor(value * 10); + if (format > 0) { + format -= Config.getShaderDynamicDelay(); + } + Config.setDepthUpdateDelay(format); + format += Config.getShaderDynamicDelay(); + return format * 0.1; + }, (value) -> { + int format = Mth.floor(value * 10); + return String.valueOf(format); + }, () -> Component.translatable("brute_force_rendering_culling.culling_map_update_delay")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.culling_map_update_delay")); + + addConfigButton(Config::getCullChunk, Config::getAsyncChunkRebuild, Config::setAsyncChunkRebuild, () -> Component.translatable("brute_force_rendering_culling.async")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.async")); + addConfigButton(Config::getCullChunk, Config::setCullChunk, () -> Component.translatable("brute_force_rendering_culling.cull_chunk")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.cull_chunk")); + addConfigButton(Config::getCullEntity, Config::setCullEntity, () -> Component.translatable("brute_force_rendering_culling.cull_entity")) + .setDetailMessage(() -> Component.translatable("brute_force_rendering_culling.detail.cull_entity")); + super.init(); } + public NeatButton addConfigButton(Supplier getter, Consumer setter, Supplier updateMessage) { + NeatButton button = new NeatButton(width / 2 - 50, (int) ((height * 0.8) - heightScale * children().size()), 100, 14 + , getter, setter, updateMessage); + this.addWidget(button); + return button; + } + + public NeatButton addConfigButton(Supplier enable, Supplier getter, Consumer setter, Supplier updateMessage) { + NeatButton button = new NeatButton(width / 2 - 50, (int) ((height * 0.8) - heightScale * children().size()), 100, 14 + , enable, getter, setter, updateMessage); + this.addWidget(button); + return button; + } + + public NeatSliderButton addConfigButton(Supplier getter, Function setter, Function display, Supplier name) { + NeatSliderButton button = new NeatSliderButton(width / 2 - 50, (int) ((height * 0.8) - heightScale * children().size()), 100, 14 + , getter, setter, display, name); + this.addWidget(button); + return button; + } + @Override public boolean mouseDragged(double p_94699_, double p_94700_, int p_94701_, double p_94702_, double p_94703_) { return super.mouseDragged(p_94699_, p_94700_, p_94701_, p_94702_, p_94703_); @@ -155,13 +194,19 @@ public boolean mouseDragged(double p_94699_, double p_94700_, int p_94701_, doub @Override public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { - + this.renderBackground(guiGraphics); List children = children(); + for (GuiEventListener button : children) { - if(button instanceof AbstractWidget b) + if (button instanceof AbstractWidget b) b.render(guiGraphics, mouseX, mouseY, partialTicks); } - this.renderBackground(guiGraphics); + for (GuiEventListener button : children) { + if (button instanceof NeatButton b) + b.shouDetail(guiGraphics, minecraft.font); + if (button instanceof NeatSliderButton b) + b.shouDetail(guiGraphics, minecraft.font); + } } } diff --git a/src/main/java/rogo/renderingculling/gui/NeatButton.java b/src/main/java/rogo/renderingculling/gui/NeatButton.java index adc8760..ab3deab 100644 --- a/src/main/java/rogo/renderingculling/gui/NeatButton.java +++ b/src/main/java/rogo/renderingculling/gui/NeatButton.java @@ -10,45 +10,97 @@ import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; +import rogo.renderingculling.api.CullingHandler; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; public class NeatButton extends Button { - public NeatButton(int p_93721_, int p_93722_, int p_93723_, int p_93724_, OnPress p_93726_, Supplier updateMessage) { - super(p_93721_, p_93722_, p_93723_, p_93724_, updateMessage.get(), (b) -> { - p_93726_.onPress(b); - b.setMessage(updateMessage.get()); - }, DEFAULT_NARRATION); + public boolean cache = false; + public Supplier getter; + public Supplier name; + private Supplier enable = () -> true; + private Supplier detailMessage; + + public NeatButton(int p_93721_, int p_93722_, int p_93723_, int p_93724_, Supplier getter, Consumer setter, Supplier name) { + super(p_93721_, p_93722_, p_93723_, p_93724_, name.get(), (b) -> ((NeatButton) b).updateValue(setter), DEFAULT_NARRATION); + this.getter = getter; + this.name = name; + } + + public NeatButton(int p_93721_, int p_93722_, int p_93723_, int p_93724_, Supplier shouldEnable, Supplier getter, Consumer setter, Supplier name) { + this(p_93721_, p_93722_, p_93723_, p_93724_, getter, setter, name); + this.enable = shouldEnable; + } + + private void updateValue(Consumer setter) { + if (enable.get()) { + setter.accept(!getter.get()); + } + } + + public void setDetailMessage(Supplier detailMessage) { + this.detailMessage = detailMessage; } @Override public void renderWidget(GuiGraphics guiGraphics, int p_93747_, int p_93748_, float p_93749_) { Minecraft minecraft = Minecraft.getInstance(); Font font = minecraft.font; + boolean display = getter.get(); + int j = display && enable.get() ? 16777215 : 10526880; + guiGraphics.drawCenteredString(font, display ? Component.literal("■") : Component.literal("□"), this.getX() + this.width / 2 - 40, this.getY() + (this.height - 8) / 2, j | Mth.ceil(this.alpha * 255.0F) << 24); + + if (display != cache) { + cache = display; + } RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.DST_COLOR); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_DST_COLOR, GlStateManager.DestFactor.ZERO); RenderSystem.setShader(GameRenderer::getPositionColorShader); BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder(); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - float alpha = this.isHoveredOrFocused() ? 0.7f : 0.5f; - bufferbuilder.vertex(this.getX(), this.getY()+height, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width, this.getY()+height, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width, this.getY(), 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX(), this.getY(), 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); + float color = display ? this.isHovered() ? 1.0f : 0.8f : this.isHovered() ? 0.7f : 0.5f; + if (!enable.get()) { + color = 0.2f; + } + bufferbuilder.vertex(this.getX(), this.getY() + height, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width, this.getY() + height, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width, this.getY(), 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX(), this.getY(), 0.0D).color(color, color, color, 1.0f).endVertex(); + if (enable.get()) { + color = 0.7f; + } + bufferbuilder.vertex(this.getX() - 1, this.getY() + height + 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width + 1, this.getY() + height + 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width + 1, this.getY() - 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() - 1, this.getY() - 1, 0.0D).color(color, color, color, 1.0f).endVertex(); BufferUploader.drawWithShader(bufferbuilder.end()); - bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - RenderSystem.defaultBlendFunc(); - bufferbuilder.vertex(this.getX()-1, this.getY()+height+1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width+1, this.getY()+height+1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width+1, this.getY()-1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()-1, this.getY()-1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - BufferUploader.drawWithShader(bufferbuilder.end()); - int j = this.active ? 16777215 : 10526880; guiGraphics.drawCenteredString(font, this.getMessage(), this.getX() + this.width / 2, this.getY() + (this.height - 8) / 2, j | Mth.ceil(this.alpha * 255.0F) << 24); RenderSystem.disableBlend(); RenderSystem.defaultBlendFunc(); } + + public void shouDetail(GuiGraphics guiGraphics, Font font) { + if (detailMessage != null && isHovered()) { + CullingHandler.reColorToolTip = true; + List components = new ArrayList<>(); + String[] parts = detailMessage.get().getString().split("\\n"); + for (String part : parts) { + components.add(Component.literal(part)); + } + guiGraphics.renderComponentTooltip(font, components, this.getX() + this.width / 2, this.getY() + (this.height - 8) / 2); + CullingHandler.reColorToolTip = false; + } + } + + @Override + public void onRelease(double p_93609_, double p_93610_) { + super.onRelease(p_93609_, p_93610_); + this.setFocused(false); + } } diff --git a/src/main/java/rogo/renderingculling/gui/NeatSliderButton.java b/src/main/java/rogo/renderingculling/gui/NeatSliderButton.java index 1bdf6b9..acc9849 100644 --- a/src/main/java/rogo/renderingculling/gui/NeatSliderButton.java +++ b/src/main/java/rogo/renderingculling/gui/NeatSliderButton.java @@ -9,28 +9,38 @@ import net.minecraft.client.gui.components.AbstractOptionSliderButton; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; import net.minecraft.util.Mth; +import rogo.renderingculling.api.CullingHandler; +import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; public class NeatSliderButton extends AbstractOptionSliderButton { - private final Function updateMessage; + private final Function name; private final Consumer applyValue; - protected NeatSliderButton(int p_93380_, int p_93381_, int p_93382_, int p_93383_, double p_93384_, Function updateMessage, Consumer applyValue) { - super(Minecraft.getInstance().options, p_93380_, p_93381_, p_93382_, p_93383_, p_93384_); - this.updateMessage = updateMessage; + private Supplier detailMessage; + + protected NeatSliderButton(int p_93380_, int p_93381_, int p_93382_, int p_93383_, Supplier getter, Function setter, Function display, Supplier name) { + super(Minecraft.getInstance().options, p_93380_, p_93381_, p_93382_, p_93383_, getter.get()); + this.name = (sliderButton) -> name.get().append(": ").append(Component.literal(display.apply(this.value))); updateMessage(); - this.applyValue = applyValue; + this.applyValue = (value) -> this.value = setter.apply(value); } public double getValue() { return this.value; } + public void setDetailMessage(Supplier detailMessage) { + this.detailMessage = detailMessage; + } @Override public void updateMessage() { - this.setMessage(updateMessage.apply(this)); + this.setMessage(name.apply(this)); } @Override @@ -46,38 +56,48 @@ public void renderWidget(GuiGraphics guiGraphics, int p_93677_, int p_93678_, fl guiGraphics.drawCenteredString(font, this.getMessage(), this.getX() + this.width / 2, this.getY() + (this.height - 8) / 2, j | Mth.ceil(this.alpha * 255.0F) << 24); RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); RenderSystem.enableBlend(); - RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.DST_COLOR); + RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE_MINUS_DST_COLOR, GlStateManager.DestFactor.ZERO); RenderSystem.setShader(GameRenderer::getPositionColorShader); BufferBuilder bufferbuilder = Tesselator.getInstance().getBuilder(); bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - float alpha = this.isHoveredOrFocused() ? 0.7f : 0.5f; - bufferbuilder.vertex(this.getX(), this.getY()+height, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width, this.getY()+height, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width, this.getY(), 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX(), this.getY(), 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - BufferUploader.drawWithShader(bufferbuilder.end()); - bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - RenderSystem.defaultBlendFunc(); - bufferbuilder.vertex(this.getX()-1, this.getY()+height+1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width+1, this.getY()+height+1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()+width+1, this.getY()-1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX()-1, this.getY()-1, 0.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - BufferUploader.drawWithShader(bufferbuilder.end()); - RenderSystem.disableBlend(); + float color = this.isHovered() ? 1.0f : 0.8f; + bufferbuilder.vertex(this.getX(), this.getY() + height, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width, this.getY() + height, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width, this.getY(), 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX(), this.getY(), 0.0D).color(color, color, color, 1.0f).endVertex(); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0f); - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - RenderSystem.setShader(GameRenderer::getPositionColorShader); - bufferbuilder = Tesselator.getInstance().getBuilder(); - bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); - alpha = this.isHoveredOrFocused() ? 1.0f : 0.0f; - bufferbuilder.vertex(this.getX() + (int)(this.value * (double)(this.width - 8)), this.getY()+height, 90.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX() + (int)(this.value * (double)(this.width - 8))+8, this.getY()+height, 90.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX() + (int)(this.value * (double)(this.width - 8))+8, this.getY(), 90.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); - bufferbuilder.vertex(this.getX() + (int)(this.value * (double)(this.width - 8)), this.getY(), 90.0D).color(alpha, alpha, alpha, 0.5f).endVertex(); + color = 0.7f; + bufferbuilder.vertex(this.getX() - 1, this.getY() + height + 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width + 1, this.getY() + height + 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + width + 1, this.getY() - 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() - 1, this.getY() - 1, 0.0D).color(color, color, color, 1.0f).endVertex(); + + color = 1.0f; + bufferbuilder.vertex(this.getX() + (int) (this.value * (double) (this.width - 8)), this.getY() + height, 90.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + (int) (this.value * (double) (this.width - 8)) + 8, this.getY() + height, 90.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + (int) (this.value * (double) (this.width - 8)) + 8, this.getY(), 90.0D).color(color, color, color, 1.0f).endVertex(); + bufferbuilder.vertex(this.getX() + (int) (this.value * (double) (this.width - 8)), this.getY(), 90.0D).color(color, color, color, 1.0f).endVertex(); BufferUploader.drawWithShader(bufferbuilder.end()); RenderSystem.defaultBlendFunc(); RenderSystem.disableBlend(); } + + public void shouDetail(GuiGraphics guiGraphics, Font font) { + if (detailMessage != null && isHovered() && !isFocused()) { + CullingHandler.reColorToolTip = true; + List components = new ArrayList<>(); + String[] parts = detailMessage.get().getString().split("\\n"); + for (String part : parts) { + components.add(Component.literal(part)); + } + guiGraphics.renderComponentTooltip(font, components, this.getX() + this.width / 2, this.getY() + (this.height - 8) / 2); + CullingHandler.reColorToolTip = false; + } + } + + @Override + public void onRelease(double p_93609_, double p_93610_) { + super.onRelease(p_93609_, p_93610_); + this.setFocused(false); + } } diff --git a/src/main/java/rogo/renderingculling/mixin/AccessorLevelRender.java b/src/main/java/rogo/renderingculling/mixin/AccessorLevelRender.java index a1d20d0..909d37e 100644 --- a/src/main/java/rogo/renderingculling/mixin/AccessorLevelRender.java +++ b/src/main/java/rogo/renderingculling/mixin/AccessorLevelRender.java @@ -12,5 +12,8 @@ public interface AccessorLevelRender { @Accessor("capturedFrustum") Frustum getCapturedFrustum(); + + @Accessor("needsFullRenderChunkUpdate") + boolean getNeedsFullRenderChunkUpdate(); } diff --git a/src/main/java/rogo/renderingculling/mixin/MixinBlockEntityRender.java b/src/main/java/rogo/renderingculling/mixin/MixinBlockEntityRender.java index 56441de..c51a924 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinBlockEntityRender.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinBlockEntityRender.java @@ -11,7 +11,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import rogo.renderingculling.api.Config; import rogo.renderingculling.api.CullingHandler; @Mixin(BlockEntityRenderDispatcher.class) @@ -20,7 +19,7 @@ public class MixinBlockEntityRender { public void onShouldRender(E p_112268_, float p_112269_, PoseStack p_112270_, MultiBufferSource p_112271_, CallbackInfo ci) { AABB aabb = new AABB(p_112268_.getBlockPos()); aabb.inflate(Vec3.atCenterOf(p_112268_.getBlockPos()).distanceTo(Minecraft.getInstance().gameRenderer.getMainCamera().getPosition()) * 0.03125); - if(CullingHandler.INSTANCE.shouldSkipBlock(p_112268_, aabb, p_112268_.getBlockPos())) + if(CullingHandler.shouldSkipBlockEntity(p_112268_, aabb, p_112268_.getBlockPos())) ci.cancel(); } } diff --git a/src/main/java/rogo/renderingculling/mixin/MixinEntityRender.java b/src/main/java/rogo/renderingculling/mixin/MixinEntityRender.java index 56f8e21..1aeafff 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinEntityRender.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinEntityRender.java @@ -11,9 +11,9 @@ @Mixin(EntityRenderDispatcher.class) public class MixinEntityRender { - @Inject(method = "shouldRender", at=@At("RETURN"), cancellable = true) + @Inject(method = "shouldRender", at = @At("RETURN"), cancellable = true) public void onShouldRender(E p_114398_, Frustum p_114399_, double p_114400_, double p_114401_, double p_114402_, CallbackInfoReturnable cir) { - if(cir.getReturnValue() && !p_114398_.noCulling && CullingHandler.INSTANCE.shouldSkipEntity(p_114398_)) { + if (cir.getReturnValue() && !p_114398_.noCulling && CullingHandler.shouldSkipEntity(p_114398_)) { cir.setReturnValue(false); } } diff --git a/src/main/java/rogo/renderingculling/mixin/MixinGameRenderer.java b/src/main/java/rogo/renderingculling/mixin/MixinGameRenderer.java new file mode 100644 index 0000000..3c9e008 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/MixinGameRenderer.java @@ -0,0 +1,25 @@ +package rogo.renderingculling.mixin; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.GameRenderer; +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 rogo.renderingculling.api.CullingHandler; + +@Mixin(GameRenderer.class) +public abstract class MixinGameRenderer { + @Shadow + protected abstract double getFov(Camera p_109142_, float p_109143_, boolean p_109144_); + + @Shadow + public abstract Camera getMainCamera(); + + @Inject(method = "renderLevel", at = @At(value = "HEAD")) + public void afterRunTick(float p_109090_, long p_109091_, PoseStack p_109092_, CallbackInfo ci) { + CullingHandler.FOV = this.getFov(this.getMainCamera(), p_109090_, true); + } +} diff --git a/src/main/java/rogo/renderingculling/mixin/MixinInactiveProfiler.java b/src/main/java/rogo/renderingculling/mixin/MixinInactiveProfiler.java index 7d943c7..b24a707 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinInactiveProfiler.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinInactiveProfiler.java @@ -12,13 +12,11 @@ public class MixinInactiveProfiler { @Inject(method = "popPush(Ljava/lang/String;)V", at = @At(value = "HEAD")) public void onPopPush(String p_18395_, CallbackInfo ci) { - if(CullingHandler.INSTANCE != null) - CullingHandler.INSTANCE.onProfilerPopPush(p_18395_); + CullingHandler.onProfilerPopPush(p_18395_); } @Inject(method = "push(Ljava/lang/String;)V", at = @At(value = "HEAD")) public void onPush(String p_18395_, CallbackInfo ci) { - if(CullingHandler.INSTANCE != null) - CullingHandler.INSTANCE.onProfilerPush(p_18395_); + CullingHandler.onProfilerPush(p_18395_); } } diff --git a/src/main/java/rogo/renderingculling/mixin/MixinLevel.java b/src/main/java/rogo/renderingculling/mixin/MixinLevel.java deleted file mode 100644 index c9e1342..0000000 --- a/src/main/java/rogo/renderingculling/mixin/MixinLevel.java +++ /dev/null @@ -1,42 +0,0 @@ -package rogo.renderingculling.mixin; - -import net.minecraft.client.Minecraft; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.TickingBlockEntity; -import net.minecraft.world.phys.AABB; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import rogo.renderingculling.api.Config; -import rogo.renderingculling.api.CullingHandler; - -import java.util.function.Consumer; - -@Mixin(Level.class) -public abstract class MixinLevel { - - @Inject(method = "guardEntityTick", at=@At(value = "HEAD"), cancellable = true) - public void onEntityTick(Consumer p_46654_, T entity, CallbackInfo ci) { - if(!Config.getCullEntity() || (entity.level() instanceof ServerLevel)) return; - AABB aabb = entity.getBoundingBoxForCulling().inflate(0.5D); - if (aabb.hasNaN() || aabb.getSize() == 0.0D) { - aabb = new AABB(entity.getX() - 2.0D, entity.getY() - 2.0D, entity.getZ() - 2.0D, entity.getX() + 2.0D, entity.getY() + 2.0D, entity.getZ() + 2.0D); - } - if(CullingHandler.FRUSTUM != null && !CullingHandler.FRUSTUM.isVisible(aabb)) { - if(entity.tickCount % (20-Config.getCullingEntityRate()+1) != 0) { - entity.tickCount++; - ci.cancel(); - } - } else if(CullingHandler.INSTANCE.culledEntity.contains(entity)) { - if(entity.tickCount % (20-Config.getCullingEntityRate()+1) != 0) { - entity.tickCount++; - ci.cancel(); - } - } - } -} diff --git a/src/main/java/rogo/renderingculling/mixin/MixinLevelRender.java b/src/main/java/rogo/renderingculling/mixin/MixinLevelRender.java index 1cf65ec..dd0fb8c 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinLevelRender.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinLevelRender.java @@ -2,43 +2,62 @@ import com.mojang.blaze3d.vertex.PoseStack; import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.Camera; import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.ViewArea; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import net.minecraft.client.renderer.culling.Frustum; -import net.minecraft.world.entity.Entity; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import rogo.renderingculling.api.*; +import rogo.renderingculling.api.Config; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.impl.IEntitiesForRender; +import rogo.renderingculling.api.impl.IRenderChunkInfo; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; +import rogo.renderingculling.util.VanillaAsyncUtil; +import javax.annotation.Nullable; import java.lang.reflect.Field; +import java.util.LinkedHashSet; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicReference; @Mixin(LevelRenderer.class) -public class MixinLevelRender implements IEntitiesForRender { +public abstract class MixinLevelRender implements IEntitiesForRender { @Final @Shadow private ObjectArrayList renderChunksInFrustum; + @Shadow @Nullable protected abstract ChunkRenderDispatcher.RenderChunk getRelativeFrom(BlockPos p_109729_, ChunkRenderDispatcher.RenderChunk p_109730_, Direction p_109731_); + + @Shadow @Nullable private ViewArea viewArea; + + @Shadow @Final private AtomicReference renderChunkStorage; + @Inject(method = "applyFrustum", at = @At(value = "RETURN")) - public void onapplyFrustum(Frustum p_194355_, CallbackInfo ci) { - if (Config.getCullChunk()) { - if(CullingHandler.OptiFine != null) { + public void onApplyFrustum(Frustum p_194355_, CallbackInfo ci) { + if (Config.shouldCullChunk() && !Config.getAsyncChunkRebuild()) { + if (CullingHandler.OptiFine != null) { try { Field field = LevelRenderer.class.getDeclaredField("renderInfosTerrain"); field.setAccessible(true); Object value = field.get(this); if (value instanceof ObjectArrayList) { - ((ObjectArrayList)value).removeIf((o -> { + ((ObjectArrayList) value).removeIf((o -> { ChunkRenderDispatcher.RenderChunk chunk = ((IRenderChunkInfo) o).getRenderChunk(); - return !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) chunk, true); + return !CullingHandler.shouldRenderChunk((IRenderSectionVisibility) chunk, true); })); } } catch (NoSuchFieldException | IllegalAccessException ignored) { @@ -47,13 +66,41 @@ public void onapplyFrustum(Frustum p_194355_, CallbackInfo ci) { this.renderChunksInFrustum.removeIf((o -> { ChunkRenderDispatcher.RenderChunk chunk = ((IRenderChunkInfo) o).getRenderChunk(); - return !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) chunk, true); + return !CullingHandler.shouldRenderChunk((IRenderSectionVisibility) chunk, true); })); } } + @Inject(method = "updateRenderChunks", at = @At(value = "INVOKE", target = "Ljava/util/Queue;isEmpty()Z"), cancellable = true) + public void onUpdateRenderChunks(LinkedHashSet p_194363_, LevelRenderer.RenderInfoMap p_194364_, Vec3 p_194365_, Queue p_194366_, boolean p_194367_, CallbackInfo ci) { + if (Config.getAsyncChunkRebuild()) { + ci.cancel(); + } + } + + @Inject(method = "setupRender", at = @At(value = "HEAD")) + public void onSetupRenderHead(Camera p_194339_, Frustum p_194340_, boolean p_194341_, boolean p_194342_, CallbackInfo ci) { + if(this.viewArea != null) { + VanillaAsyncUtil.update((LevelRenderer) (Object)this, this.viewArea.chunks.length); + } + } + + @Inject(method = "setupRender", at = @At(value = "RETURN")) + public void onSetupRenderEnd(Camera p_194339_, Frustum p_194340_, boolean p_194341_, boolean p_194342_, CallbackInfo ci) { + if (Config.getAsyncChunkRebuild() && VanillaAsyncUtil.getChunkStorage() != null) { + this.renderChunkStorage.set(VanillaAsyncUtil.getChunkStorage()); + } + } + + @Inject(method = "initializeQueueForFullUpdate", at = @At(value = "HEAD"), cancellable = true) + public void onInitializeQueueForFullUpdate(Camera p_194344_, Queue p_194345_, CallbackInfo ci) { + if (Config.getAsyncChunkRebuild()) { + ci.cancel(); + } + } + @Inject(method = "prepareCullFrustum", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/culling/Frustum;(Lorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V")) - public void onapplyFrustum(PoseStack p_172962_, Vec3 p_172963_, Matrix4f p_172964_, CallbackInfo ci) { + public void onPrepareCullFrustum(PoseStack p_172962_, Vec3 p_172963_, Matrix4f p_172964_, CallbackInfo ci) { CullingHandler.PROJECTION_MATRIX = new Matrix4f(p_172964_); } @@ -61,5 +108,32 @@ public void onapplyFrustum(PoseStack p_172962_, Vec3 p_172963_, Matrix4f p_17296 public ObjectArrayList renderChunksInFrustum() { return renderChunksInFrustum; } + + @Override + public ChunkRenderDispatcher.RenderChunk invokeGetRelativeFrom(BlockPos pos, ChunkRenderDispatcher.RenderChunk chunk, Direction dir) { + return this.getRelativeFrom(pos, chunk, dir); + } + + @Override + public ChunkRenderDispatcher.RenderChunk invokeGetRenderChunkAt(BlockPos pos) { + return ((AccessorViewArea)this.viewArea).invokeGetRenderChunkAt(pos); + } + + @Mixin(LevelRenderer.RenderChunkInfo.class) + public interface AccessorRenderChunkInfo { + + @Accessor("directions") + byte getDirections(); + + @Accessor("step") + int getStep(); + } + + @Mixin(ViewArea.class) + public interface AccessorViewArea { + + @Invoker("getRenderChunkAt") + ChunkRenderDispatcher.RenderChunk invokeGetRenderChunkAt(BlockPos pos); + } } diff --git a/src/main/java/rogo/renderingculling/mixin/MixinMinecraft.java b/src/main/java/rogo/renderingculling/mixin/MixinMinecraft.java index 1d5dba5..f42a712 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinMinecraft.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinMinecraft.java @@ -7,18 +7,21 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import rogo.renderingculling.api.CullingHandler; -import rogo.renderingculling.event.WorldUnloadEvent; @Mixin(Minecraft.class) public class MixinMinecraft { - @Inject(method = "runTick", at = @At(value = "RETURN")) - public void onRender(boolean p_91384_, CallbackInfo ci) { - if(CullingHandler.INSTANCE != null) - CullingHandler.INSTANCE.onProfilerPopPush("afterRunTick"); + @Inject(method = "runTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;render(FJZ)V", shift = At.Shift.AFTER)) + public void afterRunTick(boolean p_91384_, CallbackInfo ci) { + CullingHandler.onProfilerPopPush("afterRunTick"); + } + + @Inject(method = "runTick", at = @At(value = "HEAD")) + public void beforeRunTick(boolean p_91384_, CallbackInfo ci) { + CullingHandler.onProfilerPopPush("beforeRunTick"); } @Inject(method = "setLevel", at = @At(value = "HEAD")) public void onJoinWorld(ClientLevel world, CallbackInfo ci) { - WorldUnloadEvent.WORLD_UNLOAD.invoker().onWorldUnload(world); + CullingHandler.onWorldUnload(world); } } diff --git a/src/main/java/rogo/renderingculling/mixin/MixinOcclusionCuller.java b/src/main/java/rogo/renderingculling/mixin/MixinOcclusionCuller.java deleted file mode 100644 index 1f3c15f..0000000 --- a/src/main/java/rogo/renderingculling/mixin/MixinOcclusionCuller.java +++ /dev/null @@ -1,21 +0,0 @@ -package rogo.renderingculling.mixin; - -import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; -import me.jellysquid.mods.sodium.client.render.chunk.occlusion.OcclusionCuller; -import me.jellysquid.mods.sodium.client.render.viewport.Viewport; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.*; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import rogo.renderingculling.api.Config; -import rogo.renderingculling.api.CullingHandler; -import rogo.renderingculling.api.IRenderSectionVisibility; - -@Mixin(OcclusionCuller.class) -public abstract class MixinOcclusionCuller { - - @Inject(method = "isSectionVisible", at = @At(value = "HEAD"), remap = false, cancellable = true) - private static void onIsSectionVisible(RenderSection section, Viewport viewport, float maxDistance, CallbackInfoReturnable cir) { - if(Config.getCullChunk()) - cir.setReturnValue(CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) section, true)); - } -} diff --git a/src/main/java/rogo/renderingculling/mixin/MixinRenderChunk.java b/src/main/java/rogo/renderingculling/mixin/MixinRenderChunk.java index f102ad5..263ecc5 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinRenderChunk.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinRenderChunk.java @@ -1,19 +1,19 @@ package rogo.renderingculling.mixin; -import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import net.minecraft.core.BlockPos; -import net.minecraft.world.phys.AABB; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; -import rogo.renderingculling.api.IRenderSectionVisibility; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; @Mixin(ChunkRenderDispatcher.RenderChunk.class) public abstract class MixinRenderChunk implements IRenderSectionVisibility { - @Shadow @Final private BlockPos.MutableBlockPos origin; + @Shadow + @Final + private BlockPos.MutableBlockPos origin; @Unique private int cullingLastVisibleFrame; diff --git a/src/main/java/rogo/renderingculling/mixin/MixinRenderChunkInfo.java b/src/main/java/rogo/renderingculling/mixin/MixinRenderChunkInfo.java index 21c7914..0dcfc16 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinRenderChunkInfo.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinRenderChunkInfo.java @@ -1,12 +1,13 @@ package rogo.renderingculling.mixin; +import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import rogo.renderingculling.api.IRenderChunkInfo; +import rogo.renderingculling.api.impl.IRenderChunkInfo; -@Mixin(targets = "net.minecraft.client.renderer.LevelRenderer$RenderChunkInfo") +@Mixin(LevelRenderer.RenderChunkInfo.class) public class MixinRenderChunkInfo implements IRenderChunkInfo { @Final diff --git a/src/main/java/rogo/renderingculling/mixin/MixinRenderSectionManager.java b/src/main/java/rogo/renderingculling/mixin/MixinRenderSectionManager.java deleted file mode 100644 index 29838d1..0000000 --- a/src/main/java/rogo/renderingculling/mixin/MixinRenderSectionManager.java +++ /dev/null @@ -1,41 +0,0 @@ -package rogo.renderingculling.mixin; - -import me.jellysquid.mods.sodium.client.render.chunk.ChunkUpdateType; -import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; -import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; -import me.jellysquid.mods.sodium.client.render.chunk.lists.SortedRenderLists; -import me.jellysquid.mods.sodium.client.render.chunk.lists.VisibleChunkCollector; -import me.jellysquid.mods.sodium.client.render.viewport.Viewport; -import net.minecraft.client.Camera; -import net.minecraft.core.BlockPos; -import org.jetbrains.annotations.NotNull; -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 rogo.renderingculling.api.Config; -import rogo.renderingculling.api.CullingHandler; -import rogo.renderingculling.api.IRenderSectionVisibility; - -import java.util.ArrayDeque; -import java.util.Map; -import java.util.Queue; - -@Mixin(RenderSectionManager.class) -public abstract class MixinRenderSectionManager { - - @Shadow protected abstract RenderSection getRenderSection(int x, int y, int z); - - @Shadow private @NotNull SortedRenderLists renderLists; - - @Shadow private @NotNull Map> rebuildLists; - - @Inject(method = "isSectionVisible", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/RenderSection;getLastVisibleFrame()I"), remap = false, locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) - private void onIsSectionVisible(int x, int y, int z, CallbackInfoReturnable cir, RenderSection section) { - if(Config.getCullChunk()) - cir.setReturnValue(CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) section, false)); - } -} \ No newline at end of file diff --git a/src/main/java/rogo/renderingculling/mixin/MixinShaderInstance.java b/src/main/java/rogo/renderingculling/mixin/MixinShaderInstance.java index 69021a1..b324725 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinShaderInstance.java +++ b/src/main/java/rogo/renderingculling/mixin/MixinShaderInstance.java @@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import rogo.renderingculling.api.CullingHandler; import rogo.renderingculling.api.CullingRenderEvent; -import rogo.renderingculling.api.ICullingShader; +import rogo.renderingculling.api.impl.ICullingShader; @Mixin(ShaderInstance.class) @@ -31,6 +31,10 @@ public Uniform getUniform(String p_173349_) { @Shadow private static String SHADER_CORE_PATH; + @Nullable + public Uniform CULLING_CAMERA_DIR; + @Nullable + public Uniform CULLING_FOV; @Nullable public Uniform CULLING_CAMERA_POS; @Nullable @@ -61,6 +65,8 @@ public Uniform getUniform(String p_173349_) { @Inject(at = @At("TAIL"), method = "") public void construct(CallbackInfo ci) { this.CULLING_CAMERA_POS = this.getUniform("CullingCameraPos"); + this.CULLING_CAMERA_DIR = this.getUniform("CullingCameraDir"); + this.CULLING_FOV = this.getUniform("CullingFov"); this.RENDER_DISTANCE = this.getUniform("RenderDistance"); this.DEPTH_SIZE = this.getUniform("DepthSize"); this.CULLING_SIZE = this.getUniform("CullingSize"); @@ -75,7 +81,7 @@ public void construct(CallbackInfo ci) { @ModifyArg(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/resources/ResourceLocation;(Ljava/lang/String;)V")) public String onInit(String string) { - if(CullingHandler.SHADER_ENABLED) { + if (CullingHandler.SHADER_ENABLED) { string = string.replace(SHADER_CORE_PATH, ""); string = string.replace(".json", ""); ResourceLocation rl = ResourceLocation.tryParse(string); @@ -86,12 +92,12 @@ public String onInit(String string) { @ModifyArg(method = "getOrCreate", at = @At(value = "INVOKE", target = "Lnet/minecraft/resources/ResourceLocation;(Ljava/lang/String;)V")) private static String onGetOrCreate(String string) { - if(CullingHandler.SHADER_ENABLED) { + if (CullingHandler.SHADER_ENABLED) { string = string.replace(SHADER_CORE_PATH, ""); Program.Type type = Program.Type.FRAGMENT; - if(string.contains(".fsh")) + if (string.contains(".fsh")) string = string.replace(".fsh", ""); - else if(string.contains(".vsh")) { + else if (string.contains(".vsh")) { string = string.replace(".vsh", ""); type = Program.Type.VERTEX; } @@ -102,7 +108,10 @@ else if(string.contains(".vsh")) { } @Override - public Uniform getCullingFrustum() {return CULLING_FRUSTUM;} + public Uniform getCullingFrustum() { + return CULLING_FRUSTUM; + } + @Override public Uniform getCullingCameraPos() { return CULLING_CAMERA_POS; @@ -117,38 +126,55 @@ public Uniform getRenderDistance() { public Uniform getDepthSize() { return DEPTH_SIZE; } + @Override public Uniform getCullingSize() { return CULLING_SIZE; } + @Override public Uniform getLevelHeightOffset() { return LEVEL_HEIGHT_OFFSET; } + @Override public Uniform getLevelMinSection() { return LEVEL_MIN_SECTION; } + @Override public Uniform getEntityCullingSize() { return ENTITY_CULLING_SIZE; } + @Override public Uniform getFrustumPos() { return FRUSTUM_POS; } + @Override public Uniform getCullingViewMat() { return CULLING_VIEW_MAT; } + @Override public Uniform getCullingProjMat() { return CULLING_PROJ_MAT; } + @Override + public Uniform getCullingCameraDir() { + return CULLING_CAMERA_DIR; + } + + @Override + public Uniform getCullingFov() { + return CULLING_FOV; + } + @Inject(at = @At("TAIL"), method = "apply") public void onApply(CallbackInfo ci) { - if(CullingHandler.updatingDepth) + if (CullingHandler.updatingDepth) ProgramManager.glUseProgram(this.programId); } diff --git a/src/main/java/rogo/renderingculling/mixin/fabric/MixinKeyboardHandler.java b/src/main/java/rogo/renderingculling/mixin/fabric/MixinKeyboardHandler.java index d2648b9..83d2346 100644 --- a/src/main/java/rogo/renderingculling/mixin/fabric/MixinKeyboardHandler.java +++ b/src/main/java/rogo/renderingculling/mixin/fabric/MixinKeyboardHandler.java @@ -17,7 +17,7 @@ public class MixinKeyboardHandler { @Inject(method = "keyPress", at= @At(value = "RETURN")) void onKeyPress(long l, int i, int j, int k, int m, CallbackInfo ci) { if (l == Minecraft.getInstance().getWindow().getWindow()) { - CullingHandler.INSTANCE.onProfilerPush("onKeyboardInput"); + CullingHandler.onProfilerPush("onKeyboardInput"); } } } diff --git a/src/main/java/rogo/renderingculling/mixin/fabric/MixinTooltipRenderUtil.java b/src/main/java/rogo/renderingculling/mixin/fabric/MixinTooltipRenderUtil.java new file mode 100644 index 0000000..30fe721 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/fabric/MixinTooltipRenderUtil.java @@ -0,0 +1,47 @@ +package rogo.renderingculling.mixin.fabric; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.ModLoader; + +@Mixin(TooltipRenderUtil.class) +public class MixinTooltipRenderUtil { + + @ModifyConstant( + method = "renderTooltipBackground", + constant = @Constant(intValue = -267386864) + ) + private static int onBG(int constant) { + if (CullingHandler.reColorToolTip) { + return ModLoader.getBG(); + } + return constant; + } + + @ModifyConstant( + method = "renderTooltipBackground", + constant = @Constant(intValue = 1347420415) + ) + private static int onBT(int constant) { + if (CullingHandler.reColorToolTip) { + return ModLoader.getB(); + } + return constant; + } + + @ModifyConstant( + method = "renderTooltipBackground", + constant = @Constant(intValue = 1344798847) + ) + private static int onBB(int constant) { + if (CullingHandler.reColorToolTip) { + return ModLoader.getB(); + } + return constant; + } +} diff --git a/src/main/java/rogo/renderingculling/mixin/sodium/AccessorRenderSectionManager.java b/src/main/java/rogo/renderingculling/mixin/sodium/AccessorRenderSectionManager.java new file mode 100644 index 0000000..f56fbd4 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/sodium/AccessorRenderSectionManager.java @@ -0,0 +1,17 @@ +package rogo.renderingculling.mixin.sodium; + +import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; +import net.minecraft.client.Camera; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(RenderSectionManager.class) +public interface AccessorRenderSectionManager { + + @Invoker(value = "getSearchDistance", remap = false) + float invokeSearchDistance(); + + @Invoker(value = "shouldUseOcclusionCulling", remap = false) + boolean invokeShouldUseOcclusionCulling(Camera camera, boolean spectator); +} + diff --git a/src/main/java/rogo/renderingculling/mixin/sodium/MixinOcclusionCuller.java b/src/main/java/rogo/renderingculling/mixin/sodium/MixinOcclusionCuller.java new file mode 100644 index 0000000..fc440e3 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/sodium/MixinOcclusionCuller.java @@ -0,0 +1,33 @@ +package rogo.renderingculling.mixin.sodium; + +import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; +import me.jellysquid.mods.sodium.client.render.chunk.occlusion.OcclusionCuller; +import me.jellysquid.mods.sodium.client.render.viewport.Viewport; +import org.spongepowered.asm.mixin.Mixin; +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 rogo.renderingculling.api.Config; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; +import rogo.renderingculling.util.OcclusionCullerThread; +import rogo.renderingculling.util.SodiumSectionAsyncUtil; + +@Mixin(OcclusionCuller.class) +public abstract class MixinOcclusionCuller { + + @Inject(method = "isSectionVisible", at = @At(value = "RETURN"), remap = false, cancellable = true) + private static void onIsSectionVisible(RenderSection section, Viewport viewport, float maxDistance, CallbackInfoReturnable cir) { + if (Config.shouldCullChunk() && cir.getReturnValue() && !CullingHandler.shouldRenderChunk((IRenderSectionVisibility) section, true)) + cir.setReturnValue(false); + } + + @Inject(method = "findVisible", at = @At(value = "HEAD"), remap = false, cancellable = true) + private void onFindVisible(OcclusionCuller.Visitor visitor, Viewport viewport, float searchDistance, boolean useOcclusionCulling, int frame, CallbackInfo ci) { + if (SodiumSectionAsyncUtil.asyncSearching && Thread.currentThread() != OcclusionCullerThread.INSTANCE) { + SodiumSectionAsyncUtil.asyncSearching = false; + ci.cancel(); + } + } +} \ No newline at end of file diff --git a/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderRegion.java b/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderRegion.java new file mode 100644 index 0000000..9c8b6f9 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderRegion.java @@ -0,0 +1,24 @@ +package rogo.renderingculling.mixin.sodium; + +import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; +import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion; +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 rogo.renderingculling.util.SodiumSectionAsyncUtil; + +@Mixin(RenderRegion.class) +public abstract class MixinRenderRegion { + @Shadow(remap = false) + @Final + private RenderSection[] sections; + + @Inject(method = "getSection", at = @At("HEAD"), cancellable = true, remap = false) + private void onGetSection(int id, CallbackInfoReturnable cir) { + if (SodiumSectionAsyncUtil.renderingEntities && this.sections[id] == null) + cir.setReturnValue(new RenderSection((RenderRegion) (Object) this, 0, 0, 0)); + } +} diff --git a/src/main/java/rogo/renderingculling/mixin/MixinRenderSection.java b/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderSection.java similarity index 73% rename from src/main/java/rogo/renderingculling/mixin/MixinRenderSection.java rename to src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderSection.java index 925bbf3..f3560d1 100644 --- a/src/main/java/rogo/renderingculling/mixin/MixinRenderSection.java +++ b/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderSection.java @@ -1,19 +1,22 @@ -package rogo.renderingculling.mixin; +package rogo.renderingculling.mixin.sodium; import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; -import rogo.renderingculling.api.IRenderSectionVisibility; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; @Mixin(RenderSection.class) public abstract class MixinRenderSection implements IRenderSectionVisibility { - @Shadow public abstract int getOriginX(); + @Shadow(remap = false) + public abstract int getOriginX(); - @Shadow public abstract int getOriginY(); + @Shadow(remap = false) + public abstract int getOriginY(); - @Shadow public abstract int getOriginZ(); + @Shadow(remap = false) + public abstract int getOriginZ(); @Unique private int cullingLastVisibleFrame; diff --git a/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderSectionManager.java b/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderSectionManager.java new file mode 100644 index 0000000..e43ef6b --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/sodium/MixinRenderSectionManager.java @@ -0,0 +1,50 @@ +package rogo.renderingculling.mixin.sodium; + +import it.unimi.dsi.fastutil.longs.Long2ReferenceMap; +import me.jellysquid.mods.sodium.client.gl.device.CommandList; +import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; +import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; +import me.jellysquid.mods.sodium.client.render.chunk.lists.VisibleChunkCollector; +import net.minecraft.client.multiplayer.ClientLevel; +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.ModifyVariable; +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 rogo.renderingculling.api.Config; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; +import rogo.renderingculling.util.SodiumSectionAsyncUtil; + +@Mixin(RenderSectionManager.class) +public abstract class MixinRenderSectionManager { + + @Shadow(remap = false) + @Final + private Long2ReferenceMap sectionByPosition; + + @Inject(method = "", at = @At("TAIL")) + private void init(ClientLevel world, int renderDistance, CommandList commandList, CallbackInfo ci) { + SodiumSectionAsyncUtil.fromSectionManager(this.sectionByPosition, world); + } + + @Inject(method = "isSectionVisible", at = @At(value = "RETURN"), remap = false, locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) + private void onIsSectionVisible(int x, int y, int z, CallbackInfoReturnable cir, RenderSection section) { + if (Config.shouldCullChunk()) + cir.setReturnValue(CullingHandler.shouldRenderChunk((IRenderSectionVisibility) section, false)); + } + + @ModifyVariable(name = "visitor", method = "createTerrainRenderList", at = @At(value = "INVOKE", target = "Lme/jellysquid/mods/sodium/client/render/chunk/occlusion/OcclusionCuller;findVisible(Lme/jellysquid/mods/sodium/client/render/chunk/occlusion/OcclusionCuller$Visitor;Lme/jellysquid/mods/sodium/client/render/viewport/Viewport;FZI)V", shift = At.Shift.BEFORE), remap = false) + private VisibleChunkCollector onCreateTerrainRenderList(VisibleChunkCollector value) { + if (Config.getAsyncChunkRebuild() && !CullingHandler.needPauseRebuild()) { + SodiumSectionAsyncUtil.asyncSearching = true; + VisibleChunkCollector collector = CullingHandler.renderingIris() ? SodiumSectionAsyncUtil.getShadowCollector() : SodiumSectionAsyncUtil.getChunkCollector(); + return collector == null ? value : collector; + } + return value; + } +} \ No newline at end of file diff --git a/src/main/java/rogo/renderingculling/mixin/sodium/MixinSodiumWorldRenderer.java b/src/main/java/rogo/renderingculling/mixin/sodium/MixinSodiumWorldRenderer.java new file mode 100644 index 0000000..667bb55 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/sodium/MixinSodiumWorldRenderer.java @@ -0,0 +1,46 @@ +package rogo.renderingculling.mixin.sodium; + +import com.mojang.blaze3d.vertex.PoseStack; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; +import me.jellysquid.mods.sodium.client.render.chunk.RenderSectionManager; +import me.jellysquid.mods.sodium.client.render.viewport.Viewport; +import net.minecraft.client.Camera; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher; +import net.minecraft.server.level.BlockDestructionProgress; +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 rogo.renderingculling.api.Config; +import rogo.renderingculling.util.SodiumSectionAsyncUtil; + +import java.util.SortedSet; + +@Mixin(SodiumWorldRenderer.class) +public abstract class MixinSodiumWorldRenderer { + + @Shadow(remap = false) + private RenderSectionManager renderSectionManager; + + @Inject(method = "setupTerrain", at = @At(value = "HEAD"), remap = false) + public void injectTerrainSetup(Camera camera, Viewport viewport, int frame, boolean spectator, boolean updateChunksImmediately, CallbackInfo ci) { + if (Config.shouldCullChunk()) { + SodiumSectionAsyncUtil.update(viewport, ((AccessorRenderSectionManager) this.renderSectionManager).invokeSearchDistance() + , ((AccessorRenderSectionManager) this.renderSectionManager).invokeShouldUseOcclusionCulling(camera, spectator)); + } + } + + @Inject(method = "renderBlockEntities(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/RenderBuffers;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;FLnet/minecraft/client/renderer/MultiBufferSource$BufferSource;DDDLnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;)V", at = @At(value = "HEAD"), remap = false) + public void beforeRenderBlockEntities(PoseStack matrices, RenderBuffers bufferBuilders, Long2ObjectMap> blockBreakingProgressions, float tickDelta, MultiBufferSource.BufferSource immediate, double x, double y, double z, BlockEntityRenderDispatcher blockEntityRenderer, CallbackInfo ci) { + SodiumSectionAsyncUtil.renderingEntities = true; + } + + @Inject(method = "renderBlockEntities(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/RenderBuffers;Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;FLnet/minecraft/client/renderer/MultiBufferSource$BufferSource;DDDLnet/minecraft/client/renderer/blockentity/BlockEntityRenderDispatcher;)V", at = @At(value = "RETURN"), remap = false) + public void afterRenderBlockEntities(PoseStack matrices, RenderBuffers bufferBuilders, Long2ObjectMap> blockBreakingProgressions, float tickDelta, MultiBufferSource.BufferSource immediate, double x, double y, double z, BlockEntityRenderDispatcher blockEntityRenderer, CallbackInfo ci) { + SodiumSectionAsyncUtil.renderingEntities = false; + } +} \ No newline at end of file diff --git a/src/main/java/rogo/renderingculling/mixin/sodium/MixinVisibleChunkCollector.java b/src/main/java/rogo/renderingculling/mixin/sodium/MixinVisibleChunkCollector.java new file mode 100644 index 0000000..42fcc73 --- /dev/null +++ b/src/main/java/rogo/renderingculling/mixin/sodium/MixinVisibleChunkCollector.java @@ -0,0 +1,27 @@ +package rogo.renderingculling.mixin.sodium; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; +import me.jellysquid.mods.sodium.client.render.chunk.lists.ChunkRenderList; +import me.jellysquid.mods.sodium.client.render.chunk.lists.VisibleChunkCollector; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import rogo.renderingculling.api.impl.ICollectorAccessor; + +@Mixin(VisibleChunkCollector.class) +public abstract class MixinVisibleChunkCollector implements ICollectorAccessor { + @Shadow(remap = false) @Final private ObjectArrayList sortedRenderLists; + + @Shadow(remap = false) protected abstract void addToRebuildLists(RenderSection section); + + @Override + public void addAsyncToRebuildLists(RenderSection renderSection) { + this.addToRebuildLists(renderSection); + } + + @Override + public void addRenderList(ChunkRenderList renderList) { + this.sortedRenderLists.add(renderList); + } +} diff --git a/src/main/java/rogo/renderingculling/util/IrisLoaderImpl.java b/src/main/java/rogo/renderingculling/util/IrisLoaderImpl.java index b847498..86f6a0a 100644 --- a/src/main/java/rogo/renderingculling/util/IrisLoaderImpl.java +++ b/src/main/java/rogo/renderingculling/util/IrisLoaderImpl.java @@ -1,7 +1,5 @@ package rogo.renderingculling.util; -/* -import com.mojang.blaze3d.platform.GlStateManager; import net.coderbot.iris.Iris; import net.coderbot.iris.gl.framebuffer.GlFramebuffer; import net.coderbot.iris.pipeline.SodiumTerrainPipeline; @@ -38,7 +36,12 @@ public int getFrameBufferID() { } @Override - public boolean renderingShader() { + public boolean renderingShaderPass() { + return IrisApi.getInstance().isRenderingShadowPass(); + } + + @Override + public boolean enabledShader() { return IrisApi.getInstance().isShaderPackInUse(); } @@ -46,5 +49,4 @@ public boolean renderingShader() { public void bindDefaultFrameBuffer() { Minecraft.getInstance().getMainRenderTarget().bindWrite(true); } -} - */ \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/java/rogo/renderingculling/util/OcclusionCullerThread.java b/src/main/java/rogo/renderingculling/util/OcclusionCullerThread.java new file mode 100644 index 0000000..f759c22 --- /dev/null +++ b/src/main/java/rogo/renderingculling/util/OcclusionCullerThread.java @@ -0,0 +1,41 @@ +package rogo.renderingculling.util; + +import net.minecraft.client.Minecraft; +import rogo.renderingculling.api.Config; +import rogo.renderingculling.api.CullingHandler; + +public class OcclusionCullerThread extends Thread { + public static OcclusionCullerThread INSTANCE; + private boolean finished = false; + + public OcclusionCullerThread() { + if (INSTANCE != null) { + INSTANCE.finished = true; + } + INSTANCE = this; + } + + @Override + public void run() { + while (!finished) { + try { + if (CullingHandler.CHUNK_CULLING_MAP != null && CullingHandler.CHUNK_CULLING_MAP.isDone()) { + if (Config.getAsyncChunkRebuild()) { + //CullingHandler.CHUNK_CULLING_MAP.updateVisibleChunks(); + if (CullingHandler.hasSodium()) { + SodiumSectionAsyncUtil.asyncSearchRebuildSection(); + } else { + //VanillaAsyncUtil.asyncSearchRebuildSection(); + } + } + } + + if (Minecraft.getInstance().level == null) { + finished = true; + } + } catch (Exception ignored) { + + } + } + } +} diff --git a/src/main/java/rogo/renderingculling/util/OptiFineLoaderImpl.java b/src/main/java/rogo/renderingculling/util/OptiFineLoaderImpl.java index 615613c..d9c4c10 100644 --- a/src/main/java/rogo/renderingculling/util/OptiFineLoaderImpl.java +++ b/src/main/java/rogo/renderingculling/util/OptiFineLoaderImpl.java @@ -26,7 +26,7 @@ public int getFrameBufferID() { } @Override - public boolean renderingShader() { + public boolean renderingShaderPass() { try { Field field = CullingHandler.OptiFine.getDeclaredField("shaderPackLoaded"); field.setAccessible(true); @@ -37,6 +37,11 @@ public boolean renderingShader() { return false; } + @Override + public boolean enabledShader() { + return renderingShaderPass(); + } + @Override public void bindDefaultFrameBuffer() { if (glState == null) { diff --git a/src/main/java/rogo/renderingculling/util/ShaderLoader.java b/src/main/java/rogo/renderingculling/util/ShaderLoader.java index f440210..3c218bd 100644 --- a/src/main/java/rogo/renderingculling/util/ShaderLoader.java +++ b/src/main/java/rogo/renderingculling/util/ShaderLoader.java @@ -3,7 +3,9 @@ public interface ShaderLoader { int getFrameBufferID(); - boolean renderingShader(); + boolean renderingShaderPass(); + + boolean enabledShader(); void bindDefaultFrameBuffer(); } diff --git a/src/main/java/rogo/renderingculling/util/SodiumSectionAsyncUtil.java b/src/main/java/rogo/renderingculling/util/SodiumSectionAsyncUtil.java new file mode 100644 index 0000000..45ac405 --- /dev/null +++ b/src/main/java/rogo/renderingculling/util/SodiumSectionAsyncUtil.java @@ -0,0 +1,122 @@ +package rogo.renderingculling.util; + +import it.unimi.dsi.fastutil.longs.Long2ReferenceMap; +import me.jellysquid.mods.sodium.client.render.chunk.ChunkUpdateType; +import me.jellysquid.mods.sodium.client.render.chunk.RenderSection; +import me.jellysquid.mods.sodium.client.render.chunk.lists.ChunkRenderList; +import me.jellysquid.mods.sodium.client.render.chunk.lists.VisibleChunkCollector; +import me.jellysquid.mods.sodium.client.render.chunk.occlusion.OcclusionCuller; +import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion; +import me.jellysquid.mods.sodium.client.render.viewport.Viewport; +import net.minecraft.world.level.Level; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.impl.ICollectorAccessor; + +import java.util.ArrayDeque; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +public class SodiumSectionAsyncUtil { + private static int frame = 0; + private static OcclusionCuller occlusionCuller; + private static Viewport viewport; + private static float searchDistance; + private static boolean useOcclusionCulling; + private static Viewport shadowViewport; + private static float shadowSearchDistance; + private static boolean shadowUseOcclusionCulling; + private static volatile boolean shouldSearch = true; + private static VisibleChunkCollector collector; + private static VisibleChunkCollector shadowCollector; + public static boolean renderingEntities; + public static boolean asyncSearching; + + public static void fromSectionManager(Long2ReferenceMap sections, Level world) { + SodiumSectionAsyncUtil.occlusionCuller = new OcclusionCuller(sections, world); + } + + public static void asyncSearchRebuildSection() { + if(CullingHandler.enabledShader() && shadowViewport != null) { + frame++; + CullingHandler.useOcclusionCulling = false; + AsynchronousChunkCollector shadowCollector = new AsynchronousChunkCollector(frame); + occlusionCuller.findVisible(shadowCollector, shadowViewport, shadowSearchDistance, shadowUseOcclusionCulling, frame); + SodiumSectionAsyncUtil.shadowCollector = shadowCollector; + CullingHandler.useOcclusionCulling = true; + } + + if (viewport != null) { + frame++; + AsynchronousChunkCollector collector = new AsynchronousChunkCollector(frame); + occlusionCuller.findVisible(collector, viewport, searchDistance, useOcclusionCulling, frame); + SodiumSectionAsyncUtil.collector = collector; + } + CullingHandler.CHUNK_CULLING_MAP.queueUpdateCount++; + } + + public static void update(Viewport viewport, float searchDistance, boolean useOcclusionCulling) { + if(CullingHandler.renderingShader()) { + SodiumSectionAsyncUtil.shadowViewport = viewport; + SodiumSectionAsyncUtil.shadowSearchDistance = searchDistance; + SodiumSectionAsyncUtil.shadowUseOcclusionCulling = useOcclusionCulling; + } else { + SodiumSectionAsyncUtil.viewport = viewport; + SodiumSectionAsyncUtil.searchDistance = searchDistance; + SodiumSectionAsyncUtil.useOcclusionCulling = useOcclusionCulling; + } + } + + public static VisibleChunkCollector getChunkCollector() { + return SodiumSectionAsyncUtil.collector; + } + + public static VisibleChunkCollector getShadowCollector() { + return SodiumSectionAsyncUtil.shadowCollector; + } + + public static class AsynchronousChunkCollector extends VisibleChunkCollector { + private final HashMap renderListMap = new HashMap<>(); + private final EnumMap> syncRebuildLists; + + public AsynchronousChunkCollector(int frame) { + super(frame); + this.syncRebuildLists = new EnumMap<>(ChunkUpdateType.class); + ChunkUpdateType[] var2 = ChunkUpdateType.values(); + + for (ChunkUpdateType type : var2) { + this.syncRebuildLists.put(type, new ArrayDeque<>()); + } + } + + @Override + public void visit(RenderSection section, boolean visible) { + if (visible && section.getFlags() != 0) { + RenderRegion region = section.getRegion(); + ChunkRenderList renderList; + if (!renderListMap.containsKey(region)) { + renderList = new ChunkRenderList(region); + ((ICollectorAccessor) this).addRenderList(renderList); + renderListMap.put(region, renderList); + } else { + renderList = renderListMap.get(region); + } + renderList.add(section); + } + + ((ICollectorAccessor) this).addAsyncToRebuildLists(section); + } + + @Override + public Map> getRebuildLists() { + super.getRebuildLists().forEach(((chunkUpdateType, renderSections) -> { + for(RenderSection section : renderSections) { + if (!section.isDisposed()) { + syncRebuildLists.get(chunkUpdateType).add(section); + } + } + })); + return syncRebuildLists; + } + } +} \ No newline at end of file diff --git a/src/main/java/rogo/renderingculling/util/VanillaAsyncUtil.java b/src/main/java/rogo/renderingculling/util/VanillaAsyncUtil.java new file mode 100644 index 0000000..c90a54a --- /dev/null +++ b/src/main/java/rogo/renderingculling/util/VanillaAsyncUtil.java @@ -0,0 +1,72 @@ +package rogo.renderingculling.util; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import rogo.renderingculling.api.CullingHandler; +import rogo.renderingculling.api.impl.IEntitiesForRender; +import rogo.renderingculling.api.impl.IRenderChunkInfo; +import rogo.renderingculling.api.impl.IRenderSectionVisibility; +import rogo.renderingculling.mixin.MixinLevelRender; + +import java.util.ArrayDeque; +import java.util.Queue; + +import static net.minecraft.client.renderer.LevelRenderer.DIRECTIONS; + +public class VanillaAsyncUtil { + private static int chunkLength = 0; + private static LevelRenderer.RenderChunkStorage storage; + private static LevelRenderer levelRenderer; + + public static void asyncSearchRebuildSection() { + if(levelRenderer == null) return; + LevelRenderer.RenderChunkStorage renderChunkStorage = new LevelRenderer.RenderChunkStorage(chunkLength); + Queue queue = new ArrayDeque<>(); + BlockPos origin = getOriginPos(); + LevelRenderer.RenderChunkInfo originChunk = new LevelRenderer.RenderChunkInfo(((IEntitiesForRender) levelRenderer).invokeGetRenderChunkAt(origin), null, 0); + queue.add(originChunk); + double range = Minecraft.getInstance().options.getEffectiveRenderDistance() * 16; + while (!queue.isEmpty()) { + LevelRenderer.RenderChunkInfo last = queue.poll(); + ChunkRenderDispatcher.RenderChunk lastRenderChunk = ((IRenderChunkInfo) last).getRenderChunk(); + if(originChunk != last && !lastRenderChunk.getOrigin().closerThan(origin, range) && (!CullingHandler.FRUSTUM.isVisible(lastRenderChunk.getBoundingBox()) || !CullingHandler.shouldRenderChunk((IRenderSectionVisibility)lastRenderChunk, false))) continue; + renderChunkStorage.renderChunks.add(last); + + for (Direction direction : DIRECTIONS) { + ChunkRenderDispatcher.RenderChunk sideRenderChunk = ((IEntitiesForRender) levelRenderer).invokeGetRelativeFrom(origin, lastRenderChunk, direction); + if (sideRenderChunk != null && (!last.hasDirection(direction.getOpposite()))) { + LevelRenderer.RenderChunkInfo newRenderChunk = new LevelRenderer.RenderChunkInfo(sideRenderChunk, direction, ((MixinLevelRender.AccessorRenderChunkInfo) last).getStep() + 1); + newRenderChunk.setDirections(((MixinLevelRender.AccessorRenderChunkInfo) last).getDirections(), direction); + queue.add(newRenderChunk); + renderChunkStorage.renderInfoMap.put(sideRenderChunk, newRenderChunk); + } + } + } + storage = renderChunkStorage; + } + + public static void update(LevelRenderer renderer, int length) { + levelRenderer = renderer; + chunkLength = length; + } + + public static LevelRenderer.RenderChunkStorage getChunkStorage() { + return storage; + } + + @NotNull + private static BlockPos getOriginPos() { + BlockPos origin = Minecraft.getInstance().gameRenderer.getMainCamera().getBlockPosition(); + if (origin.getY() < Minecraft.getInstance().level.getMinBuildHeight()) { + origin = new BlockPos(origin.getX(), Minecraft.getInstance().level.getMinBuildHeight(), origin.getZ()); + } else if (origin.getY() > Minecraft.getInstance().level.getMaxBuildHeight()) { + origin = new BlockPos(origin.getX(), Minecraft.getInstance().level.getMaxBuildHeight(), origin.getZ()); + } + return origin; + } +} \ No newline at end of file diff --git a/src/main/resources/assets/brute_force_rendering_culling/lang/en_us.json b/src/main/resources/assets/brute_force_rendering_culling/lang/en_us.json index d1bfdee..30d62e6 100644 --- a/src/main/resources/assets/brute_force_rendering_culling/lang/en_us.json +++ b/src/main/resources/assets/brute_force_rendering_culling/lang/en_us.json @@ -4,7 +4,8 @@ "brute_force_rendering_culling.key.debug": "Debug UI", "brute_force_rendering_culling.cull_entity": "Cull Entity", - "brute_force_rendering_culling.cull_chunk": "Cull chunk", + "brute_force_rendering_culling.cull_chunk": "Cull Chunk", + "brute_force_rendering_culling.async": "Async Rebuild Chunk", "brute_force_rendering_culling.sampler": "Depth Sampler Rate", "brute_force_rendering_culling.culling_map_update_delay": "Culling Map update delay", "brute_force_rendering_culling.entity_culling": "Culled entity", @@ -16,13 +17,15 @@ "brute_force_rendering_culling.chunk_culling": "Culled chunk", "brute_force_rendering_culling.chunk_culling_time": "Chunk culling cost", - "brute_force_rendering_culling.chunk_update_count": "Chunk Update Count", - - "brute_force_rendering_culling.culling_entity_update_rate": "Culled entity update frequency", + "brute_force_rendering_culling.chunk_update_count": "Async Chunk Update Count", "brute_force_rendering_culling.enable": "Enable", "brute_force_rendering_culling.disable": "Disable", - - "brute_force_rendering_culling.skip_culling_entities": "Entities that skip culling", - "brute_force_rendering_culling.skip_culling_block_entities": "Block Entities that skip culling" + "brute_force_rendering_culling.detail.cull_entity": "Cull entities visually invisible", + "brute_force_rendering_culling.detail.cull_chunk": "Cull chunks visually invisible", + "brute_force_rendering_culling.detail.async": "Requires Sodium support, asynchronously updates visible chunks in a separate thread and synchronizes with the main rendering thread. \n\nYou can increase the number of threads for loading blocks in the game settings to assist with this functionality.\n\nThis can accelerate chunk loading speed during movement and improve frame rates.\n\nIn the testing phase, if the game crashes during chunk loading, please try turning off this setting.", + "brute_force_rendering_culling.detail.sampler": "Scaling ratio of the depth texture used for culling. Higher ratios result in more precise culling and better effects. \n\nDevices with better graphics card performance or lower game resolutions can increase this value. Not recommended to be below 20%.", + "brute_force_rendering_culling.detail.culling_map_update_delay": "Frames delayed before culling texture updates to the CPU, preventing main thread stutter caused by incomplete data transmission. \n\nDebug by observing culling map update times.\n\nEnabling shaders will slow down data transmission, resulting in at least 1 frame delay.", + "brute_force_rendering_culling.detail.check_texture": "For testing purposes, enables checking of depth and culling texture functionality when the data monitoring panel is open.", + "brute_force_rendering_culling.detail.debug": "For testing purposes, effective only in vanilla Minecraft, used to view culled chunks." } \ No newline at end of file diff --git a/src/main/resources/assets/brute_force_rendering_culling/lang/zh_cn.json b/src/main/resources/assets/brute_force_rendering_culling/lang/zh_cn.json index 01fbd9b..6cea091 100644 --- a/src/main/resources/assets/brute_force_rendering_culling/lang/zh_cn.json +++ b/src/main/resources/assets/brute_force_rendering_culling/lang/zh_cn.json @@ -5,6 +5,7 @@ "brute_force_rendering_culling.cull_entity": "剔除实体", "brute_force_rendering_culling.cull_chunk": "剔除区块", + "brute_force_rendering_culling.async": "异步构建区块", "brute_force_rendering_culling.sampler": "深度采样率", "brute_force_rendering_culling.culling_map_update_delay": "剔除图更新推迟", "brute_force_rendering_culling.entity_culling": "实体剔除", @@ -16,12 +17,15 @@ "brute_force_rendering_culling.chunk_culling": "区块剔除", "brute_force_rendering_culling.chunk_culling_time": "区块剔除耗时", - - "brute_force_rendering_culling.culling_entity_update_rate": "被剔除实体更新频率", + "brute_force_rendering_culling.chunk_update_count": "异步区块更新次数", "brute_force_rendering_culling.enable": "开启", "brute_force_rendering_culling.disable": "关闭", - - "brute_force_rendering_culling.skip_culling_entities": "不被剔除的实体", - "brute_force_rendering_culling.skip_culling_block_entities": "不被剔除的方块实体" + "brute_force_rendering_culling.detail.cull_entity": "剔除视觉上不可见的实体", + "brute_force_rendering_culling.detail.cull_chunk": "剔除视觉上不可见的区块", + "brute_force_rendering_culling.detail.async": "需要Sodium支持,使用独立线程异步更新可见区块,并同步到主渲染线程。\n\n可以适当提高游戏设置的多线程加载区块数量辅助此功能。\n\n可以加速移动时的区块构建速度,提升帧数。\n\n处于测试阶段,如在加载区块时崩溃请尝试关掉此设置。", + "brute_force_rendering_culling.detail.sampler": "剔除所用深度纹理的缩放比例,比例越高剔除越精准、效果越好。\n\n显卡性能较好的设备、游戏分辨率较低的设备可以拉高此数值。不建议低于20%。", + "brute_force_rendering_culling.detail.culling_map_update_delay": "剔除纹理更新到CPU之前所推迟的帧数,防止数据未完全传输造成主线程卡顿。\n\n可观察剔除图更新耗时进行调试。\n\n开启光影会使数据传输变慢,至少会有1帧延迟。", + "brute_force_rendering_culling.detail.check_texture": "测试用,开启数据监控面板时可以查看深度纹理与剔除纹理工作是否正常。", + "brute_force_rendering_culling.detail.debug": "测试用,仅在原版生效,用于查看被剔除区块。" } \ No newline at end of file diff --git a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.fsh b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.fsh index 4e34702..b4fec29 100644 --- a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.fsh +++ b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.fsh @@ -9,6 +9,8 @@ uniform vec2 CullingSize; uniform mat4 CullingViewMat; uniform mat4 CullingProjMat; uniform vec3 CullingCameraPos; +uniform vec3 CullingCameraDir; +uniform float CullingFov; uniform vec3 FrustumPos; uniform float RenderDistance; uniform int LevelHeightOffset; @@ -50,6 +52,13 @@ vec3 worldToScreenSpace(vec3 pos) { return (ndc + vec3(1.0)) * 0.5; } +vec3 moveTowardsCamera(vec3 pos, float distance) { + vec3 direction = normalize(pos - CullingCameraPos); + vec3 newPos = pos - direction * distance; + + return newPos; +} + vec3 blockToChunk(vec3 blockPos) { vec3 chunkPos; chunkPos.x = floor(blockPos.x / 16.0); @@ -119,11 +128,10 @@ void main() { vec3 chunkBasePos = vec3(chunkX, chunkY, chunkZ); vec3 chunkPos = vec3(chunkBasePos+blockToChunk(CullingCameraPos))*16; chunkPos = vec3(chunkPos.x, chunkY*16, chunkPos.z)+vec3(8.0); - vec3 chunkPosOffset = chunkPos; - float chunkCenterDepth = worldToScreenSpace(chunkPos).z; + float chunkCenterDepth = worldToScreenSpace(moveTowardsCamera(chunkPos, 16)).z; - if(calculateDistance(chunkPos, CullingCameraPos) < 1024) { + if (calculateDistance(chunkPos, CullingCameraPos) < 256) { fragColor = vec4(1.0, 1.0, 1.0, 1.0); return; } @@ -145,11 +153,17 @@ void main() { float minX = 1; float minY = 1; + bool inside = false; + float fovRadians = radians(CullingFov); + float halfFovRadians = fovRadians / 2.0; + float cosHalfFov = cos(halfFovRadians); for (int i = 0; i < 8; ++i) { + if(dot(normalize(aabb[i] - CullingCameraPos), normalize(CullingCameraDir)) < cosHalfFov) { + continue; + } else { + inside = true; + } vec3 screenPos = worldToScreenSpace(aabb[i]); - bool xIn = screenPos.x >= 0.0 && screenPos.x <= 1.0; - bool yIn = screenPos.y >= 0.0 && screenPos.y <= 1.0; - bool zIn = screenPos.z >= 0.0 && screenPos.z <= 1.0; if(screenPos.x > maxX) maxX = screenPos.x; @@ -161,12 +175,17 @@ void main() { minY = screenPos.y; } - float chunkDepth = LinearizeDepth(chunkCenterDepth)-24; + float chunkDepth = LinearizeDepth(chunkCenterDepth); if(chunkDepth < 0) { fragColor = vec4(1.0, 1.0, 1.0, 1.0); return; } + if (!inside) { + fragColor = vec4(0.0, 0.0, 1.0, 1.0); + return; + } + int idx = getSampler( min(1.0, max(0.0, maxX))-min(1.0, max(0.0, minX)), min(1.0, max(0.0, maxY))-min(1.0, max(0.0, minY))); diff --git a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.json b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.json index 29ff5f5..159ca5a 100644 --- a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.json +++ b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/chunk_culling.json @@ -21,6 +21,8 @@ { "name": "CullingSize", "type": "float", "count": 2, "values": [1.0, 1.0] }, { "name": "DepthSize", "type": "float", "count": 8, "values": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] }, { "name": "CullingCameraPos", "type": "float", "count": 3, "values": [ 1.0, 1.0, 1.0] }, + { "name": "CullingCameraDir", "type": "float", "count": 3, "values": [ 1.0, 1.0, 1.0] }, + { "name": "CullingFov", "type": "float", "count": 1, "values": [ 90.0] }, { "name": "FrustumPos", "type": "float", "count": 3, "values": [ 1.0, 1.0, 1.0] }, { "name": "RenderDistance", "type": "float", "count": 1, "values": [ 0.0] }, { "name": "LevelHeightOffset", "type": "int", "count": 1, "values": [ 16 ] }, diff --git a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/entity_culling.fsh b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/entity_culling.fsh index e5ad123..94cca65 100644 --- a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/entity_culling.fsh +++ b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/entity_culling.fsh @@ -2,6 +2,9 @@ uniform vec2 EntityCullingSize; uniform mat4 CullingViewMat; +uniform vec3 CullingCameraPos; +uniform vec3 CullingCameraDir; +uniform float CullingFov; uniform mat4 CullingProjMat; uniform float DepthOffset; uniform vec3 FrustumPos; @@ -22,10 +25,10 @@ float near = 0.1; float far = 1000.0; int getSampler(float xLength, float yLength) { - for(int i = 0; i < DepthScreenSize.length(); ++i) { + for (int i = 0; i < DepthScreenSize.length(); ++i) { float xStep = 3.0 / DepthScreenSize[i].x; float yStep = 3.0 / DepthScreenSize[i].y; - if(xStep > xLength && yStep > yLength) { + if (xStep > xLength && yStep > yLength) { return i; } } @@ -48,8 +51,15 @@ vec3 worldToScreenSpace(vec3 pos) { return (ndc + vec3(1.0)) * 0.5; } +vec3 moveTowardsCamera(vec3 pos, float distance) { + vec3 direction = normalize(pos - CullingCameraPos); + vec3 newPos = pos - direction * distance; + + return newPos; +} + bool cubeInFrustum(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { - for(int i = 0; i < 6; ++i) { + for (int i = 0; i < 6; ++i) { vec4 plane = frustum[i]; if (!(dot(plane, vec4(minX, minY, minZ, 1.0)) > 0.0) && !(dot(plane, vec4(maxX, minY, minZ, 1.0)) > 0.0) && @@ -88,12 +98,12 @@ bool isVisible(vec3 vec, float width, float height) { } float getUVDepth(int idx, vec2 uv) { - if(idx == 0) - return texture(Sampler0, uv).r * 500; - else if(idx == 1) - return texture(Sampler1, uv).r * 500; - else if(idx == 2) - return texture(Sampler2, uv).r * 500; + if (idx == 0) + return texture(Sampler0, uv).r * 500; + else if (idx == 1) + return texture(Sampler1, uv).r * 500; + else if (idx == 2) + return texture(Sampler2, uv).r * 500; return texture(Sampler3, uv).r * 500; } @@ -102,18 +112,16 @@ void main() { float halfWidth = Size.x*0.5; float halfHeight = Size.y*0.5; - float entityDepth = worldToScreenSpace(Pos).z; - - if(!isVisible(Pos, halfWidth, halfHeight)) { + if (!isVisible(Pos, halfWidth, halfHeight)) { fragColor = vec4(0.0, 0.0, 1.0, 1.0); return; } vec3 aabb[8] = vec3[]( - Pos+vec3(-halfWidth, -halfHeight, -halfWidth), Pos+vec3(halfWidth, -halfHeight, -halfWidth), - Pos+vec3(-halfWidth, halfHeight, -halfWidth), Pos+vec3(halfWidth, halfHeight, -halfWidth), - Pos+vec3(-halfWidth, -halfHeight, halfWidth), Pos+vec3(halfWidth, -halfHeight, halfWidth), - Pos+vec3(-halfWidth, halfHeight, halfWidth), Pos+vec3(halfWidth, halfHeight, halfWidth) + Pos+vec3(-halfWidth, -halfHeight, -halfWidth), Pos+vec3(halfWidth, -halfHeight, -halfWidth), + Pos+vec3(-halfWidth, halfHeight, -halfWidth), Pos+vec3(halfWidth, halfHeight, -halfWidth), + Pos+vec3(-halfWidth, -halfHeight, halfWidth), Pos+vec3(halfWidth, -halfHeight, halfWidth), + Pos+vec3(-halfWidth, halfHeight, halfWidth), Pos+vec3(halfWidth, halfHeight, halfWidth) ); float maxX = -1; @@ -121,26 +129,30 @@ void main() { float minX = 1; float minY = 1; + bool inside = false; + float fovRadians = radians(CullingFov); + float halfFovRadians = fovRadians / 2.0; + float cosHalfFov = cos(halfFovRadians); for (int i = 0; i < 8; ++i) { + if (dot(normalize(aabb[i] - CullingCameraPos), normalize(CullingCameraDir)) < cosHalfFov) { + continue; + } else { + inside = true; + } vec3 screenPos = worldToScreenSpace(aabb[i]); - bool xIn = screenPos.x >= 0.0 && screenPos.x <= 1.0; - bool yIn = screenPos.y >= 0.0 && screenPos.y <= 1.0; - bool zIn = screenPos.z >= 0.0 && screenPos.z <= 1.0; - if(screenPos.x > maxX) + if (screenPos.x > maxX) maxX = screenPos.x; - if(screenPos.y > maxY) + if (screenPos.y > maxY) maxY = screenPos.y; - if(screenPos.x < minX) + if (screenPos.x < minX) minX = screenPos.x; - if(screenPos.y < minY) + if (screenPos.y < minY) minY = screenPos.y; } - entityDepth = LinearizeDepth(entityDepth)-sqrt(halfWidth*halfWidth+halfWidth*halfWidth)*1.2 - 4.0; - - if(entityDepth < 0) { - fragColor = vec4(1.0, 1.0, 1.0, 1.0); + if (!inside) { + fragColor = vec4(0.0, 0.0, 1.0, 1.0); return; } @@ -156,10 +168,12 @@ void main() { maxY = min(1.0, max(0.0, maxY+yStep)); minY = min(1.0, max(0.0, minY-yStep)); - for(float x = minX; x <= maxX; x += xStep) { - for(float y = minY; y <= maxY; y += yStep) { + float entityDepth = LinearizeDepth(worldToScreenSpace(moveTowardsCamera(Pos, sqrt(halfWidth*halfWidth+halfWidth*halfWidth))).z); + for (float x = minX; x <= maxX; x += xStep) { + for (float y = minY; y <= maxY; y += yStep) { float pixelDepth = getUVDepth(idx, vec2(x, y)); - if(entityDepth < pixelDepth) { + + if (entityDepth < pixelDepth) { fragColor = vec4(0.0, 1.0, 0.0, 1.0); return; } diff --git a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/instanced_entity_culling.json b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/instanced_entity_culling.json index 659096a..51bc03a 100644 --- a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/instanced_entity_culling.json +++ b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/instanced_entity_culling.json @@ -17,6 +17,9 @@ { "name": "CullingViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "CullingProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "EntityCullingSize", "type": "float", "count": 2, "values": [1.0, 1.0] }, + { "name": "CullingCameraDir", "type": "float", "count": 3, "values": [ 1.0, 1.0, 1.0] }, + { "name": "CullingCameraPos", "type": "float", "count": 3, "values": [ 1.0, 1.0, 1.0 ] }, + { "name": "CullingFov", "type": "float", "count": 1, "values": [ 90.0] }, { "name": "DepthSize", "type": "float", "count": 8, "values": [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] }, { "name": "FrustumPos", "type": "float", "count": 3, "values": [ 1.0, 1.0, 1.0] }, { "name": "CullingFrustum", "type": "float", "count": 24, "values": [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] } diff --git a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/remove_color.fsh b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/remove_color.fsh new file mode 100644 index 0000000..45afa2d --- /dev/null +++ b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/remove_color.fsh @@ -0,0 +1,19 @@ +#version 150 + +uniform sampler2D Sampler0; + +uniform vec4 ColorModulator; + +in vec4 vertexColor; +in vec2 texCoord0; + +out vec4 fragColor; + +void main() { + vec4 color = texture(Sampler0, texCoord0); + + vec3 removeColor = vec3((color.x + color.y + color.z)/3); + removeColor = (vertexColor.a * color.rgb) + ((1.0-vertexColor.a) * removeColor.rgb); + color.rgb = vec3((1.0 - removeColor + ColorModulator.a)*(1-ColorModulator.a)); + fragColor = color; +} diff --git a/src/main/resources/assets/brute_force_rendering_culling/shaders/core/remove_color.json b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/remove_color.json new file mode 100644 index 0000000..f7f5dbd --- /dev/null +++ b/src/main/resources/assets/brute_force_rendering_culling/shaders/core/remove_color.json @@ -0,0 +1,24 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "position_color_tex", + "fragment": "brute_force_rendering_culling:remove_color", + "attributes": [ + "Position", + "Color", + "UV0" + ], + "samplers": [ + { + "name": "Sampler0" + } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } + ] +} diff --git a/src/main/resources/brute_force_rendering_culling.accesswidener b/src/main/resources/brute_force_rendering_culling.accesswidener new file mode 100644 index 0000000..506d5b0 --- /dev/null +++ b/src/main/resources/brute_force_rendering_culling.accesswidener @@ -0,0 +1,6 @@ +accessWidener v1 named +accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo +accessible method net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo (Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;Lnet/minecraft/core/Direction;I)V +accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkStorage +accessible class net/minecraft/client/renderer/LevelRenderer$RenderInfoMap + diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 5e3d0e8..b6bc473 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "brute_force_rendering_culling", - "version": "0.4.4", + "version": "0.5.4", "name": "Brute force Rendering Culling", "description": "", "authors": [ @@ -15,7 +15,7 @@ "environment": "client", "entrypoints": { "main": [ - "rogo.renderingculling.api.CullingHandler" + "rogo.renderingculling.api.ModLoader" ] }, "mixins": [ @@ -29,5 +29,6 @@ }, "suggests": { "another-mod": "*" - } + }, + "accessWidener" : "brute_force_rendering_culling.accesswidener" } \ No newline at end of file diff --git a/src/main/resources/mixins.bfrc.json b/src/main/resources/mixins.bfrc.json index 13f0bf7..27fadf3 100644 --- a/src/main/resources/mixins.bfrc.json +++ b/src/main/resources/mixins.bfrc.json @@ -1,12 +1,6 @@ { "required": true, "package": "rogo.renderingculling.mixin", - "mixins": [ - "MixinMinecraft", - "MixinOcclusionCuller", - "MixinRenderSection", - "MixinRenderSectionManager" - ], "minVersion": "0.8.1", "compatibilityLevel": "JAVA_17", "refmap": "BruteForceRenderingCulling-fabric-1.20.1-refmap.json", @@ -20,14 +14,25 @@ "AccessorMinecraft", "MixinBlockEntityRender", "MixinEntityRender", + "MixinGameRenderer", "MixinInactiveProfiler", - "MixinLevel", "MixinLevelRender", + "MixinLevelRender$AccessorRenderChunkInfo", + "MixinLevelRender$AccessorViewArea", + "MixinMinecraft", "MixinRenderChunk", "MixinRenderChunkInfo", "MixinShaderInstance", "MixinShaderInstance$MixinRenderSystem", + "fabric.MixinTooltipRenderUtil", "fabric.MixinKeyboardHandler", - "fabric.MixinWindow" + "fabric.MixinWindow", + "sodium.AccessorRenderSectionManager", + "sodium.MixinOcclusionCuller", + "sodium.MixinRenderRegion", + "sodium.MixinRenderSection", + "sodium.MixinRenderSectionManager", + "sodium.MixinSodiumWorldRenderer", + "sodium.MixinVisibleChunkCollector" ] } \ No newline at end of file