Skip to content

Commit

Permalink
Fix bug:
Browse files Browse the repository at this point in the history
Entity culling does not respect block entities on Valkyrien Skies ships.
Features:

Added dynamic depth delay feature.
Changed the method of determining block visibility for more timely chunk culling.
  • Loading branch information
RogoShum committed Apr 14, 2024
1 parent 61455dd commit 633e898
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 77 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ loader_version=0.15.9
fiber_version = 0.23.0-SNAPSHOT

# Mod Properties
mod_version=0.4.6
mod_version=0.4.7
maven_group=rogo.renderingculling
archives_base_name=BruteForceRenderingCulling-fabric-1.18.2

Expand Down
60 changes: 13 additions & 47 deletions src/main/java/rogo/renderingculling/api/ChunkCullingMap.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package rogo.renderingculling.api;

import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.phys.Vec3;
import rogo.renderingculling.util.Vec2i;

import java.util.HashMap;

public class ChunkCullingMap extends CullingMap {
private final HashMap<BlockPos, Integer> screenIndex = new HashMap<>();
private int renderDistance = 0;
private int spacePartitionSize = 0;

public ChunkCullingMap(int width, int height) {
super(width, height);
Expand All @@ -24,35 +21,10 @@ int bindFrameBufferId() {
return CullingHandler.CHUNK_CULLING_MAP_TARGET.frameBufferId;
}

public int getPosIndex(BlockPos pos) {
int renderDistance = Minecraft.getInstance().options.getEffectiveRenderDistance();
int spacePartitionSize = 2 * renderDistance + 1;
int x = pos.getX() + renderDistance;
int z = pos.getZ() + renderDistance;
int y = pos.getY();

return x * spacePartitionSize * CullingHandler.LEVEL_HEIGHT_OFFSET + z * CullingHandler.LEVEL_HEIGHT_OFFSET + y;
}

public Vec2i getScreenPosFromIndex(int idx) {
int y = idx / width;
int x = idx - (y*width);
return new Vec2i(x, y);
}

public void generateIndex(int renderDistance) {
screenIndex.clear();
for(int x = -renderDistance; x <= renderDistance; ++x) {
for (int z = -renderDistance; z <= renderDistance; ++z) {
for (int y = 0; y < CullingHandler.LEVEL_HEIGHT_OFFSET; ++y) {
BlockPos pos = new BlockPos(x, y, z);
Vec2i coord = getScreenPosFromIndex(getPosIndex(pos));
if(coord.x() >= 0 && coord.y() >= 0 && coord.x() < this.width && coord.y() < this.height) {
screenIndex.put(pos, getPosIndex(pos));
}
}
}
}
this.renderDistance = renderDistance;
spacePartitionSize = 2 * renderDistance + 1;

}

public boolean isChunkVisible(double x, double y, double z) {
Expand All @@ -64,24 +36,18 @@ public boolean isChunkVisible(double x, double y, double z) {
y -= 9;

int chunkX = (int) x >> 4;
int chunkY = (int) y / 16 + CullingHandler.LEVEL_MIN_SECTION_ABS;
int chunkY = (int) (y * 0.0625) + CullingHandler.LEVEL_MIN_SECTION_ABS;
int chunkZ = (int) z >> 4;
BlockPos pos = new BlockPos(chunkX - cameraX, chunkY, chunkZ - cameraZ);

if(screenIndex.containsKey(pos)) {
Integer index = screenIndex.get(pos);
float cullingValue = (float) (cullingBuffer.get(1+index*4) & 0xFF) / 255.0f;
return cullingValue > 0.5;
}
int posX = chunkX - cameraX + renderDistance;
int posZ = chunkZ - cameraZ + renderDistance;

return false;
}
int index = 1 + (((posX * spacePartitionSize * CullingHandler.LEVEL_HEIGHT_OFFSET + posZ * CullingHandler.LEVEL_HEIGHT_OFFSET + chunkY) << 2));

public boolean isTransferred() {
return transferred;
}
if (index > 0 && index < cullingBuffer.limit()) {
return (cullingBuffer.get(index) & 0xFF) > 0;
}

public void setTransferred(boolean transferred) {
this.transferred = transferred;
return false;
}
}
3 changes: 2 additions & 1 deletion src/main/java/rogo/renderingculling/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ public static void setCullChunk(boolean value) {
public static int getDepthUpdateDelay() {
if(unload())
return 1;
return UPDATE_DELAY.getValue();
int dynamicWithShader = CullingHandler.INSTANCE.renderingShader() ? 1 : 0;
return UPDATE_DELAY.getValue() + dynamicWithShader;
}

public static void setDepthUpdateDelay(int value) {
Expand Down
32 changes: 23 additions & 9 deletions src/main/java/rogo/renderingculling/api/CullingHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public class CullingHandler implements ModInitializer {
public static Camera CAMERA;
private static final HashMap<Integer, Integer> SHADER_DEPTH_BUFFER_ID = new HashMap<>();
public static boolean SHADER_ENABLED = false;
private int frame;

static {
RenderSystem.recordRenderCall(() -> {
Expand Down Expand Up @@ -252,39 +253,47 @@ private void cleanup() {
SHADER_DEPTH_BUFFER_ID.clear();
}

public boolean shouldRenderChunk(IRenderSectionVisibility section) {
chunkCount++;
public boolean shouldRenderChunk(IRenderSectionVisibility section, boolean count) {
if(count)
chunkCount++;
if (!Config.getCullChunk() || CHUNK_CULLING_MAP == null || !CHUNK_CULLING_MAP.isDone()) {
return true;
}

long time = System.nanoTime();
boolean render;
boolean actualRender = false;
long time = System.nanoTime();

if (!section.shouldCheckVisibility(clientTickCount)) {
if (!section.shouldCheckVisibility(frame)) {
render = true;
} else {
actualRender = CHUNK_CULLING_MAP.isChunkVisible(section.getPositionX(), section.getPositionY(), section.getPositionZ());
render = actualRender;
}

preChunkCullingTime += System.nanoTime() - time;

if (checkCulling)
render = !render;

if (!render) {
if (!render && count) {
chunkCulling++;
} else if(actualRender) {
section.updateVisibleTick(clientTickCount);
section.updateVisibleTick(frame);
}

if(count)
preChunkCullingTime += System.nanoTime() - time;
return render;
}

public boolean shouldSkipBlock(BlockEntity blockEntity, AABB aabb, BlockPos pos) {
blockCount++;

//for valkyrien skies
if(CAMERA.getPosition().distanceToSqr(pos.getX(), pos.getY(), pos.getZ()) >
Minecraft.getInstance().options.getEffectiveRenderDistance() * Minecraft.getInstance().options.getEffectiveRenderDistance() * 2) {
return false;
}

if (ENTITY_CULLING_MAP == null || !Config.getCullEntity()) return false;
if (FRUSTUM == null || !FRUSTUM.isVisible(aabb)) return true;
String type = BlockEntityType.getKey(blockEntity.getType()).toString();
Expand Down Expand Up @@ -435,6 +444,7 @@ public void onProfilerPush(String s) {
}

public void beforeRenderingWorld() {
++frame;
if(SHADER_LOADER != null) {
boolean clear = false;
if(SHADER_LOADER.renderingShader() && !usingShader) {
Expand Down Expand Up @@ -654,7 +664,11 @@ public static void callDepthTexture() {
}

public boolean renderingOculus() {
return SHADER_LOADER != null && OptiFine == null && SHADER_LOADER.renderingShader();
return renderingShader() && OptiFine == null;
}

public boolean renderingShader() {
return SHADER_LOADER != null && SHADER_LOADER.renderingShader();
}

public boolean isNextTick(int tick) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/rogo/renderingculling/api/CullingMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public void readData() {
abstract int configDelayCount();

public int dynamicDelayCount() {
if(CullingHandler.INSTANCE.fps > 100) {
return CullingHandler.INSTANCE.fps / 100;
if(CullingHandler.INSTANCE.fps > 200) {
return CullingHandler.INSTANCE.fps / 200;
}

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import net.minecraft.client.gui.Font;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.phys.Vec3;
import rogo.renderingculling.instanced.EntityCullingInstanceRenderer;
Expand Down Expand Up @@ -55,7 +56,7 @@ public void onOverlayRender(PoseStack matrixStack, float tickDelta ) {
String cullingInitTime = new TranslatableComponent("brute_force_rendering_culling.chunk_culling_init").getString() + ": " + (CullingHandler.INSTANCE.chunkCullingInitTime /1000/CullingHandler.INSTANCE.cullingInitCount) + " μs";
drawString(cullingInitTime, width, height - heightScale);

String chunkCullingTime = new TranslatableComponent("brute_force_rendering_culling.chunk_culling_time").getString() + ": " + (CullingHandler.INSTANCE.chunkCullingTime/1000/CullingHandler.INSTANCE.fps) + " μs";
String chunkCullingTime = new TranslatableComponent("brute_force_rendering_culling.chunk_culling_time").getString() + ": " + (CullingHandler.INSTANCE.chunkCullingTime / 1000 / CullingHandler.INSTANCE.chunkCount) + " μs";
drawString(chunkCullingTime, width, height - heightScale);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void onapplyFrustum(Frustum p_194355_, CallbackInfo ci) {
if (value instanceof ObjectArrayList) {
((ObjectArrayList<?>)value).removeIf((o -> {
ChunkRenderDispatcher.RenderChunk chunk = ((IRenderChunkInfo) o).getRenderChunk();
return !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) chunk);
return !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) chunk, true);
}));
}
} catch (NoSuchFieldException | IllegalAccessException ignored) {
Expand All @@ -47,7 +47,7 @@ public void onapplyFrustum(Frustum p_194355_, CallbackInfo ci) {

this.renderChunksInFrustum.removeIf((o -> {
ChunkRenderDispatcher.RenderChunk chunk = ((IRenderChunkInfo) o).getRenderChunk();
return !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) chunk);
return !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) chunk, true);
}));
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/rogo/renderingculling/mixin/MixinRenderChunk.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ public abstract class MixinRenderChunk implements IRenderSectionVisibility {

@Shadow @Final private BlockPos.MutableBlockPos origin;
@Unique
private int cullingLastVisibleTick;
private int cullingLastVisibleFrame;

@Override
public boolean shouldCheckVisibility(int clientTick) {
return clientTick - cullingLastVisibleTick > 20;
public boolean shouldCheckVisibility(int frame) {
return frame != cullingLastVisibleFrame;
}

@Override
public void updateVisibleTick(int clientTick) {
cullingLastVisibleTick = clientTick;
public void updateVisibleTick(int frame) {
cullingLastVisibleFrame = frame;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ public abstract class MixinRenderSection implements IRenderSectionVisibility {
@Shadow(remap = false) public abstract int getOriginZ();

@Unique
private int cullingLastVisibleTick;
private int cullingLastVisibleFrame;

@Override
public boolean shouldCheckVisibility(int clientTick) {
return clientTick - cullingLastVisibleTick > 20;
public boolean shouldCheckVisibility(int frame) {
return frame != cullingLastVisibleFrame;
}

@Override
public void updateVisibleTick(int clientTick) {
cullingLastVisibleTick = clientTick;
public void updateVisibleTick(int frame) {
cullingLastVisibleFrame = frame;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import rogo.renderingculling.api.CullingHandler;
import rogo.renderingculling.api.IRenderSectionVisibility;

@Mixin(RenderSectionManager.class)
public abstract class MixinRenderSectionManager {
@Inject(method = "addVisible", at = @At(value = "HEAD"), remap = false, cancellable = true)
public void onBfsEnqueue(RenderSection section, Direction flow, CallbackInfo ci) {
if(!section.isEmpty() && !CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) section))
ci.cancel();
@Inject(method = "isWithinRenderDistance", at = @At(value = "HEAD"), remap = false, cancellable = true)
public void onIsWithinRenderDistance(RenderSection section, CallbackInfoReturnable<Boolean> cir) {
cir.setReturnValue(CullingHandler.INSTANCE.shouldRenderChunk((IRenderSectionVisibility) section, true));
}
}

0 comments on commit 633e898

Please sign in to comment.