Update Iris compat

Giving up on waiting for the NeoForge version, so we reshuffle this to
avoid calling getUnderlyingVertexFormat — the RenderType's vertex format
reports this fine.

I did consider dropping all this code and just falling back to the slow
fixed width font renderer when the vertex format doesn't match our
direct one — the overhead is much lower than what it used to be — but
does still touching some internals of BufferBuilder, so probably best
avoided right now.
This commit is contained in:
Jonathan Coates
2026-09-17 15:35:20 +01:00
parent 704ed90395
commit 12a8b185ee
8 changed files with 52 additions and 58 deletions
@@ -50,6 +50,7 @@ repositories {
includeGroup("me.shedaniel.cloth")
includeGroup("me.shedaniel")
includeGroup("mezz.jei")
includeGroup("net.caffeinemc")
includeModule("com.terraformersmc", "modmenu")
}
}
+13 -14
View File
@@ -9,7 +9,7 @@
minecraft = "26.3"
fabric-api = "0.160.5+26.3"
fabric-loader = "0.19.4"
neoForge = "26.3.0.0-beta"
neoForge = "26.3.0.3-beta"
neoMergeTool = "2.0.0"
mixin = "0.8.5"
@@ -34,12 +34,11 @@ nightConfig = "3.9.0"
# Minecraft mods
iris-fabric = "1.11.6+26.3-fabric"
iris-forge = "1.9.1+1.21.7-neoforge"
jei = "29.5.0.26"
modmenu = "16.0.0-rc.1"
jei = "31.0.0.5"
modmenu = "21.0.0-beta.1"
rei = "26.1.818"
sodium-fabric = "mc1.21.11-0.8.0-fabric"
sodium-forge = "mc1.21.11-0.8.0-neoforge"
mixinExtra = "0.3.5"
sodium = "0.9.2+mc26.3"
mixinExtra = "0.5.4"
create-forge = "6.0.0-6"
create-fabric = "6.0.7.0+mc1.20.1-build.1716"
@@ -101,17 +100,17 @@ fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fab
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
iris-fabric = { module = "maven.modrinth:iris", version.ref = "iris-fabric" }
iris-forge = { module = "maven.modrinth:iris", version.ref = "iris-forge" }
jei-api = { module = "mezz.jei:jei-26.1.2-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-26.1.2-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-26.1.2-neoforge", version.ref = "jei" }
jei-api = { module = "mezz.jei:jei-26.3-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-26.3-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-26.3-neoforge", version.ref = "jei" }
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
sodium-fabric = { module = "maven.modrinth:sodium", version.ref = "sodium.fabric" }
sodium-forge = { module = "maven.modrinth:sodium", version.ref = "sodium.forge" }
sodium-fabric = { module = "net.caffeinemc:sodium-fabric", version.ref = "sodium" }
sodium-forge = { module = "net.caffeinemc:sodium-neoforge", version.ref = "sodium" }
# Testing
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
@@ -168,11 +167,11 @@ annotations = ["checkerFramework", "jetbrainsAnnotations", "jspecify"]
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
# Minecraft
externalMods-common = ["iris-forge", "jei-api", "nightConfig-core", "nightConfig-toml"]
externalMods-common = ["iris-fabric", "jei-api", "nightConfig-core", "nightConfig-toml"]
externalMods-forge-compile = ["iris-forge", "jei-api"]
externalMods-forge-runtime = []
externalMods-forge-runtime = ["jei-forge"]
externalMods-fabric-compile = ["iris-fabric", "jei-api", "rei-api", "rei-builtin"]
externalMods-fabric-runtime = []
externalMods-fabric-runtime = ["modmenu", "jei-fabric"]
# Testing
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
@@ -30,21 +30,22 @@ public class IrisShaderMod implements ShaderMod.Provider {
}
@Override
public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int quadCount, IntFunction<ByteBuffer> makeBuffer) {
return new IrisQuadEmitter(quadCount, makeBuffer);
public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(VertexFormat format, int quadCount, IntFunction<ByteBuffer> makeBuffer) {
// If we're using the vanilla vertex format, fall back to our default emitter.
if (format == DirectFixedWidthFontRenderer.VERTEX_FORMAT) {
return super.getQuadEmitter(format, quadCount, makeBuffer);
}
var sink = IrisApi.getInstance().createTextVertexSink(quadCount, makeBuffer);
// TODO(26.3): Verify the vertex format matches what we expect once Iris has updated.
return new IrisQuadEmitter(sink);
}
private static final class IrisQuadEmitter extends DirectFixedWidthFontRenderer.QuadEmitter {
private final IrisTextVertexSink sink;
private IrisQuadEmitter(int vertexCount, IntFunction<ByteBuffer> builder) {
sink = IrisApi.getInstance().createTextVertexSink(vertexCount, builder);
}
@Override
public VertexFormat format() {
// TODO(26.3): return sink.getUnderlyingVertexFormat();
throw new UnsupportedOperationException();
private IrisQuadEmitter(IrisTextVertexSink sink) {
this.sink = sink;
}
@Override
@@ -4,6 +4,7 @@
package dan200.computercraft.client.integration;
import com.mojang.renderpearl.api.vertex.VertexFormat;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import java.nio.ByteBuffer;
@@ -11,8 +12,6 @@ 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.
*/
@@ -33,12 +32,13 @@ public class ShaderMod {
/**
* Get an appropriate quad emitter for use with a vertex buffer and {@link DirectFixedWidthFontRenderer} .
*
* @param format The vertex format to write.
* @param quadCount The number of quads.
* @param makeBuffer A function to allocate a temporary buffer.
* @return The quad emitter.
*/
public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(int quadCount, IntFunction<ByteBuffer> makeBuffer) {
return new DirectFixedWidthFontRenderer.ByteBufferEmitter(makeBuffer.apply(TERMINAL_TEXT.format().getVertexSize() * quadCount * 4));
public DirectFixedWidthFontRenderer.QuadEmitter getQuadEmitter(VertexFormat format, int quadCount, IntFunction<ByteBuffer> makeBuffer) {
return new DirectFixedWidthFontRenderer.ByteBufferEmitter(format, makeBuffer.apply(format.getVertexSize() * quadCount * 4));
}
public interface Provider {
@@ -101,7 +101,7 @@ public class MonitorBlockEntityRenderer implements BoundedBlockEntityRenderer<Mo
// Draw the contents
var terminal = state.terminal.getTerminal();
if (terminal != null) {
if (terminal != null && !ShaderMod.get().isRenderingShadowPass()) {
// Draw a terminal
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
@@ -126,14 +126,15 @@ public class MonitorBlockEntityRenderer implements BoundedBlockEntityRenderer<Mo
private static void prepareTerminal(
ClientMonitor monitor, MonitorRenderState renderState, Terminal terminal, float xMargin, float yMargin
) {
var vertexFormat = FixedWidthFontRenderer.TERMINAL_TEXT.format();
var redraw = monitor.pollTerminalChanged();
if (renderState.vertexBuffer == null) redraw = true;
if (renderState.vertexBuffer == null || renderState.vertexFormat != vertexFormat) redraw = true;
if (redraw) {
// Cursor, Foreground, Background+Margin
var maxQuadCount = 1 + (terminal.getWidth() * terminal.getHeight()) + ((terminal.getWidth() + 2) * (terminal.getHeight() + 2));
var maxVertexCount = 4 * maxQuadCount;
var sink = ShaderMod.get().getQuadEmitter(maxQuadCount, MonitorBlockEntityRenderer::getBuffer);
var sink = ShaderMod.get().getQuadEmitter(vertexFormat, maxQuadCount, MonitorBlockEntityRenderer::getBuffer);
DirectFixedWidthFontRenderer.drawTerminalBackground(sink, 0, 0, terminal, yMargin, yMargin, xMargin, xMargin);
var vertexCountAfterBackground = sink.vertexCount();
@@ -156,10 +157,10 @@ public class MonitorBlockEntityRenderer implements BoundedBlockEntityRenderer<Mo
var resultBuffer = sink.byteBuffer().flip();
// Ensure our buffer contains the correct number of vertices.
if (resultBuffer.remaining() != sink.format().getVertexSize() * vertexCountAfterCursor) {
if (resultBuffer.remaining() != vertexFormat.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
resultBuffer.limit(), vertexFormat.getVertexSize(), vertexCountAfterCursor, vertexFormat.getVertexSize() * vertexCountAfterCursor
));
}
@@ -177,6 +178,7 @@ public class MonitorBlockEntityRenderer implements BoundedBlockEntityRenderer<Mo
}
}
renderState.vertexFormat = vertexFormat;
renderState.vertexCountAfterBackground = vertexCountAfterBackground;
renderState.vertexCountAfterForeground = vertexCountAfterForeground;
renderState.vertexCountAfterCursor = vertexCountAfterCursor;
@@ -6,8 +6,8 @@ package dan200.computercraft.client.render.monitor;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import com.mojang.renderpearl.api.buffers.GpuBuffer;
import com.mojang.renderpearl.api.vertex.VertexFormat;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import net.minecraft.core.BlockPos;
import org.jspecify.annotations.Nullable;
import java.util.HashSet;
@@ -24,12 +24,8 @@ public final class MonitorRenderState implements ClientMonitor.RenderState {
@GuardedBy("allMonitors")
private static final Set<MonitorRenderState> allMonitors = new HashSet<>();
long lastRenderFrame = -1;
@Nullable
BlockPos lastRenderPos = null;
@Nullable
GpuBuffer vertexBuffer;
@Nullable VertexFormat vertexFormat;
@Nullable GpuBuffer vertexBuffer;
int vertexCountAfterBackground;
int vertexCountAfterForeground;
@@ -27,7 +27,7 @@ import static org.lwjgl.system.MemoryUtil.*;
* There are some limitations here:
* <ul>
* <li>No transformation matrix (not needed for VBOs).</li>
* <li>Only works with {@link DefaultVertexFormat#POSITION_COLOR_TEX_LIGHTMAP}.</li>
* <li>Only works with {@link DefaultVertexFormat#POSITION_TEX_LIGHTMAP_COLOR}.</li>
* <li>The buffer <strong>MUST</strong> be allocated with {@link MemoryUtil}, and not through any other means.</li>
* </ul>
* <p>
@@ -38,6 +38,11 @@ import static org.lwjgl.system.MemoryUtil.*;
* {@link FixedWidthFontRenderer}.
*/
public final class DirectFixedWidthFontRenderer {
/**
* The vertex format {@link DirectFixedWidthFontRenderer} works with.
*/
public static final VertexFormat VERTEX_FORMAT = DefaultVertexFormat.POSITION_TEX_LIGHTMAP_COLOR;
private static final boolean IS_LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
private DirectFixedWidthFontRenderer() {
@@ -173,8 +178,6 @@ public final class DirectFixedWidthFontRenderer {
public abstract ByteBuffer byteBuffer();
public abstract VertexFormat format();
/**
* Emit a quad to this buffer.
*
@@ -199,7 +202,8 @@ public final class DirectFixedWidthFontRenderer {
public static final class ByteBufferEmitter extends QuadEmitter {
private final ByteBuffer buffer;
public ByteBufferEmitter(ByteBuffer buffer) {
public ByteBufferEmitter(VertexFormat format, ByteBuffer buffer) {
if (format != VERTEX_FORMAT) throw new IllegalArgumentException("Unexpected vertex format");
this.buffer = buffer;
}
@@ -208,11 +212,6 @@ public final class DirectFixedWidthFontRenderer {
return buffer;
}
@Override
public VertexFormat format() {
return TERMINAL_TEXT.format();
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, int nativeColour, float u1, float v1, float u2, float v2) {
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, nativeColour, u1, v1, u2, v2);
@@ -224,16 +223,15 @@ public final class DirectFixedWidthFontRenderer {
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
// This provides significant performance gains, at the cost of well, using Unsafe.
// Each vertex is 28 bytes, giving 112 bytes in total. Vertices are of the form (xyz:FFF)(uv1:FF)(uv2:SS)(abgr:BBBB),
// which matches the POSITION_COLOR_TEX_LIGHTMAP vertex format.
// which matches the POSITION_TEX_LIGHTMAP_COLOR vertex format.
var position = buffer.position();
var addr = MemoryUtil.memAddress(buffer);
// We're doing terrible unsafe hacks below, so let's be really sure that what we're doing is reasonable.
if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException();
// Require the pointer to be aligned to a 32-bit boundary.
if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned");
if (TERMINAL_TEXT.format().getVertexSize() != 28) {
throw new IllegalStateException("Incorrect vertex size");
}
if (VERTEX_FORMAT.getVertexSize() != 28) throw new IllegalStateException("Incorrect vertex size");
memPutFloat(addr + 0, x1);
memPutFloat(addr + 4, y1);
@@ -58,12 +58,9 @@ public final class ForgeClientHooks {
@SubscribeEvent
public static void onRenderInHand(RenderHandEvent event) {
// TODO(26.3): Add these to the event in upstream NeoForge.
var minecraft = Minecraft.getInstance();
var heldRenderer = minecraft.gameRenderer.firstPersonHandsAndItemsRenderer;
var playerState = minecraft.gameRenderer.gameRenderState().levelRenderState.playerRenderState;
var heldRenderer = Minecraft.getInstance().gameRenderer.firstPersonHandsAndItemsRenderer;
if (ClientHooks.onRenderHeldItem(
heldRenderer, playerState, event.getPoseStack(), event.getSubmitNodeCollector(), event.getPackedLight(),
heldRenderer, event.getPlayerState(), event.getPoseStack(), event.getSubmitNodeCollector(), event.getPackedLight(),
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
)) {
event.setCanceled(true);