From 5d3b607cb1325cb4f7070c7d249deefcc2b650cf Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Tue, 31 Dec 2024 04:28:44 +0800 Subject: [PATCH 1/6] [1.21.1] Implement cached BlockEntity rendering pipeline --- .../net/minecraft/client/Minecraft.java.patch | 8 + .../client/renderer/LevelRenderer.java.patch | 7 +- .../world/level/chunk/LevelChunk.java.patch | 10 +- .../neoforge/client/ClientHooks.java | 5 + .../client/FullyBufferedBufferSource.java | 98 +++++++++ .../cached/CacheableBERenderingPipeline.java | 93 ++++++++ .../neoforge/client/cached/CachedRegion.java | 202 ++++++++++++++++++ .../neoforge/client/cached/CompileResult.java | 70 ++++++ .../neoforge/client/cached/package-info.java | 14 ++ .../IBlockEntityRendererExtension.java | 12 ++ .../resources/META-INF/accesstransformer.cfg | 9 + .../neoforge/debug/client/CachedBERTests.java | 191 +++++++++++++++++ 12 files changed, 716 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java create mode 100644 src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java create mode 100644 src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java create mode 100644 src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java create mode 100644 src/main/java/net/neoforged/neoforge/client/cached/package-info.java create mode 100644 tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java diff --git a/patches/net/minecraft/client/Minecraft.java.patch b/patches/net/minecraft/client/Minecraft.java.patch index 13ff259e92b..2a2d363e4a2 100644 --- a/patches/net/minecraft/client/Minecraft.java.patch +++ b/patches/net/minecraft/client/Minecraft.java.patch @@ -352,6 +352,14 @@ if (integratedserver != null) { this.profiler.push("waitForServer"); +@@ -2175,6 +_,7 @@ + } + + private void updateLevelInEngines(@Nullable ClientLevel p_91325_) { ++ net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline.updateLevel(p_91325_); + this.levelRenderer.setLevel(p_91325_); + this.particleEngine.setLevel(p_91325_); + this.blockEntityRenderDispatcher.setLevel(p_91325_); @@ -2258,6 +_,7 @@ private void pickBlock() { diff --git a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch index 82e084e8b4b..4ece15fcfd9 100644 --- a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch @@ -29,7 +29,7 @@ float f = this.minecraft.level.getRainLevel(1.0F) / (Minecraft.useFancyGraphics() ? 1.0F : 2.0F); if (!(f <= 0.0F)) { RandomSource randomsource = RandomSource.create((long)this.ticks * 312987231L); -@@ -942,9 +_,11 @@ +@@ -942,18 +_,24 @@ RenderSystem.clear(16640, Minecraft.ON_OSX); float f1 = p_109605_.getRenderDistance(); boolean flag1 = this.minecraft.level.effects().isFoggyAt(Mth.floor(d0), Mth.floor(d1)) || this.minecraft.gui.getBossOverlay().shouldCreateWorldFog(); @@ -41,9 +41,12 @@ profilerfiller.popPush("fog"); FogRenderer.setupFog(p_109604_, FogRenderer.FogMode.FOG_TERRAIN, Math.max(f1, 32.0F), flag1, f); profilerfiller.popPush("terrain_setup"); -@@ -953,7 +_,9 @@ + this.setupRender(p_109604_, frustum, flag, this.minecraft.player.isSpectator()); + profilerfiller.popPush("compile_sections"); ++ net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline.getInstance().runTasks(); this.compileSections(p_109604_); profilerfiller.popPush("terrain"); ++ net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline.getInstance().render(p_254120_, p_323920_); this.renderSectionLayer(RenderType.solid(), d0, d1, d2, p_254120_, p_323920_); + this.minecraft.getModelManager().getAtlas(TextureAtlas.LOCATION_BLOCKS).setBlurMipmap(false, this.minecraft.options.mipmapLevels().get() > 0); // Neo: fix flickering leaves when mods mess up the blurMipmap settings this.renderSectionLayer(RenderType.cutoutMipped(), d0, d1, d2, p_254120_, p_323920_); diff --git a/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch b/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch index a47b0de44e2..93fe60cd255 100644 --- a/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch +++ b/patches/net/minecraft/world/level/chunk/LevelChunk.java.patch @@ -78,7 +78,15 @@ } else { CompoundTag compoundtag = this.pendingBlockEntities.get(p_62932_); if (compoundtag != null) { -@@ -436,6 +_,7 @@ +@@ -431,11 +_,15 @@ + if (this.isInLevel()) { + BlockEntity blockentity = this.blockEntities.remove(p_62919_); + if (blockentity != null) { ++ if (this.level.isClientSide) { ++ net.neoforged.neoforge.client.ClientHooks.onBlockEntityRemoved(blockentity); ++ } + if (this.level instanceof ServerLevel serverlevel) { + this.removeGameEventListener(blockentity, serverlevel); } blockentity.setRemoved(); diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 005dd1e25b8..0b088a25f5f 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -137,6 +137,7 @@ import net.neoforged.fml.ModLoader; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.asm.enumextension.ExtensionInfo; +import net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline; import net.neoforged.neoforge.client.entity.animation.json.AnimationTypeManager; import net.neoforged.neoforge.client.event.AddSectionGeometryEvent; import net.neoforged.neoforge.client.event.CalculateDetachedCameraDistanceEvent; @@ -791,6 +792,10 @@ public static void registerShaders(RegisterShadersEvent event) throws IOExceptio } } + public static void onBlockEntityRemoved(BlockEntity blockEntity) { + CacheableBERenderingPipeline.getInstance().blockRemoved(blockEntity); + } + public static Font getTooltipFont(ItemStack stack, Font fallbackFont) { Font stackFont = IClientItemExtensions.of(stack).getFont(stack, IClientItemExtensions.FontContext.TOOLTIP); return stackFont == null ? fallbackFont : stackFont; diff --git a/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java b/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java new file mode 100644 index 00000000000..bf6e35c88f7 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java @@ -0,0 +1,98 @@ +package net.neoforged.neoforge.client; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexConsumer; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.neoforged.neoforge.client.cached.CompileResult; +import org.lwjgl.system.MemoryUtil; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class FullyBufferedBufferSource extends MultiBufferSource.BufferSource implements AutoCloseable { + private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); + private final Map byteBuffers = new HashMap<>(); + private final Map bufferBuilders = new HashMap<>(); + private final Reference2IntMap indexCountMap = new Reference2IntOpenHashMap<>(); + + public FullyBufferedBufferSource() { + super(null, null); + } + + private ByteBufferBuilder getByteBuffer(RenderType renderType) { + return byteBuffers.computeIfAbsent(renderType, it -> new ByteBufferBuilder(786432)); + } + + @Override + public VertexConsumer getBuffer(RenderType renderType) { + return bufferBuilders.computeIfAbsent( + renderType, + it -> new BufferBuilder(getByteBuffer(it), it.mode, it.format) + ); + } + + public boolean isEmpty() { + return !bufferBuilders.isEmpty() && bufferBuilders.values().stream().noneMatch(it -> it.vertices > 0); + } + + @Override + public void endBatch(RenderType renderType) { + } + + public void upload( + Function vertexBufferGetter, + Consumer runner + ) { + for (RenderType renderType : bufferBuilders.keySet()) { + runner.accept(() -> { + BufferBuilder bufferBuilder = bufferBuilders.get(renderType); + ByteBufferBuilder byteBuffer = byteBuffers.get(renderType); + long ptr = byteBuffer.pointer; + int compiledVertices = bufferBuilder.vertices * renderType.format.getVertexSize(); + if (compiledVertices >= 0) { + MeshData mesh = bufferBuilder.build(); + if (mesh != null) { + mesh.close(); + } + CompileResult compileResult = new CompileResult( + renderType, + bufferBuilder.vertices, + renderType.format.getVertexSize(), + ptr, + renderType.mode.indexCount(bufferBuilder.vertices) + ); + indexCountMap.put(renderType, renderType.mode.indexCount(bufferBuilder.vertices)); + compileResult.upload(vertexBufferGetter.apply(renderType)); + } + byteBuffer.close(); + bufferBuilders.remove(renderType); + byteBuffers.remove(renderType); + }); + } + } + + public void close(RenderType renderType) { + ByteBufferBuilder builder = byteBuffers.get(renderType); + builder.close(); + } + + public Reference2IntMap getIndexCountMap() { + return indexCountMap; + } + + public void close() { + byteBuffers.keySet().forEach(this::close); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java b/src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java new file mode 100644 index 00000000000..69e3202525b --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java @@ -0,0 +1,93 @@ +package net.neoforged.neoforge.client.cached; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.neoforged.neoforge.client.extensions.IBlockEntityRendererExtension; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; + +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; + +public class CacheableBERenderingPipeline { + @Nullable + private static CacheableBERenderingPipeline instance; + private final ClientLevel level; + private final Queue pendingCompiles = new ArrayDeque<>(); + private final Queue pendingUploads = new ArrayDeque<>(); + private final Map regions = new HashMap<>(); + private boolean valid = true; + + public CachedRegion getRenderRegion(ChunkPos chunkPos) { + if (regions.containsKey(chunkPos)) { + return regions.get(chunkPos); + } + CachedRegion region = new CachedRegion(chunkPos, this); + regions.put(chunkPos, region); + return region; + } + + public CacheableBERenderingPipeline(ClientLevel level) { + this.level = level; + } + + public void runTasks() { + while (!pendingCompiles.isEmpty() && valid) { + pendingCompiles.poll().run(); + } + while (!pendingUploads.isEmpty() && valid) { + pendingUploads.poll().run(); + } + } + + public static void updateLevel(ClientLevel level) { + if (instance != null) { + instance.releaseBuffers(); + } + instance = new CacheableBERenderingPipeline(level); + } + + public void blockRemoved(BlockEntity be) { + IBlockEntityRendererExtension renderer = Minecraft.getInstance() + .getBlockEntityRenderDispatcher() + .getRenderer(be); + if (renderer == null) return; + ChunkPos chunkPos = new ChunkPos(be.getBlockPos()); + getRenderRegion(chunkPos).blockRemoved(be); + } + + public void update(BlockEntity be) { + BlockEntityRenderer renderer = Minecraft.getInstance() + .getBlockEntityRenderDispatcher() + .getRenderer(be); + if (renderer == null) return; + ChunkPos chunkPos = new ChunkPos(be.getBlockPos()); + getRenderRegion(chunkPos).update(be); + } + + public void submitUploadTask(Runnable task) { + pendingUploads.add(task); + } + + public void submitCompileTask(Runnable task) { + pendingCompiles.add(task); + } + + public void releaseBuffers() { + regions.values().forEach(CachedRegion::releaseBuffers); + valid = false; + } + + public void render(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { + regions.values().forEach(it -> it.render(frustumMatrix, projectionMatrix)); + } + + public static CacheableBERenderingPipeline getInstance() { + return instance; + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java b/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java new file mode 100644 index 00000000000..1baf7f2d1fd --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java @@ -0,0 +1,202 @@ +package net.neoforged.neoforge.client.cached; + +import com.mojang.blaze3d.platform.Window; +import com.mojang.blaze3d.shaders.Uniform; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexFormat; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.client.FullyBufferedBufferSource; +import net.neoforged.neoforge.client.extensions.IBlockEntityRendererExtension; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL15; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CachedRegion { + private final ChunkPos chunkPos; + private final Map buffers = new HashMap<>(); + private Reference2IntMap indexCountMap = new Reference2IntOpenHashMap<>(); + private final List blockEntityList = new ArrayList<>(); + private final CacheableBERenderingPipeline pipeline; + private final Minecraft minecraft = Minecraft.getInstance(); + @Nullable + private RebuildTask lastRebuildTask; + + private boolean isEmpty = true; + + public CachedRegion(ChunkPos chunkPos, CacheableBERenderingPipeline pipeline) { + this.chunkPos = chunkPos; + this.pipeline = pipeline; + } + + public void update(BlockEntity be) { + if (lastRebuildTask != null) { + lastRebuildTask.cancel(); + } + blockEntityList.removeIf(BlockEntity::isRemoved); + if (be.isRemoved()) { + blockEntityList.remove(be); + pipeline.submitCompileTask(new RebuildTask()); + return; + } + blockEntityList.add(be); + pipeline.submitCompileTask(new RebuildTask()); + } + + public void blockRemoved(BlockEntity be) { + if (lastRebuildTask != null) { + lastRebuildTask.cancel(); + } + blockEntityList.remove(be); + blockEntityList.removeIf(BlockEntity::isRemoved); + pipeline.submitCompileTask(new RebuildTask()); + } + + public void render(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { + renderInternal(frustumMatrix, projectionMatrix, buffers.keySet()); + } + + public VertexBuffer getBuffer(RenderType renderType) { + if (buffers.containsKey(renderType)) { + return buffers.get(renderType); + } + VertexBuffer vb = new VertexBuffer(VertexBuffer.Usage.STATIC); + buffers.put(renderType, vb); + return vb; + } + + private void renderInternal( + Matrix4f frustumMatrix, + Matrix4f projectionMatrix, + Collection renderTypes + ) { + if (isEmpty) return; + RenderSystem.enableBlend(); + Window window = Minecraft.getInstance().getWindow(); + Vec3 cameraPosition = minecraft.gameRenderer.getMainCamera().getPosition(); + int renderDistance = Minecraft.getInstance().options.getEffectiveRenderDistance() * 16; + if (cameraPosition.distanceTo(new Vec3(chunkPos.x * 16, cameraPosition.y, chunkPos.z * 16)) > renderDistance) { + return; + } + for (RenderType renderType : renderTypes) { + VertexBuffer vb = buffers.get(renderType); + if (vb == null) continue; + renderLayer(renderType, vb, frustumMatrix, projectionMatrix, cameraPosition, window); + } + } + + public void releaseBuffers() { + buffers.values().forEach(VertexBuffer::close); + } + + private void renderLayer( + RenderType renderType, + VertexBuffer vertexBuffer, + Matrix4f frustumMatrix, + Matrix4f projectionMatrix, + Vec3 cameraPosition, + Window window + ) { + int indexCount = indexCountMap.getInt(renderType); + if (indexCount <= 0) return; + renderType.setupRenderState(); + ShaderInstance shader = RenderSystem.getShader(); + shader.setDefaultUniforms(VertexFormat.Mode.QUADS, frustumMatrix, projectionMatrix, window); + shader.apply(); + Uniform uniform = shader.CHUNK_OFFSET; + if (uniform != null) { + uniform.set( + (float) -cameraPosition.x, + (float) -cameraPosition.y, + (float) -cameraPosition.z + ); + uniform.upload(); + } + vertexBuffer.bind(); + GL11.glDrawElements(GL15.GL_TRIANGLES, indexCount, vertexBuffer.sequentialIndices.type().asGLType, 0L); + VertexBuffer.unbind(); + if (uniform != null) { + uniform.set(0.0F, 0.0F, 0.0F); + } + renderType.clearRenderState(); + } + + private class RebuildTask implements Runnable { + private boolean cancelled = false; + + @Override + public void run() { + lastRebuildTask = this; + PoseStack poseStack = new PoseStack(); + CachedRegion.this.isEmpty = true; + FullyBufferedBufferSource bufferSource = new FullyBufferedBufferSource(); + float partialTick = Minecraft.getInstance().getTimer().getGameTimeDeltaPartialTick(false); + for (BlockEntity be : new ArrayList<>(blockEntityList)) { + if (cancelled) { + bufferSource.close(); + return; + } + IBlockEntityRendererExtension renderer = Minecraft.getInstance() + .getBlockEntityRenderDispatcher() + .getRenderer(be); + if (renderer == null) continue; + Level level = be.getLevel(); + int packedLight; + if (level != null) { + packedLight = LevelRenderer.getLightColor(level, be.getBlockPos()); + } else { + packedLight = LightTexture.FULL_BRIGHT; + } + poseStack.pushPose(); + BlockPos pos = be.getBlockPos(); + poseStack.translate( + pos.getX(), + pos.getY(), + pos.getZ() + ); + renderer.renderCached( + be, + poseStack, + bufferSource, + partialTick, + packedLight, + OverlayTexture.NO_OVERLAY + ); + poseStack.popPose(); + } + CachedRegion.this.isEmpty = bufferSource.isEmpty(); + bufferSource.upload( + CachedRegion.this::getBuffer, + pipeline::submitUploadTask + ); + CachedRegion.this.indexCountMap = bufferSource.getIndexCountMap(); + lastRebuildTask = null; + } + + void cancel() { + cancelled = true; + } + } + + +} diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java b/src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java new file mode 100644 index 00000000000..da3626daeaf --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java @@ -0,0 +1,70 @@ +package net.neoforged.neoforge.client.cached; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexBuffer; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.client.renderer.RenderType; +import org.lwjgl.opengl.GL15; +import org.lwjgl.opengl.GL15C; +import org.lwjgl.system.MemoryUtil; + +import java.util.Objects; + +public class CompileResult { + private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); + private final RenderType renderType; + private final int vertexCount; + private final int vertexSize; + private final long vertexBufferPtr; + final int indexCount; + private boolean freed = false; + + public CompileResult( + RenderType renderType, + int vertexCount, + int vertexSize, + long vertexBufferPtr, + int indexCount + ) { + this.renderType = renderType; + this.vertexCount = vertexCount; + this.vertexSize = vertexSize; + this.vertexBufferPtr = vertexBufferPtr; + this.indexCount = indexCount; + } + + public void upload(VertexBuffer vertexBuffer) { + if (freed) return; + VertexFormat.Mode mode = renderType.mode; + vertexBuffer.bind(); + if (vertexBuffer.format != null) { + vertexBuffer.format.clearBufferState(); + } + GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBuffer.vertexBufferId); + renderType.format.setupBufferState(); + vertexBuffer.format = renderType.format; + GL15C.nglBufferData(GL15.GL_ARRAY_BUFFER, (long) vertexCount * vertexSize, vertexBufferPtr, GL15.GL_STATIC_DRAW); + RenderSystem.AutoStorageIndexBuffer indexBuffer = RenderSystem.getSequentialBuffer(mode); + if (indexBuffer != vertexBuffer.sequentialIndices || !indexBuffer.hasStorage(indexCount)) { + indexBuffer.bind(indexCount); + } + vertexBuffer.sequentialIndices = indexBuffer; + VertexBuffer.unbind(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof CompileResult that)) return false; + return vertexCount == that.vertexCount + && vertexSize == that.vertexSize + && vertexBufferPtr == that.vertexBufferPtr + && indexCount == that.indexCount + && freed == that.freed + && Objects.equals(renderType, that.renderType); + } + + @Override + public int hashCode() { + return Objects.hash(renderType, vertexCount, vertexSize, vertexBufferPtr, indexCount, freed); + } +} diff --git a/src/main/java/net/neoforged/neoforge/client/cached/package-info.java b/src/main/java/net/neoforged/neoforge/client/cached/package-info.java new file mode 100644 index 00000000000..c27f3e1baaa --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/client/cached/package-info.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.client.cached; + +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java index 99e7faef593..bb2def6c7dc 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java @@ -5,6 +5,8 @@ package net.neoforged.neoforge.client.extensions; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.AABB; @@ -20,4 +22,14 @@ public interface IBlockEntityRendererExtension { default AABB getRenderBoundingBox(T blockEntity) { return new AABB(blockEntity.getBlockPos()); } + + default void renderCached( + T blockEntity, + PoseStack poseStack, + MultiBufferSource.BufferSource bufferSource, + float partialTick, + int packedLight, + int packedOverlay + ) { + } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 11913873b90..7ebb0784023 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -501,3 +501,12 @@ public net.minecraft.world.item.enchantment.Enchantment locationContext(Lnet/min public net.minecraft.world.item.enchantment.Enchantment entityContext(Lnet/minecraft/server/level/ServerLevel;ILnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/level/storage/loot/LootContext; public net.minecraft.world.item.enchantment.Enchantment blockHitContext(Lnet/minecraft/server/level/ServerLevel;ILnet/minecraft/world/entity/Entity;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/world/level/block/state/BlockState;)Lnet/minecraft/world/level/storage/loot/LootContext; public net.minecraft.world.item.enchantment.Enchantment applyEffects(Ljava/util/List;Lnet/minecraft/world/level/storage/loot/LootContext;Ljava/util/function/Consumer;)V + +#Cached BlockEntityRenderer pipeline +public com.mojang.blaze3d.vertex.VertexBuffer format +public com.mojang.blaze3d.vertex.VertexBuffer vertexBufferId +public com.mojang.blaze3d.vertex.VertexBuffer indexBufferId +public com.mojang.blaze3d.vertex.VertexBuffer arrayObjectId +public com.mojang.blaze3d.vertex.VertexBuffer sequentialIndices +public com.mojang.blaze3d.vertex.ByteBufferBuilder pointer +public com.mojang.blaze3d.vertex.BufferBuilder vertices \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java new file mode 100644 index 00000000000..ff4c4de1c4a --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java @@ -0,0 +1,191 @@ +package net.neoforged.neoforge.debug.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.serialization.MapCodec; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.phys.BlockHitResult; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline; +import net.neoforged.neoforge.client.model.data.ModelData; +import net.neoforged.neoforge.registries.DeferredBlock; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +@ForEachTest(side = Dist.CLIENT, groups = {"client.event", "event"}) +public class CachedBERTests { + public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.Blocks.createBlocks("neotests"); + public static final DeferredRegister> BLOCK_ENTITY_TYPES = + DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, "neotests"); + + public static final DeferredBlock THE_BLOCK = BLOCKS.register( + "not_enough_vertexes", + () -> new TheBlock( + BlockBehaviour.Properties.of() + .noCollission() + .noOcclusion() + .lightLevel(state -> 15) + ) + ); + + public static final Supplier> THE_BE = BLOCK_ENTITY_TYPES.register( + "not_enough_vertexes", + () -> BlockEntityType.Builder.of( + TheBlockEntity::new, + THE_BLOCK.get() + ) + .build(null) + ); + + @TestHolder(description = "Register a block with cached BER which adds lots of vertexes") + static void registerBlock(final DynamicTest test) { + BLOCKS.register(test.framework().modEventBus()); + BLOCK_ENTITY_TYPES.register(test.framework().modEventBus()); + test.framework().modEventBus().addListener(CachedBERTests::clientSetup); + } + + static private void clientSetup(final FMLClientSetupEvent event) { + BlockEntityRenderers.register(THE_BE.get(), TheRenderer::new); + } + + public static class TheBlock extends BaseEntityBlock { + + protected TheBlock(Properties p_49224_) { + super(p_49224_); + registerDefaultState(getStateDefinition().any().setValue(BlockStateProperties.ENABLED, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(BlockStateProperties.ENABLED); + } + + @Override + protected ItemInteractionResult useItemOn(ItemStack p_316304_, BlockState state, Level level, BlockPos pos, Player p_316132_, InteractionHand p_316595_, BlockHitResult p_316140_) { + level.setBlockAndUpdate( + pos, + state.setValue(BlockStateProperties.ENABLED, !state.getValue(BlockStateProperties.ENABLED)) + ); + if (!level.isClientSide) return ItemInteractionResult.SUCCESS; + BlockEntity be = level.getBlockEntity(pos); + if (be == null) return ItemInteractionResult.SUCCESS; + if (!level.getBlockState(pos).getValue(BlockStateProperties.ENABLED)) { + CacheableBERenderingPipeline.getInstance().update(be); + Minecraft.getInstance().player.sendSystemMessage(Component.literal("RenderMode: BER")); + } else { + CacheableBERenderingPipeline.getInstance().update(be); + Minecraft.getInstance().player.sendSystemMessage(Component.literal("RenderMode: Cached BER")); + } + return ItemInteractionResult.SUCCESS; + } + + @Override + protected MapCodec codec() { + return Block.simpleCodec(TheBlock::new); + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos p_153215_, BlockState p_153216_) { + return new TheBlockEntity(p_153215_, p_153216_); + } + } + + public static class TheBlockEntity extends BlockEntity { + + public TheBlockEntity(BlockPos p_155229_, BlockState p_155230_) { + super(THE_BE.get(), p_155229_, p_155230_); + } + } + + public static class TheRenderer implements BlockEntityRenderer { + + public TheRenderer(BlockEntityRendererProvider.Context ctx) { + + } + + @Override + public void render( + TheBlockEntity blockEntity, + float partialTick, + PoseStack poseStack, + MultiBufferSource bufferSource, + int packedLight, + int packedOverlay + ) { + Level level = blockEntity.getLevel(); + BlockPos pos = blockEntity.getBlockPos(); + if (level == null) return; + BlockState blockState = level.getBlockState(pos); + if (blockState.getValue(BlockStateProperties.ENABLED)) return; + renderManyTorches(poseStack, bufferSource, packedLight, packedOverlay); + if (bufferSource instanceof MultiBufferSource.BufferSource buffer){ + buffer.endLastBatch(); + } + } + + private void renderManyTorches(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { + BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); + for (int dx = 1; dx <= 32; dx++) { + for (int dz = 1; dz <= 32; dz++) { + poseStack.pushPose(); + poseStack.translate(dx * 1f / 16f * 4, 1, dz); + dispatcher.renderSingleBlock( + Blocks.TORCH.defaultBlockState(), + poseStack, + bufferSource, + packedLight, + packedOverlay, + ModelData.EMPTY, + RenderType.cutout() + ); + poseStack.popPose(); + } + } + } + + @Override + public void renderCached( + TheBlockEntity blockEntity, + PoseStack poseStack, + MultiBufferSource.BufferSource bufferSource, + float partialTick, + int packedLight, + int packedOverlay + ) { + Level level = blockEntity.getLevel(); + BlockPos pos = blockEntity.getBlockPos(); + if (level == null) return; + BlockState blockState = level.getBlockState(pos); + if (!blockState.getValue(BlockStateProperties.ENABLED)) return; + renderManyTorches(poseStack, bufferSource, packedLight, packedOverlay); + } + } +} From a3e8536cfc37443a15b708fa492355445311cc41 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Wed, 1 Jan 2025 03:46:58 +0800 Subject: [PATCH 2/6] Implement vertex sorting and uploading with blaze3d --- .../client/FullyBufferedBufferSource.java | 44 ++++++++---- .../neoforge/client/cached/CachedRegion.java | 59 +++++++++++----- .../neoforge/client/cached/CompileResult.java | 70 ------------------- .../resources/META-INF/accesstransformer.cfg | 3 - .../neoforge/debug/client/CachedBERTests.java | 14 ++-- 5 files changed, 82 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java diff --git a/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java b/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java index bf6e35c88f7..e44f9fa0408 100644 --- a/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java +++ b/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java @@ -1,16 +1,17 @@ package net.neoforged.neoforge.client; +import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.MeshData; import com.mojang.blaze3d.vertex.VertexBuffer; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.datafixers.types.Func; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; -import net.neoforged.neoforge.client.cached.CompileResult; import org.lwjgl.system.MemoryUtil; import javax.annotation.ParametersAreNonnullByDefault; @@ -22,10 +23,10 @@ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public class FullyBufferedBufferSource extends MultiBufferSource.BufferSource implements AutoCloseable { - private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); private final Map byteBuffers = new HashMap<>(); private final Map bufferBuilders = new HashMap<>(); private final Reference2IntMap indexCountMap = new Reference2IntOpenHashMap<>(); + private final Map meshSorts = new HashMap<>(); public FullyBufferedBufferSource() { super(null, null); @@ -51,30 +52,43 @@ public boolean isEmpty() { public void endBatch(RenderType renderType) { } + @Override + public void endLastBatch() { + } + + @Override + public void endBatch() { + } + public void upload( Function vertexBufferGetter, + Function byteBufferSupplier, Consumer runner ) { for (RenderType renderType : bufferBuilders.keySet()) { runner.accept(() -> { BufferBuilder bufferBuilder = bufferBuilders.get(renderType); ByteBufferBuilder byteBuffer = byteBuffers.get(renderType); - long ptr = byteBuffer.pointer; int compiledVertices = bufferBuilder.vertices * renderType.format.getVertexSize(); if (compiledVertices >= 0) { MeshData mesh = bufferBuilder.build(); + indexCountMap.put(renderType, renderType.mode.indexCount(bufferBuilder.vertices)); if (mesh != null) { - mesh.close(); + if (renderType.sortOnUpload) { + MeshData.SortState sortState = mesh.sortQuads( + byteBufferSupplier.apply(renderType), + RenderSystem.getVertexSorting() + ); + meshSorts.put( + renderType, + sortState + ); + } + VertexBuffer vertexBuffer = vertexBufferGetter.apply(renderType); + vertexBuffer.bind(); + vertexBuffer.upload(mesh); + VertexBuffer.unbind(); } - CompileResult compileResult = new CompileResult( - renderType, - bufferBuilder.vertices, - renderType.format.getVertexSize(), - ptr, - renderType.mode.indexCount(bufferBuilder.vertices) - ); - indexCountMap.put(renderType, renderType.mode.indexCount(bufferBuilder.vertices)); - compileResult.upload(vertexBufferGetter.apply(renderType)); } byteBuffer.close(); bufferBuilders.remove(renderType); @@ -92,6 +106,10 @@ public Reference2IntMap getIndexCountMap() { return indexCountMap; } + public Map getMeshSorts() { + return meshSorts; + } + public void close() { byteBuffers.keySet().forEach(this::close); } diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java b/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java index 1baf7f2d1fd..6c5acc730f7 100644 --- a/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java +++ b/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java @@ -3,9 +3,12 @@ import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.shaders.Uniform; import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.ByteBufferBuilder; +import com.mojang.blaze3d.vertex.MeshData; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexBuffer; import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexSorting; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import net.minecraft.client.Minecraft; @@ -13,6 +16,7 @@ import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.core.BlockPos; import net.minecraft.world.level.ChunkPos; @@ -20,23 +24,23 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.client.FullyBufferedBufferSource; -import net.neoforged.neoforge.client.extensions.IBlockEntityRendererExtension; import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; +import java.util.Set; public class CachedRegion { private final ChunkPos chunkPos; - private final Map buffers = new HashMap<>(); + private Map buffers = new HashMap<>(); + private final Map sortBuffers = new HashMap<>(); + private Map meshSortings = new HashMap<>(); private Reference2IntMap indexCountMap = new Reference2IntOpenHashMap<>(); - private final List blockEntityList = new ArrayList<>(); + private final Set blockEntities = new HashSet<>(); private final CacheableBERenderingPipeline pipeline; private final Minecraft minecraft = Minecraft.getInstance(); @Nullable @@ -53,13 +57,13 @@ public void update(BlockEntity be) { if (lastRebuildTask != null) { lastRebuildTask.cancel(); } - blockEntityList.removeIf(BlockEntity::isRemoved); + blockEntities.removeIf(BlockEntity::isRemoved); if (be.isRemoved()) { - blockEntityList.remove(be); + blockEntities.remove(be); pipeline.submitCompileTask(new RebuildTask()); return; } - blockEntityList.add(be); + blockEntities.add(be); pipeline.submitCompileTask(new RebuildTask()); } @@ -67,8 +71,8 @@ public void blockRemoved(BlockEntity be) { if (lastRebuildTask != null) { lastRebuildTask.cancel(); } - blockEntityList.remove(be); - blockEntityList.removeIf(BlockEntity::isRemoved); + blockEntities.remove(be); + blockEntities.removeIf(BlockEntity::isRemoved); pipeline.submitCompileTask(new RebuildTask()); } @@ -85,6 +89,15 @@ public VertexBuffer getBuffer(RenderType renderType) { return vb; } + private ByteBufferBuilder requestSortBuffer(RenderType renderType) { + if (sortBuffers.containsKey(renderType)) { + return sortBuffers.get(renderType); + } + ByteBufferBuilder builder = new ByteBufferBuilder(4096); + sortBuffers.put(renderType, builder); + return builder; + } + private void renderInternal( Matrix4f frustumMatrix, Matrix4f projectionMatrix, @@ -107,6 +120,7 @@ private void renderInternal( public void releaseBuffers() { buffers.values().forEach(VertexBuffer::close); + sortBuffers.values().forEach(ByteBufferBuilder::close); } private void renderLayer( @@ -122,7 +136,6 @@ private void renderLayer( renderType.setupRenderState(); ShaderInstance shader = RenderSystem.getShader(); shader.setDefaultUniforms(VertexFormat.Mode.QUADS, frustumMatrix, projectionMatrix, window); - shader.apply(); Uniform uniform = shader.CHUNK_OFFSET; if (uniform != null) { uniform.set( @@ -130,10 +143,21 @@ private void renderLayer( (float) -cameraPosition.y, (float) -cameraPosition.z ); - uniform.upload(); } vertexBuffer.bind(); - GL11.glDrawElements(GL15.GL_TRIANGLES, indexCount, vertexBuffer.sequentialIndices.type().asGLType, 0L); + if (renderType.sortOnUpload) { + MeshData.SortState sortState = this.meshSortings.get(renderType); + if (sortState != null) { + ByteBufferBuilder.Result result = sortState.buildSortedIndexBuffer( + this.requestSortBuffer(renderType), + VertexSorting.byDistance(cameraPosition.toVector3f()) + ); + if (result != null) { + vertexBuffer.uploadIndexBuffer(result); + } + } + } + vertexBuffer.drawWithShader(frustumMatrix, projectionMatrix, shader); VertexBuffer.unbind(); if (uniform != null) { uniform.set(0.0F, 0.0F, 0.0F); @@ -151,12 +175,12 @@ public void run() { CachedRegion.this.isEmpty = true; FullyBufferedBufferSource bufferSource = new FullyBufferedBufferSource(); float partialTick = Minecraft.getInstance().getTimer().getGameTimeDeltaPartialTick(false); - for (BlockEntity be : new ArrayList<>(blockEntityList)) { + for (BlockEntity be : new ArrayList<>(blockEntities)) { if (cancelled) { bufferSource.close(); return; } - IBlockEntityRendererExtension renderer = Minecraft.getInstance() + BlockEntityRenderer renderer = Minecraft.getInstance() .getBlockEntityRenderDispatcher() .getRenderer(be); if (renderer == null) continue; @@ -187,8 +211,11 @@ public void run() { CachedRegion.this.isEmpty = bufferSource.isEmpty(); bufferSource.upload( CachedRegion.this::getBuffer, + CachedRegion.this::requestSortBuffer, pipeline::submitUploadTask ); + + CachedRegion.this.meshSortings = bufferSource.getMeshSorts(); CachedRegion.this.indexCountMap = bufferSource.getIndexCountMap(); lastRebuildTask = null; } diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java b/src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java deleted file mode 100644 index da3626daeaf..00000000000 --- a/src/main/java/net/neoforged/neoforge/client/cached/CompileResult.java +++ /dev/null @@ -1,70 +0,0 @@ -package net.neoforged.neoforge.client.cached; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexBuffer; -import com.mojang.blaze3d.vertex.VertexFormat; -import net.minecraft.client.renderer.RenderType; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL15C; -import org.lwjgl.system.MemoryUtil; - -import java.util.Objects; - -public class CompileResult { - private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); - private final RenderType renderType; - private final int vertexCount; - private final int vertexSize; - private final long vertexBufferPtr; - final int indexCount; - private boolean freed = false; - - public CompileResult( - RenderType renderType, - int vertexCount, - int vertexSize, - long vertexBufferPtr, - int indexCount - ) { - this.renderType = renderType; - this.vertexCount = vertexCount; - this.vertexSize = vertexSize; - this.vertexBufferPtr = vertexBufferPtr; - this.indexCount = indexCount; - } - - public void upload(VertexBuffer vertexBuffer) { - if (freed) return; - VertexFormat.Mode mode = renderType.mode; - vertexBuffer.bind(); - if (vertexBuffer.format != null) { - vertexBuffer.format.clearBufferState(); - } - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBuffer.vertexBufferId); - renderType.format.setupBufferState(); - vertexBuffer.format = renderType.format; - GL15C.nglBufferData(GL15.GL_ARRAY_BUFFER, (long) vertexCount * vertexSize, vertexBufferPtr, GL15.GL_STATIC_DRAW); - RenderSystem.AutoStorageIndexBuffer indexBuffer = RenderSystem.getSequentialBuffer(mode); - if (indexBuffer != vertexBuffer.sequentialIndices || !indexBuffer.hasStorage(indexCount)) { - indexBuffer.bind(indexCount); - } - vertexBuffer.sequentialIndices = indexBuffer; - VertexBuffer.unbind(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof CompileResult that)) return false; - return vertexCount == that.vertexCount - && vertexSize == that.vertexSize - && vertexBufferPtr == that.vertexBufferPtr - && indexCount == that.indexCount - && freed == that.freed - && Objects.equals(renderType, that.renderType); - } - - @Override - public int hashCode() { - return Objects.hash(renderType, vertexCount, vertexSize, vertexBufferPtr, indexCount, freed); - } -} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 7ebb0784023..c1b024631f1 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -503,10 +503,7 @@ public net.minecraft.world.item.enchantment.Enchantment blockHitContext(Lnet/min public net.minecraft.world.item.enchantment.Enchantment applyEffects(Ljava/util/List;Lnet/minecraft/world/level/storage/loot/LootContext;Ljava/util/function/Consumer;)V #Cached BlockEntityRenderer pipeline -public com.mojang.blaze3d.vertex.VertexBuffer format public com.mojang.blaze3d.vertex.VertexBuffer vertexBufferId public com.mojang.blaze3d.vertex.VertexBuffer indexBufferId public com.mojang.blaze3d.vertex.VertexBuffer arrayObjectId -public com.mojang.blaze3d.vertex.VertexBuffer sequentialIndices -public com.mojang.blaze3d.vertex.ByteBufferBuilder pointer public com.mojang.blaze3d.vertex.BufferBuilder vertices \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java index ff4c4de1c4a..e3d5e572721 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java @@ -42,9 +42,9 @@ @ForEachTest(side = Dist.CLIENT, groups = {"client.event", "event"}) public class CachedBERTests { - public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.Blocks.createBlocks("neotests"); + public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.Blocks.createBlocks("neotests_cached_ber"); public static final DeferredRegister> BLOCK_ENTITY_TYPES = - DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, "neotests"); + DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, "neotests_cached_ber"); public static final DeferredBlock THE_BLOCK = BLOCKS.register( "not_enough_vertexes", @@ -144,19 +144,20 @@ public void render( BlockPos pos = blockEntity.getBlockPos(); if (level == null) return; BlockState blockState = level.getBlockState(pos); + if (!blockState.is(THE_BLOCK.get())) return; if (blockState.getValue(BlockStateProperties.ENABLED)) return; renderManyTorches(poseStack, bufferSource, packedLight, packedOverlay); - if (bufferSource instanceof MultiBufferSource.BufferSource buffer){ + if (bufferSource instanceof MultiBufferSource.BufferSource buffer) { buffer.endLastBatch(); } } private void renderManyTorches(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); - for (int dx = 1; dx <= 32; dx++) { - for (int dz = 1; dz <= 32; dz++) { + for (int dx = 0; dx < 32; dx++) { + for (int dz = 0; dz < 32; dz++) { poseStack.pushPose(); - poseStack.translate(dx * 1f / 16f * 4, 1, dz); + poseStack.translate(dx * 0.25, 1, dz * 0.25); dispatcher.renderSingleBlock( Blocks.TORCH.defaultBlockState(), poseStack, @@ -184,6 +185,7 @@ public void renderCached( BlockPos pos = blockEntity.getBlockPos(); if (level == null) return; BlockState blockState = level.getBlockState(pos); + if (!blockState.is(THE_BLOCK.get())) return; if (!blockState.getValue(BlockStateProperties.ENABLED)) return; renderManyTorches(poseStack, bufferSource, packedLight, packedOverlay); } From b499eff94d24503c952b7a7b722d87729d78d2e2 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Wed, 1 Jan 2025 15:54:36 +0800 Subject: [PATCH 3/6] Add javadoc for CacheableBERenderingPipeline --- .../net/minecraft/client/Minecraft.java.patch | 2 +- .../client/renderer/LevelRenderer.java.patch | 4 +- .../neoforge/client/ClientHooks.java | 14 +- .../client/FullyBufferedBufferSource.java | 47 +++---- .../CacheableBERenderingPipeline.java | 50 +++++-- .../{cached => block}/CachedRegion.java | 108 ++++++++------- .../{cached => block}/package-info.java | 5 +- .../IBlockEntityRendererExtension.java | 14 +- .../neoforge/debug/client/CachedBERTests.java | 126 ++++++++++-------- 9 files changed, 214 insertions(+), 156 deletions(-) rename src/main/java/net/neoforged/neoforge/client/{cached => block}/CacheableBERenderingPipeline.java (68%) rename src/main/java/net/neoforged/neoforge/client/{cached => block}/CachedRegion.java (75%) rename src/main/java/net/neoforged/neoforge/client/{cached => block}/package-info.java (87%) diff --git a/patches/net/minecraft/client/Minecraft.java.patch b/patches/net/minecraft/client/Minecraft.java.patch index 2a2d363e4a2..012ee51c409 100644 --- a/patches/net/minecraft/client/Minecraft.java.patch +++ b/patches/net/minecraft/client/Minecraft.java.patch @@ -356,7 +356,7 @@ } private void updateLevelInEngines(@Nullable ClientLevel p_91325_) { -+ net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline.updateLevel(p_91325_); ++ net.neoforged.neoforge.client.ClientHooks.onUpdateLevel(p_91325_); this.levelRenderer.setLevel(p_91325_); this.particleEngine.setLevel(p_91325_); this.blockEntityRenderDispatcher.setLevel(p_91325_); diff --git a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch index 4ece15fcfd9..ac0fc32d1cc 100644 --- a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch @@ -43,10 +43,10 @@ profilerfiller.popPush("terrain_setup"); this.setupRender(p_109604_, frustum, flag, this.minecraft.player.isSpectator()); profilerfiller.popPush("compile_sections"); -+ net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline.getInstance().runTasks(); ++ net.neoforged.neoforge.client.ClientHooks.onCompileSectionsPre(); this.compileSections(p_109604_); profilerfiller.popPush("terrain"); -+ net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline.getInstance().render(p_254120_, p_323920_); ++ net.neoforged.neoforge.client.ClientHooks.onRenderTerrainPre(p_254120_, p_323920_); this.renderSectionLayer(RenderType.solid(), d0, d1, d2, p_254120_, p_323920_); + this.minecraft.getModelManager().getAtlas(TextureAtlas.LOCATION_BLOCKS).setBlurMipmap(false, this.minecraft.options.mipmapLevels().get() > 0); // Neo: fix flickering leaves when mods mess up the blurMipmap settings this.renderSectionLayer(RenderType.cutoutMipped(), d0, d1, d2, p_254120_, p_323920_); diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 0b088a25f5f..a933b940e75 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -137,7 +137,7 @@ import net.neoforged.fml.ModLoader; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.asm.enumextension.ExtensionInfo; -import net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline; +import net.neoforged.neoforge.client.block.CacheableBERenderingPipeline; import net.neoforged.neoforge.client.entity.animation.json.AnimationTypeManager; import net.neoforged.neoforge.client.event.AddSectionGeometryEvent; import net.neoforged.neoforge.client.event.CalculateDetachedCameraDistanceEvent; @@ -796,6 +796,18 @@ public static void onBlockEntityRemoved(BlockEntity blockEntity) { CacheableBERenderingPipeline.getInstance().blockRemoved(blockEntity); } + public static void onRenderTranslucentPre(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { + CacheableBERenderingPipeline.getInstance().render(frustumMatrix, projectionMatrix); + } + + public static void onCompileSectionsPre() { + CacheableBERenderingPipeline.getInstance().runTasks(); + } + + public static void onUpdateLevel(ClientLevel level) { + CacheableBERenderingPipeline.updateLevel(level); + } + public static Font getTooltipFont(ItemStack stack, Font fallbackFont) { Font stackFont = IClientItemExtensions.of(stack).getFont(stack, IClientItemExtensions.FontContext.TOOLTIP); return stackFont == null ? fallbackFont : stackFont; diff --git a/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java b/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java index e44f9fa0408..76319f02a97 100644 --- a/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java +++ b/src/main/java/net/neoforged/neoforge/client/FullyBufferedBufferSource.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + package net.neoforged.neoforge.client; import com.mojang.blaze3d.systems.RenderSystem; @@ -6,19 +11,16 @@ import com.mojang.blaze3d.vertex.MeshData; import com.mojang.blaze3d.vertex.VertexBuffer; import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.datafixers.types.Func; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; -import org.lwjgl.system.MemoryUtil; - -import javax.annotation.ParametersAreNonnullByDefault; import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderType; @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault @@ -39,9 +41,8 @@ private ByteBufferBuilder getByteBuffer(RenderType renderType) { @Override public VertexConsumer getBuffer(RenderType renderType) { return bufferBuilders.computeIfAbsent( - renderType, - it -> new BufferBuilder(getByteBuffer(it), it.mode, it.format) - ); + renderType, + it -> new BufferBuilder(getByteBuffer(it), it.mode, it.format)); } public boolean isEmpty() { @@ -49,22 +50,18 @@ public boolean isEmpty() { } @Override - public void endBatch(RenderType renderType) { - } + public void endBatch(RenderType renderType) {} @Override - public void endLastBatch() { - } + public void endLastBatch() {} @Override - public void endBatch() { - } + public void endBatch() {} public void upload( - Function vertexBufferGetter, - Function byteBufferSupplier, - Consumer runner - ) { + Function vertexBufferGetter, + Function byteBufferSupplier, + Consumer runner) { for (RenderType renderType : bufferBuilders.keySet()) { runner.accept(() -> { BufferBuilder bufferBuilder = bufferBuilders.get(renderType); @@ -76,13 +73,11 @@ public void upload( if (mesh != null) { if (renderType.sortOnUpload) { MeshData.SortState sortState = mesh.sortQuads( - byteBufferSupplier.apply(renderType), - RenderSystem.getVertexSorting() - ); + byteBufferSupplier.apply(renderType), + RenderSystem.getVertexSorting()); meshSorts.put( - renderType, - sortState - ); + renderType, + sortState); } VertexBuffer vertexBuffer = vertexBufferGetter.apply(renderType); vertexBuffer.bind(); diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java b/src/main/java/net/neoforged/neoforge/client/block/CacheableBERenderingPipeline.java similarity index 68% rename from src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java rename to src/main/java/net/neoforged/neoforge/client/block/CacheableBERenderingPipeline.java index 69e3202525b..c1bf064da97 100644 --- a/src/main/java/net/neoforged/neoforge/client/cached/CacheableBERenderingPipeline.java +++ b/src/main/java/net/neoforged/neoforge/client/block/CacheableBERenderingPipeline.java @@ -1,5 +1,14 @@ -package net.neoforged.neoforge.client.cached; +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.neoforged.neoforge.client.block; + +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.Map; +import java.util.Queue; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; @@ -9,11 +18,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import java.util.ArrayDeque; -import java.util.HashMap; -import java.util.Map; -import java.util.Queue; - public class CacheableBERenderingPipeline { @Nullable private static CacheableBERenderingPipeline instance; @@ -45,6 +49,11 @@ public void runTasks() { } } + /** + * Updates the rendering pipeline instance with a new level context. + * + * @param level The new ClientLevel instance that the rendering pipeline should be updated to use. + */ public static void updateLevel(ClientLevel level) { if (instance != null) { instance.releaseBuffers(); @@ -52,19 +61,30 @@ public static void updateLevel(ClientLevel level) { instance = new CacheableBERenderingPipeline(level); } + /** + * Notifies the pipeline that a {@link BlockEntity} has been removed. + * This method will be automatically called when a {@link BlockEntity} has been removed. + * + * @param be The removed {@link BlockEntity} + */ public void blockRemoved(BlockEntity be) { IBlockEntityRendererExtension renderer = Minecraft.getInstance() - .getBlockEntityRenderDispatcher() - .getRenderer(be); + .getBlockEntityRenderDispatcher() + .getRenderer(be); if (renderer == null) return; ChunkPos chunkPos = new ChunkPos(be.getBlockPos()); getRenderRegion(chunkPos).blockRemoved(be); } + /** + * Notifies the pipeline that a {@link BlockEntity} has been updated and the cache should be rebuilt. + * + * @param be The updated {@link BlockEntity} + */ public void update(BlockEntity be) { BlockEntityRenderer renderer = Minecraft.getInstance() - .getBlockEntityRenderDispatcher() - .getRenderer(be); + .getBlockEntityRenderDispatcher() + .getRenderer(be); if (renderer == null) return; ChunkPos chunkPos = new ChunkPos(be.getBlockPos()); getRenderRegion(chunkPos).update(be); @@ -78,6 +98,9 @@ public void submitCompileTask(Runnable task) { pendingCompiles.add(task); } + /** + * Releases all buffers in use and mark current pipeline instance as invalid. + */ public void releaseBuffers() { regions.values().forEach(CachedRegion::releaseBuffers); valid = false; @@ -87,6 +110,13 @@ public void render(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { regions.values().forEach(it -> it.render(frustumMatrix, projectionMatrix)); } + /** + * Retrieves the current instance of the CacheableBERenderingPipeline. + * + * @return The current instance of the CacheableBERenderingPipeline, + * or null if there has no {@link ClientLevel} in current {@link Minecraft} client. + */ + @Nullable public static CacheableBERenderingPipeline getInstance() { return instance; } diff --git a/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java b/src/main/java/net/neoforged/neoforge/client/block/CachedRegion.java similarity index 75% rename from src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java rename to src/main/java/net/neoforged/neoforge/client/block/CachedRegion.java index 6c5acc730f7..0ad3382d34e 100644 --- a/src/main/java/net/neoforged/neoforge/client/cached/CachedRegion.java +++ b/src/main/java/net/neoforged/neoforge/client/block/CachedRegion.java @@ -1,4 +1,9 @@ -package net.neoforged.neoforge.client.cached; +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.client.block; import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.shaders.Uniform; @@ -11,6 +16,14 @@ import com.mojang.blaze3d.vertex.VertexSorting; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; @@ -27,13 +40,6 @@ import org.jetbrains.annotations.Nullable; import org.joml.Matrix4f; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - public class CachedRegion { private final ChunkPos chunkPos; private Map buffers = new HashMap<>(); @@ -53,6 +59,13 @@ public CachedRegion(ChunkPos chunkPos, CacheableBERenderingPipeline pipeline) { this.pipeline = pipeline; } + /** + * Updates the block entities collection and triggers a rebuild of the region. + *

+ * + * @see CacheableBERenderingPipeline#update(BlockEntity) + * @param be The block entity to update. + */ public void update(BlockEntity be) { if (lastRebuildTask != null) { lastRebuildTask.cancel(); @@ -67,6 +80,16 @@ public void update(BlockEntity be) { pipeline.submitCompileTask(new RebuildTask()); } + /** + * Handles the removal of a block entity from the system and initiates a cache rebuild. + *

+ * When a block entity is removed, this method is called to update the internal state of the system. + * It cancels any ongoing rebuild tasks, removes the specified block entity from the collection, + * cleans up any other removed block entities, and then submits a new rebuild task to the pipeline. + * + * @see CacheableBERenderingPipeline#blockRemoved(BlockEntity) + * @param be The block entity that has been removed. + */ public void blockRemoved(BlockEntity be) { if (lastRebuildTask != null) { lastRebuildTask.cancel(); @@ -99,10 +122,9 @@ private ByteBufferBuilder requestSortBuffer(RenderType renderType) { } private void renderInternal( - Matrix4f frustumMatrix, - Matrix4f projectionMatrix, - Collection renderTypes - ) { + Matrix4f frustumMatrix, + Matrix4f projectionMatrix, + Collection renderTypes) { if (isEmpty) return; RenderSystem.enableBlend(); Window window = Minecraft.getInstance().getWindow(); @@ -111,7 +133,9 @@ private void renderInternal( if (cameraPosition.distanceTo(new Vec3(chunkPos.x * 16, cameraPosition.y, chunkPos.z * 16)) > renderDistance) { return; } - for (RenderType renderType : renderTypes) { + List renderingOrders = new ArrayList<>(renderTypes); + renderingOrders.sort(Comparator.comparingInt(a -> (a.sortOnUpload ? 1 : 0))); + for (RenderType renderType : renderingOrders) { VertexBuffer vb = buffers.get(renderType); if (vb == null) continue; renderLayer(renderType, vb, frustumMatrix, projectionMatrix, cameraPosition, window); @@ -124,13 +148,12 @@ public void releaseBuffers() { } private void renderLayer( - RenderType renderType, - VertexBuffer vertexBuffer, - Matrix4f frustumMatrix, - Matrix4f projectionMatrix, - Vec3 cameraPosition, - Window window - ) { + RenderType renderType, + VertexBuffer vertexBuffer, + Matrix4f frustumMatrix, + Matrix4f projectionMatrix, + Vec3 cameraPosition, + Window window) { int indexCount = indexCountMap.getInt(renderType); if (indexCount <= 0) return; renderType.setupRenderState(); @@ -139,19 +162,17 @@ private void renderLayer( Uniform uniform = shader.CHUNK_OFFSET; if (uniform != null) { uniform.set( - (float) -cameraPosition.x, - (float) -cameraPosition.y, - (float) -cameraPosition.z - ); + (float) -cameraPosition.x, + (float) -cameraPosition.y, + (float) -cameraPosition.z); } vertexBuffer.bind(); if (renderType.sortOnUpload) { MeshData.SortState sortState = this.meshSortings.get(renderType); if (sortState != null) { ByteBufferBuilder.Result result = sortState.buildSortedIndexBuffer( - this.requestSortBuffer(renderType), - VertexSorting.byDistance(cameraPosition.toVector3f()) - ); + this.requestSortBuffer(renderType), + VertexSorting.byDistance(cameraPosition.toVector3f())); if (result != null) { vertexBuffer.uploadIndexBuffer(result); } @@ -181,8 +202,8 @@ public void run() { return; } BlockEntityRenderer renderer = Minecraft.getInstance() - .getBlockEntityRenderDispatcher() - .getRenderer(be); + .getBlockEntityRenderDispatcher() + .getRenderer(be); if (renderer == null) continue; Level level = be.getLevel(); int packedLight; @@ -194,26 +215,23 @@ public void run() { poseStack.pushPose(); BlockPos pos = be.getBlockPos(); poseStack.translate( - pos.getX(), - pos.getY(), - pos.getZ() - ); + pos.getX(), + pos.getY(), + pos.getZ()); renderer.renderCached( - be, - poseStack, - bufferSource, - partialTick, - packedLight, - OverlayTexture.NO_OVERLAY - ); + be, + poseStack, + bufferSource, + partialTick, + packedLight, + OverlayTexture.NO_OVERLAY); poseStack.popPose(); } CachedRegion.this.isEmpty = bufferSource.isEmpty(); bufferSource.upload( - CachedRegion.this::getBuffer, - CachedRegion.this::requestSortBuffer, - pipeline::submitUploadTask - ); + CachedRegion.this::getBuffer, + CachedRegion.this::requestSortBuffer, + pipeline::submitUploadTask); CachedRegion.this.meshSortings = bufferSource.getMeshSorts(); CachedRegion.this.indexCountMap = bufferSource.getIndexCountMap(); @@ -224,6 +242,4 @@ void cancel() { cancelled = true; } } - - } diff --git a/src/main/java/net/neoforged/neoforge/client/cached/package-info.java b/src/main/java/net/neoforged/neoforge/client/block/package-info.java similarity index 87% rename from src/main/java/net/neoforged/neoforge/client/cached/package-info.java rename to src/main/java/net/neoforged/neoforge/client/block/package-info.java index c27f3e1baaa..6589ff44eb2 100644 --- a/src/main/java/net/neoforged/neoforge/client/cached/package-info.java +++ b/src/main/java/net/neoforged/neoforge/client/block/package-info.java @@ -6,9 +6,8 @@ @FieldsAreNonnullByDefault @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -package net.neoforged.neoforge.client.cached; +package net.neoforged.neoforge.client.block; +import javax.annotation.ParametersAreNonnullByDefault; import net.minecraft.FieldsAreNonnullByDefault; import net.minecraft.MethodsReturnNonnullByDefault; - -import javax.annotation.ParametersAreNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java b/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java index bb2def6c7dc..92daa6071c5 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/IBlockEntityRendererExtension.java @@ -24,12 +24,10 @@ default AABB getRenderBoundingBox(T blockEntity) { } default void renderCached( - T blockEntity, - PoseStack poseStack, - MultiBufferSource.BufferSource bufferSource, - float partialTick, - int packedLight, - int packedOverlay - ) { - } + T blockEntity, + PoseStack poseStack, + MultiBufferSource.BufferSource bufferSource, + float partialTick, + int packedLight, + int packedOverlay) {} } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java index e3d5e572721..8dcb4489f81 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/CachedBERTests.java @@ -1,7 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + package net.neoforged.neoforge.debug.client; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.serialization.MapCodec; +import java.util.function.Supplier; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; @@ -20,6 +26,7 @@ import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; @@ -29,7 +36,7 @@ import net.minecraft.world.phys.BlockHitResult; import net.neoforged.api.distmarker.Dist; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.neoforge.client.cached.CacheableBERenderingPipeline; +import net.neoforged.neoforge.client.block.CacheableBERenderingPipeline; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.DeferredRegister; @@ -38,32 +45,25 @@ import net.neoforged.testframework.annotation.TestHolder; import org.jetbrains.annotations.Nullable; -import java.util.function.Supplier; - -@ForEachTest(side = Dist.CLIENT, groups = {"client.event", "event"}) +@ForEachTest(side = Dist.CLIENT, groups = { "client.event", "event" }) public class CachedBERTests { public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.Blocks.createBlocks("neotests_cached_ber"); - public static final DeferredRegister> BLOCK_ENTITY_TYPES = - DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, "neotests_cached_ber"); + public static final DeferredRegister> BLOCK_ENTITY_TYPES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, "neotests_cached_ber"); public static final DeferredBlock THE_BLOCK = BLOCKS.register( - "not_enough_vertexes", - () -> new TheBlock( - BlockBehaviour.Properties.of() - .noCollission() - .noOcclusion() - .lightLevel(state -> 15) - ) - ); + "not_enough_vertexes", + () -> new TheBlock( + BlockBehaviour.Properties.of() + .noCollission() + .noOcclusion() + .lightLevel(state -> 15))); public static final Supplier> THE_BE = BLOCK_ENTITY_TYPES.register( - "not_enough_vertexes", - () -> BlockEntityType.Builder.of( - TheBlockEntity::new, - THE_BLOCK.get() - ) - .build(null) - ); + "not_enough_vertexes", + () -> BlockEntityType.Builder.of( + TheBlockEntity::new, + THE_BLOCK.get()) + .build(null)); @TestHolder(description = "Register a block with cached BER which adds lots of vertexes") static void registerBlock(final DynamicTest test) { @@ -77,7 +77,6 @@ static private void clientSetup(final FMLClientSetupEvent event) { } public static class TheBlock extends BaseEntityBlock { - protected TheBlock(Properties p_49224_) { super(p_49224_); registerDefaultState(getStateDefinition().any().setValue(BlockStateProperties.ENABLED, false)); @@ -91,9 +90,8 @@ protected void createBlockStateDefinition(StateDefinition.Builder codec() { return Block.simpleCodec(TheBlock::new); @@ -119,54 +122,60 @@ protected MapCodec codec() { } public static class TheBlockEntity extends BlockEntity { - public TheBlockEntity(BlockPos p_155229_, BlockState p_155230_) { super(THE_BE.get(), p_155229_, p_155230_); } } public static class TheRenderer implements BlockEntityRenderer { - - public TheRenderer(BlockEntityRendererProvider.Context ctx) { - - } + public TheRenderer(BlockEntityRendererProvider.Context ctx) {} @Override public void render( - TheBlockEntity blockEntity, - float partialTick, - PoseStack poseStack, - MultiBufferSource bufferSource, - int packedLight, - int packedOverlay - ) { + TheBlockEntity blockEntity, + float partialTick, + PoseStack poseStack, + MultiBufferSource bufferSource, + int packedLight, + int packedOverlay) { Level level = blockEntity.getLevel(); BlockPos pos = blockEntity.getBlockPos(); if (level == null) return; BlockState blockState = level.getBlockState(pos); if (!blockState.is(THE_BLOCK.get())) return; if (blockState.getValue(BlockStateProperties.ENABLED)) return; - renderManyTorches(poseStack, bufferSource, packedLight, packedOverlay); - if (bufferSource instanceof MultiBufferSource.BufferSource buffer) { - buffer.endLastBatch(); - } + renderManyBlocks(poseStack, bufferSource, packedLight, packedOverlay); } - private void renderManyTorches(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { + private void renderManyBlocks(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); for (int dx = 0; dx < 32; dx++) { for (int dz = 0; dz < 32; dz++) { poseStack.pushPose(); - poseStack.translate(dx * 0.25, 1, dz * 0.25); + poseStack.translate(dx, 2, dz); + dispatcher.renderSingleBlock( + Blocks.TORCH.defaultBlockState(), + poseStack, + bufferSource, + packedLight, + packedOverlay, + ModelData.EMPTY, + RenderType.cutout()); + poseStack.popPose(); + } + } + for (int dx = 0; dx < 32; dx++) { + for (int dz = 0; dz < 32; dz++) { + poseStack.pushPose(); + poseStack.translate(dx, 1, dz); dispatcher.renderSingleBlock( - Blocks.TORCH.defaultBlockState(), - poseStack, - bufferSource, - packedLight, - packedOverlay, - ModelData.EMPTY, - RenderType.cutout() - ); + Blocks.BLACK_STAINED_GLASS.defaultBlockState(), + poseStack, + bufferSource, + packedLight, + packedOverlay, + ModelData.EMPTY, + RenderType.translucent()); poseStack.popPose(); } } @@ -174,20 +183,19 @@ private void renderManyTorches(PoseStack poseStack, MultiBufferSource bufferSour @Override public void renderCached( - TheBlockEntity blockEntity, - PoseStack poseStack, - MultiBufferSource.BufferSource bufferSource, - float partialTick, - int packedLight, - int packedOverlay - ) { + TheBlockEntity blockEntity, + PoseStack poseStack, + MultiBufferSource.BufferSource bufferSource, + float partialTick, + int packedLight, + int packedOverlay) { Level level = blockEntity.getLevel(); BlockPos pos = blockEntity.getBlockPos(); if (level == null) return; BlockState blockState = level.getBlockState(pos); if (!blockState.is(THE_BLOCK.get())) return; if (!blockState.getValue(BlockStateProperties.ENABLED)) return; - renderManyTorches(poseStack, bufferSource, packedLight, packedOverlay); + renderManyBlocks(poseStack, bufferSource, packedLight, packedOverlay); } } } From b9f1584eda8d7d55e75427d12542cba5724355b6 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Wed, 1 Jan 2025 16:16:10 +0800 Subject: [PATCH 4/6] Correct the incorrect method names in the patch --- patches/net/minecraft/client/renderer/LevelRenderer.java.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch index ac0fc32d1cc..6ee4bdddfe6 100644 --- a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch @@ -46,7 +46,7 @@ + net.neoforged.neoforge.client.ClientHooks.onCompileSectionsPre(); this.compileSections(p_109604_); profilerfiller.popPush("terrain"); -+ net.neoforged.neoforge.client.ClientHooks.onRenderTerrainPre(p_254120_, p_323920_); ++ net.neoforged.neoforge.client.ClientHooks.onRenderTranslucentPre(p_254120_, p_323920_); this.renderSectionLayer(RenderType.solid(), d0, d1, d2, p_254120_, p_323920_); + this.minecraft.getModelManager().getAtlas(TextureAtlas.LOCATION_BLOCKS).setBlurMipmap(false, this.minecraft.options.mipmapLevels().get() > 0); // Neo: fix flickering leaves when mods mess up the blurMipmap settings this.renderSectionLayer(RenderType.cutoutMipped(), d0, d1, d2, p_254120_, p_323920_); From 2e24e8bcca396cdc03aee494709eaa2a42d8cfe8 Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Wed, 1 Jan 2025 17:34:24 +0800 Subject: [PATCH 5/6] Fix improperly face culling --- .../client/renderer/LevelRenderer.java.patch | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch index 6ee4bdddfe6..c2aca80494c 100644 --- a/patches/net/minecraft/client/renderer/LevelRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/LevelRenderer.java.patch @@ -29,7 +29,7 @@ float f = this.minecraft.level.getRainLevel(1.0F) / (Minecraft.useFancyGraphics() ? 1.0F : 2.0F); if (!(f <= 0.0F)) { RandomSource randomsource = RandomSource.create((long)this.ticks * 312987231L); -@@ -942,18 +_,24 @@ +@@ -942,18 +_,23 @@ RenderSystem.clear(16640, Minecraft.ON_OSX); float f1 = p_109605_.getRenderDistance(); boolean flag1 = this.minecraft.level.effects().isFoggyAt(Mth.floor(d0), Mth.floor(d1)) || this.minecraft.gui.getBossOverlay().shouldCreateWorldFog(); @@ -46,7 +46,6 @@ + net.neoforged.neoforge.client.ClientHooks.onCompileSectionsPre(); this.compileSections(p_109604_); profilerfiller.popPush("terrain"); -+ net.neoforged.neoforge.client.ClientHooks.onRenderTranslucentPre(p_254120_, p_323920_); this.renderSectionLayer(RenderType.solid(), d0, d1, d2, p_254120_, p_323920_); + this.minecraft.getModelManager().getAtlas(TextureAtlas.LOCATION_BLOCKS).setBlurMipmap(false, this.minecraft.options.mipmapLevels().get() > 0); // Neo: fix flickering leaves when mods mess up the blurMipmap settings this.renderSectionLayer(RenderType.cutoutMipped(), d0, d1, d2, p_254120_, p_323920_); @@ -73,13 +72,13 @@ multibuffersource = multibuffersource$buffersource; } -@@ -1027,12 +_,14 @@ +@@ -1027,12 +_,13 @@ multibuffersource$buffersource.endBatch(RenderType.entityCutout(TextureAtlas.LOCATION_BLOCKS)); multibuffersource$buffersource.endBatch(RenderType.entityCutoutNoCull(TextureAtlas.LOCATION_BLOCKS)); multibuffersource$buffersource.endBatch(RenderType.entitySmoothCutout(TextureAtlas.LOCATION_BLOCKS)); + net.neoforged.neoforge.client.ClientHooks.dispatchRenderStage(net.neoforged.neoforge.client.event.RenderLevelStageEvent.Stage.AFTER_ENTITIES, this, posestack, p_254120_, p_323920_, this.ticks, p_109604_, frustum); profilerfiller.popPush("blockentities"); - +- for (SectionRenderDispatcher.RenderSection sectionrenderdispatcher$rendersection : this.visibleSections) { List list = sectionrenderdispatcher$rendersection.getCompiled().getRenderableBlockEntities(); if (!list.isEmpty()) { @@ -156,6 +155,14 @@ } this.minecraft.debugRenderer.render(posestack, multibuffersource$buffersource, d0, d1, d2); +@@ -1133,6 +_,7 @@ + multibuffersource$buffersource.endBatch(RenderType.entityGlint()); + multibuffersource$buffersource.endBatch(RenderType.entityGlintDirect()); + multibuffersource$buffersource.endBatch(RenderType.waterMask()); ++ net.neoforged.neoforge.client.ClientHooks.onRenderTranslucentPre(p_254120_, p_323920_); + this.renderBuffers.crumblingBufferSource().endBatch(); + if (this.transparencyChain != null) { + multibuffersource$buffersource.endBatch(RenderType.lines()); @@ -1147,9 +_,13 @@ this.particlesTarget.copyDepthFrom(this.minecraft.getMainRenderTarget()); RenderStateShard.PARTICLES_TARGET.setupRenderState(); From 29f6c723c85f44dce90dc587366fdba75c77965e Mon Sep 17 00:00:00 2001 From: ZhuRuoLing Date: Wed, 1 Jan 2025 21:14:56 +0800 Subject: [PATCH 6/6] Remove unused access transformer entries --- src/main/resources/META-INF/accesstransformer.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index c1b024631f1..955c379e5b1 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -503,7 +503,4 @@ public net.minecraft.world.item.enchantment.Enchantment blockHitContext(Lnet/min public net.minecraft.world.item.enchantment.Enchantment applyEffects(Ljava/util/List;Lnet/minecraft/world/level/storage/loot/LootContext;Ljava/util/function/Consumer;)V #Cached BlockEntityRenderer pipeline -public com.mojang.blaze3d.vertex.VertexBuffer vertexBufferId -public com.mojang.blaze3d.vertex.VertexBuffer indexBufferId -public com.mojang.blaze3d.vertex.VertexBuffer arrayObjectId public com.mojang.blaze3d.vertex.BufferBuilder vertices \ No newline at end of file