diff --git a/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java b/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java index 5dc96122..1e07e750 100644 --- a/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java +++ b/src/main/java/li/cil/oc2/client/gui/MachineTerminalWidget.java @@ -31,6 +31,7 @@ public final class MachineTerminalWidget extends GuiComponent { private final Terminal terminal; private int leftPos, topPos; private boolean isMouseOverTerminal; + private Terminal.RendererView rendererView; /////////////////////////////////////////////////////////////////// @@ -55,7 +56,12 @@ public final class MachineTerminalWidget extends GuiComponent { final PoseStack terminalStack = new PoseStack(); terminalStack.translate(leftPos + TERMINAL_X, topPos + TERMINAL_Y, getClient().getItemRenderer().blitOffset); terminalStack.scale(TERMINAL_WIDTH / (float) terminal.getWidth(), TERMINAL_HEIGHT / (float) terminal.getHeight(), 1f); - terminal.render(terminalStack); + + if (rendererView == null) { + rendererView = terminal.getRenderer(); + } + + rendererView.render(terminalStack); } else { final Font font = getClient().font; if (error != null) { @@ -96,7 +102,7 @@ public final class MachineTerminalWidget extends GuiComponent { } else { final byte[] sequence = TerminalInput.getSequence(keyCode, modifiers); if (sequence != null) { - for (byte b : sequence) { + for (final byte b : sequence) { terminal.putInput(b); } } @@ -114,6 +120,10 @@ public final class MachineTerminalWidget extends GuiComponent { public void onClose() { getClient().keyboardHandler.setSendRepeatsToGui(false); + if (rendererView != null) { + terminal.releaseRenderer(rendererView); + rendererView = null; + } } /////////////////////////////////////////////////////////////////// diff --git a/src/main/java/li/cil/oc2/client/renderer/blockentity/ComputerRenderer.java b/src/main/java/li/cil/oc2/client/renderer/blockentity/ComputerRenderer.java index aa21249c..f5d7737b 100644 --- a/src/main/java/li/cil/oc2/client/renderer/blockentity/ComputerRenderer.java +++ b/src/main/java/li/cil/oc2/client/renderer/blockentity/ComputerRenderer.java @@ -39,6 +39,7 @@ public final class ComputerRenderer implements BlockEntityRenderer renderers = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); private transient boolean displayOnly; // Set on client to not send responses to status requests. - private transient boolean hasPendingBell; /////////////////////////////////////////////////////////////////// @@ -93,10 +94,6 @@ public final class Terminal { /////////////////////////////////////////////////////////////////// - public void setDisplayOnly(final boolean value) { - displayOnly = value; - } - public int getWidth() { return WIDTH * CHAR_WIDTH; } @@ -106,17 +103,22 @@ public final class Terminal { } @OnlyIn(Dist.CLIENT) - public void render(final PoseStack stack) { - if (hasPendingBell) { - hasPendingBell = false; - final Minecraft client = Minecraft.getInstance(); - client.execute(() -> client.getSoundManager().play(SimpleSoundInstance.forUI(NoteBlockInstrument.PLING.getSoundEvent(), 1))); - } + public void setDisplayOnly(final boolean value) { + displayOnly = value; + } - if (renderer == null) { - renderer = new Renderer(this); + @OnlyIn(Dist.CLIENT) + public RendererView getRenderer() { + final Renderer renderer = new Renderer(this); + renderers.add(renderer); + return renderer; + } + + @OnlyIn(Dist.CLIENT) + public void releaseRenderer(final RendererView renderer) { + if (renderer instanceof final RendererModel rendererModel) { + renderers.remove(rendererModel); } - ((Renderer) renderer).render(dirty, stack); } public synchronized int readInput() { @@ -173,7 +175,7 @@ public final class Terminal { } } case (byte) '\b' -> setCursorPos(x - 1, y); - case 7 -> hasPendingBell = true; + case 7 -> renderers.forEach(RendererModel::setBell); case 27 -> state = State.ESCAPE; default -> { if (!Character.isISOControl(ch)) { @@ -363,14 +365,14 @@ public final class Terminal { buffer[index] = (byte) ch; colors[index] = color; styles[index] = style; - dirty.accumulateAndGet(1 << y, (prev, next) -> prev | next); + renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(1 << y, (prev, next) -> prev | next)); } private void clear() { Arrays.fill(buffer, (byte) ' '); Arrays.fill(colors, DEFAULT_COLORS); Arrays.fill(styles, DEFAULT_STYLE); - dirty.set((1 << HEIGHT) - 1); + renderers.forEach(model -> model.getDirtyMask().set((1 << HEIGHT) - 1)); } private void clearLine(final int y) { @@ -381,7 +383,7 @@ public final class Terminal { Arrays.fill(buffer, y * WIDTH + fromIndex, y * WIDTH + toIndex, (byte) ' '); Arrays.fill(colors, y * WIDTH + fromIndex, y * WIDTH + toIndex, DEFAULT_COLORS); Arrays.fill(styles, y * WIDTH + fromIndex, y * WIDTH + toIndex, DEFAULT_STYLE); - dirty.accumulateAndGet(1 << y, (prev, next) -> prev | next); + renderers.forEach(model -> model.getDirtyMask().accumulateAndGet(1 << y, (prev, next) -> prev | next)); } private void putNewLine() { @@ -401,13 +403,19 @@ public final class Terminal { Arrays.fill(styles, WIDTH * HEIGHT - WIDTH, WIDTH * HEIGHT, DEFAULT_STYLE); // Offset is baked into buffers so we must rebuild them all. - dirty.set(-1); + renderers.forEach(model -> model.getDirtyMask().set(-1)); } /////////////////////////////////////////////////////////////////// + private interface RendererModel { + AtomicInteger getDirtyMask(); + + void setBell(); + } + @OnlyIn(Dist.CLIENT) - private static final class Renderer { + private static final class Renderer implements RendererModel, RendererView { private static final ResourceLocation LOCATION_FONT_TEXTURE = new ResourceLocation(API.MOD_ID, "textures/font/terminus.png"); private static final int TEXTURE_RESOLUTION = 256; private static final float ONE_OVER_TEXTURE_RESOLUTION = 1.0f / (float) TEXTURE_RESOLUTION; @@ -440,6 +448,9 @@ public final class Terminal { private final Terminal terminal; + private final transient AtomicInteger dirty = new AtomicInteger(-1); + private transient boolean hasPendingBell; + private final Object[] lines = new Object[HEIGHT]; // Cached vertex buffers for rendering, untyped for server. private Object lastMatrix; // Untyped for server. @@ -451,7 +462,14 @@ public final class Terminal { /////////////////////////////////////////////////////////////// - public void render(final AtomicInteger dirty, final PoseStack stack) { + @Override + public void render(final PoseStack stack) { + if (hasPendingBell) { + hasPendingBell = false; + final Minecraft client = Minecraft.getInstance(); + client.execute(() -> client.getSoundManager().play(SimpleSoundInstance.forUI(NoteBlockInstrument.PLING.getSoundEvent(), 1))); + } + validateLineCache(dirty, stack); renderBuffer(); @@ -460,6 +478,16 @@ public final class Terminal { } } + @Override + public AtomicInteger getDirtyMask() { + return dirty; + } + + @Override + public void setBell() { + hasPendingBell = true; + } + /////////////////////////////////////////////////////////////// private void renderBuffer() { @@ -469,7 +497,8 @@ public final class Terminal { final BufferBuilder builder = Tesselator.getInstance().getBuilder(); for (final Object line : lines) { - final ByteBuffer buffer = (ByteBuffer) line; + final ByteBuffer buffer = ((ByteBuffer) line).rewind(); + builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); builder.putBulkData(buffer); builder.end(); @@ -509,10 +538,7 @@ public final class Terminal { builder.end(); final ByteBuffer buffer = builder.popNextBuffer().getSecond(); - final ByteBuffer bufferCopy = ByteBuffer.allocate(buffer.limit()); - bufferCopy.put(buffer); - bufferCopy.flip(); - lines[row] = bufferCopy; + lines[row] = ByteBuffer.allocate(buffer.limit()).put(buffer); builder.clear();