From ec3dd328b3e9829d92c8f676f7d0119b57090713 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 15 Jun 2025 18:03:59 +0100 Subject: [PATCH] Construct ByteBuffer manually in monitor renderer In the 1.21.4 update (9277aa33e97456c94b5f7d1aa54e00e204a316dd), we removed our DirectVertexBuffer class, and switched to vanilla's VertexBuffer. This forced us to use MeshData and thus ByteBufferBuilder instead of allocating the ByteBuffer ourselves. One thing I'd missed with this is that Iris's text vertex sink API requires us to allocate the whole buffer up-front and so the resulting buffer has a limit of the *maximum* number of vertices rendered, not the actual one. This wasn't an issue on 1.21.4, as we didn't check this (I guess we just silently rendered junk??), but for the 1.21.5 update (a1df19667361601c7f37a71074ea7755ccc4019d) we added some extra assertions here, which now fail on Iris. Typically, the whole original change was is now entirely redundant, as Vanilla has removed VertexBuffer entirely, and so we can/should work in terms of raw ByteBuffers again. Fixes #2219. --- .../client/integration/IrisShaderMod.java | 23 +++-- .../client/integration/ShaderMod.java | 13 +-- .../monitor/MonitorBlockEntityRenderer.java | 85 ++++++++++++------- .../text/DirectFixedWidthFontRenderer.java | 25 ++++-- 4 files changed, 89 insertions(+), 57 deletions(-) diff --git a/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java b/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java index 85581bb84..1a6b33bae 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java +++ b/projects/common/src/client/java/dan200/computercraft/client/integration/IrisShaderMod.java @@ -5,18 +5,16 @@ package dan200.computercraft.client.integration; import com.google.auto.service.AutoService; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.VertexFormat; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; import dan200.computercraft.shared.platform.PlatformHelper; import net.irisshaders.iris.api.v0.IrisApi; import net.irisshaders.iris.api.v0.IrisTextVertexSink; import net.minecraft.client.renderer.LightTexture; -import org.jspecify.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; import java.util.Optional; +import java.util.function.IntFunction; @AutoService(ShaderMod.Provider.class) public class IrisShaderMod implements ShaderMod.Provider { @@ -32,21 +30,20 @@ public class IrisShaderMod implements ShaderMod.Provider { } @Override - public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int vertexCount, ByteBufferBuilder makeBuffer) { - return IrisApi.getInstance().getMinorApiRevision() >= 1 - ? new IrisQuadEmitter(vertexCount, makeBuffer) - : super.getQuadEmitter(vertexCount, makeBuffer); + public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int quadCount, IntFunction makeBuffer) { + return new IrisQuadEmitter(quadCount, makeBuffer); } private static final class IrisQuadEmitter extends DirectFixedWidthFontRenderer.QuadEmitter { private final IrisTextVertexSink sink; - private @Nullable ByteBuffer buffer; - private IrisQuadEmitter(int vertexCount, ByteBufferBuilder builder) { - sink = IrisApi.getInstance().createTextVertexSink(vertexCount, i -> { - if (buffer != null) throw new IllegalStateException("Allocated multiple buffers"); - return buffer = MemoryUtil.memByteBuffer(builder.reserve(i), i); - }); + private IrisQuadEmitter(int vertexCount, IntFunction builder) { + sink = IrisApi.getInstance().createTextVertexSink(vertexCount, builder); + } + + @Override + public ByteBuffer byteBuffer() { + return sink.getUnderlyingByteBuffer(); } @Override diff --git a/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java b/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java index 6285229cc..b49f02464 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java +++ b/projects/common/src/client/java/dan200/computercraft/client/integration/ShaderMod.java @@ -4,11 +4,14 @@ package dan200.computercraft.client.integration; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; +import java.nio.ByteBuffer; import java.util.Optional; import java.util.ServiceLoader; +import java.util.function.IntFunction; + +import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.TERMINAL_TEXT; /** * Find the currently loaded shader mod (if present) and provides utilities for interacting with it. @@ -30,12 +33,12 @@ public class ShaderMod { /** * Get an appropriate quad emitter for use with a vertex buffer and {@link DirectFixedWidthFontRenderer} . * - * @param vertexCount The number of vertices. - * @param buffer A function to allocate a temporary buffer. + * @param quadCount The number of quads. + * @param makeBuffer A function to allocate a temporary buffer. * @return The quad emitter. */ - public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int vertexCount, ByteBufferBuilder buffer) { - return new DirectFixedWidthFontRenderer.ByteBufferEmitter(buffer); + public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int quadCount, IntFunction makeBuffer) { + return new DirectFixedWidthFontRenderer.ByteBufferEmitter(makeBuffer.apply(TERMINAL_TEXT.format().getVertexSize() * quadCount * 4)); } public interface Provider { diff --git a/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java b/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java index 1d3bbef15..a6ea0649b 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java +++ b/projects/common/src/client/java/dan200/computercraft/client/render/monitor/MonitorBlockEntityRenderer.java @@ -7,7 +7,6 @@ package dan200.computercraft.client.render.monitor; import com.mojang.blaze3d.buffers.BufferType; import com.mojang.blaze3d.buffers.BufferUsage; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Axis; import dan200.computercraft.annotations.ForgeOverride; @@ -28,7 +27,10 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; +import org.jspecify.annotations.Nullable; +import org.lwjgl.system.MemoryUtil; +import java.nio.ByteBuffer; import java.util.OptionalDouble; import java.util.OptionalInt; @@ -42,7 +44,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer renderState.vertexBuffer.size()) { - if (renderState.vertexBuffer != null) { - renderState.vertexBuffer.close(); - renderState.vertexBuffer = null; - } - renderState.vertexBuffer = RenderSystem.getDevice().createBuffer( - () -> "Monitor at " + monitor.getOrigin().getBlockPos(), - BufferType.VERTICES, BufferUsage.STATIC_WRITE, resultBuffer - ); - } else if (!renderState.vertexBuffer.isClosed()) { - commandEncoder.writeToBuffer(renderState.vertexBuffer, resultBuffer, 0); - } + // Ensure our buffer contains the correct number of vertices. + if (resultBuffer.remaining() != sink.format().getVertexSize() * vertexCountAfterCursor) { + throw new IllegalStateException(String.format( + "Mismatched vertex count. Buffer is %d bytes long, but was expected to be %d (vertex size) * %d (vertex count) = %d bytes.", + resultBuffer.limit(), sink.format().getVertexSize(), vertexCountAfterCursor, sink.format().getVertexSize() * vertexCountAfterCursor + )); } + + // Upload the buffer, reallocating if required. + if (renderState.vertexBuffer == null || resultBuffer.remaining() > renderState.vertexBuffer.size()) { + if (renderState.vertexBuffer != null) { + renderState.vertexBuffer.close(); + renderState.vertexBuffer = null; + } + renderState.vertexBuffer = RenderSystem.getDevice().createBuffer( + () -> "Monitor at " + monitor.getOrigin().getBlockPos(), + BufferType.VERTICES, BufferUsage.STATIC_WRITE, resultBuffer + ); + } else if (!renderState.vertexBuffer.isClosed()) { + commandEncoder.writeToBuffer(renderState.vertexBuffer, resultBuffer, 0); + } + + var mode = FixedWidthFontRenderer.TERMINAL_TEXT.mode(); + indexAfterBackground = mode.indexCount(vertexCountAfterBackground); + indexAfterForeground = mode.indexCount(vertexCountAfterForeground); + indexAfterCursor = mode.indexCount(vertexCountAfterCursor); } - var mode = FixedWidthFontRenderer.TERMINAL_TEXT.mode(); - renderState.indexAfterBackground = mode.indexCount(vertexCountAfterBackground); - renderState.indexAfterForeground = mode.indexCount(vertexCountAfterForeground); - renderState.indexAfterCursor = mode.indexCount(vertexCountAfterCursor); + renderState.indexAfterForeground = indexAfterForeground; + renderState.indexAfterBackground = indexAfterBackground; + renderState.indexAfterCursor = indexAfterCursor; } if (renderState.indexAfterCursor == 0) return; @@ -249,4 +260,14 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer