1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-09-28 15:08:47 +00:00

Move the rendering monitor code to the client side

It's kinda funny looking at all the pre-CC:T proxy code, how much this
has come full circle. I think this design is nicer than what 1.12 did,
but it's still slighly embarassing.
This commit is contained in:
Jonathan Coates 2022-11-09 19:52:29 +00:00
parent b36b96e0bc
commit 48285404b9
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
5 changed files with 204 additions and 163 deletions

View File

@ -13,12 +13,12 @@ import dan200.computercraft.client.render.CableHighlightRenderer;
import dan200.computercraft.client.render.ItemPocketRenderer; import dan200.computercraft.client.render.ItemPocketRenderer;
import dan200.computercraft.client.render.ItemPrintoutRenderer; import dan200.computercraft.client.render.ItemPrintoutRenderer;
import dan200.computercraft.client.render.MonitorHighlightRenderer; import dan200.computercraft.client.render.MonitorHighlightRenderer;
import dan200.computercraft.client.render.monitor.MonitorRenderState;
import dan200.computercraft.client.sound.SpeakerManager; import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.computer.core.ServerContext;
import dan200.computercraft.shared.media.items.ItemPrintout; import dan200.computercraft.shared.media.items.ItemPrintout;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.TileMonitor; import dan200.computercraft.shared.peripheral.monitor.TileMonitor;
import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.blocks.TileTurtle; import dan200.computercraft.shared.turtle.blocks.TileTurtle;
@ -58,7 +58,7 @@ public final class ClientHooks {
} }
public static void onWorldUnload() { public static void onWorldUnload() {
ClientMonitor.destroyAll(); MonitorRenderState.destroyAll();
SpeakerManager.reset(); SpeakerManager.reset();
ClientPocketComputers.reset(); ClientPocketComputers.reset();
} }

View File

@ -17,6 +17,7 @@ import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.monitor.MonitorRenderState;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer; import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer; import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.client.util.DirectBuffers; import dan200.computercraft.client.util.DirectBuffers;
@ -31,6 +32,7 @@ import net.minecraft.Util;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31; import org.lwjgl.opengl.GL31;
@ -64,18 +66,19 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
if (originTerminal == null) return; if (originTerminal == null) return;
var origin = originTerminal.getOrigin(); var origin = originTerminal.getOrigin();
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
var monitorPos = monitor.getBlockPos(); var monitorPos = monitor.getBlockPos();
// Ensure each monitor terminal is rendered only once. We allow rendering a specific tile // Ensure each monitor terminal is rendered only once. We allow rendering a specific tile
// multiple times in a single frame to ensure compatibility with shaders which may run a // multiple times in a single frame to ensure compatibility with shaders which may run a
// pass multiple times. // pass multiple times.
var renderFrame = FrameInfo.getRenderFrame(); var renderFrame = FrameInfo.getRenderFrame();
if (originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals(originTerminal.lastRenderPos)) { if (renderState.lastRenderFrame == renderFrame && !monitorPos.equals(renderState.lastRenderPos)) {
return; return;
} }
originTerminal.lastRenderFrame = renderFrame; renderState.lastRenderFrame = renderFrame;
originTerminal.lastRenderPos = monitorPos; renderState.lastRenderPos = monitorPos;
var originPos = origin.getBlockPos(); var originPos = origin.getBlockPos();
@ -116,7 +119,7 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
var matrix = transform.last().pose(); var matrix = transform.last().pose();
renderTerminal(matrix, originTerminal, terminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale)); renderTerminal(matrix, originTerminal, renderState, terminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale));
transform.popPose(); transform.popPose();
} else { } else {
@ -130,34 +133,36 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
transform.popPose(); transform.popPose();
} }
private static void renderTerminal(Matrix4f matrix, ClientMonitor monitor, Terminal terminal, float xMargin, float yMargin) { private static void renderTerminal(
Matrix4f matrix, ClientMonitor monitor, MonitorRenderState renderState, Terminal terminal, float xMargin, float yMargin
) {
int width = terminal.getWidth(), height = terminal.getHeight(); int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
var renderType = MonitorRenderer.current(); var renderType = currentRenderer();
var redraw = monitor.pollTerminalChanged(); var redraw = monitor.pollTerminalChanged();
if (monitor.createBuffer(renderType)) redraw = true; if (renderState.createBuffer(renderType)) redraw = true;
switch (renderType) { switch (renderType) {
case TBO -> { case TBO -> {
if (redraw) { if (redraw) {
var terminalBuffer = getBuffer(width * height * 3); var terminalBuffer = getBuffer(width * height * 3);
MonitorTextureBufferShader.setTerminalData(terminalBuffer, terminal); MonitorTextureBufferShader.setTerminalData(terminalBuffer, terminal);
DirectBuffers.setBufferData(GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer, terminalBuffer, GL20.GL_STATIC_DRAW); DirectBuffers.setBufferData(GL31.GL_TEXTURE_BUFFER, renderState.tboBuffer, terminalBuffer, GL20.GL_STATIC_DRAW);
var uniformBuffer = getBuffer(MonitorTextureBufferShader.UNIFORM_SIZE); var uniformBuffer = getBuffer(MonitorTextureBufferShader.UNIFORM_SIZE);
MonitorTextureBufferShader.setUniformData(uniformBuffer, terminal); MonitorTextureBufferShader.setUniformData(uniformBuffer, terminal);
DirectBuffers.setBufferData(GL31.GL_UNIFORM_BUFFER, monitor.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW); DirectBuffers.setBufferData(GL31.GL_UNIFORM_BUFFER, renderState.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW);
} }
// Nobody knows what they're doing! // Nobody knows what they're doing!
var active = GlStateManager._getActiveTexture(); var active = GlStateManager._getActiveTexture();
RenderSystem.activeTexture(MonitorTextureBufferShader.TEXTURE_INDEX); RenderSystem.activeTexture(MonitorTextureBufferShader.TEXTURE_INDEX);
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, monitor.tboTexture); GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, renderState.tboTexture);
RenderSystem.activeTexture(active); RenderSystem.activeTexture(active);
var shader = RenderTypes.getMonitorTextureBufferShader(); var shader = RenderTypes.getMonitorTextureBufferShader();
shader.setupUniform(monitor.tboUniform); shader.setupUniform(renderState.tboUniform);
var buffer = Tesselator.getInstance().getBuilder(); var buffer = Tesselator.getInstance().getBuilder();
buffer.begin(RenderTypes.MONITOR_TBO.mode(), RenderTypes.MONITOR_TBO.format()); buffer.begin(RenderTypes.MONITOR_TBO.mode(), RenderTypes.MONITOR_TBO.format());
@ -168,8 +173,8 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
RenderTypes.MONITOR_TBO.end(buffer, 0, 0, 0); RenderTypes.MONITOR_TBO.end(buffer, 0, 0, 0);
} }
case VBO -> { case VBO -> {
var backgroundBuffer = assertNonNull(monitor.backgroundBuffer); var backgroundBuffer = assertNonNull(renderState.backgroundBuffer);
var foregroundBuffer = assertNonNull(monitor.foregroundBuffer); var foregroundBuffer = assertNonNull(renderState.foregroundBuffer);
if (redraw) { if (redraw) {
var size = DirectFixedWidthFontRenderer.getVertexCount(terminal); var size = DirectFixedWidthFontRenderer.getVertexCount(terminal);
@ -238,7 +243,6 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
} }
private static ByteBuffer getBuffer(int capacity) { private static ByteBuffer getBuffer(int capacity) {
var buffer = backingBuffer; var buffer = backingBuffer;
if (buffer == null || buffer.capacity() < capacity) { if (buffer == null || buffer.capacity() < capacity) {
buffer = backingBuffer = buffer == null ? MemoryTracker.create(capacity) : MemoryTracker.resize(buffer, capacity); buffer = backingBuffer = buffer == null ? MemoryTracker.create(capacity) : MemoryTracker.resize(buffer, capacity);
@ -252,4 +256,30 @@ public class TileEntityMonitorRenderer implements BlockEntityRenderer<TileMonito
public int getViewDistance() { public int getViewDistance() {
return ComputerCraft.monitorDistance; return ComputerCraft.monitorDistance;
} }
/**
* Get the current renderer to use.
*
* @return The current renderer. Will not return {@link MonitorRenderer#BEST}.
*/
public static MonitorRenderer currentRenderer() {
var current = ComputerCraft.monitorRenderer;
if (current == MonitorRenderer.BEST) current = ComputerCraft.monitorRenderer = bestRenderer();
return current;
}
private static MonitorRenderer bestRenderer() {
if (!GL.getCapabilities().OpenGL31) {
ComputerCraft.log.warn("Texture buffers are not supported on your graphics card. Falling back to VBO monitor renderer.");
return MonitorRenderer.VBO;
}
if (ShaderMod.INSTANCE.isShaderMod()) {
ComputerCraft.log.warn("Optifine is loaded, assuming shaders are being used. Falling back to VBO monitor renderer.");
return MonitorRenderer.VBO;
}
return MonitorRenderer.TBO;
}
} }

View File

@ -0,0 +1,132 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.render.monitor;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.client.util.DirectVertexBuffer;
import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import net.minecraft.core.BlockPos;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.Set;
public class MonitorRenderState implements ClientMonitor.RenderState {
@GuardedBy("allMonitors")
private static final Set<MonitorRenderState> allMonitors = new HashSet<>();
public long lastRenderFrame = -1;
public @Nullable BlockPos lastRenderPos = null;
public int tboBuffer;
public int tboTexture;
public int tboUniform;
public @Nullable DirectVertexBuffer backgroundBuffer;
public @Nullable DirectVertexBuffer foregroundBuffer;
/**
* Create the appropriate buffer if needed.
*
* @param renderer The renderer to use.
* @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer,
* or this mode does not require one.
*/
public boolean createBuffer(MonitorRenderer renderer) {
switch (renderer) {
case TBO: {
if (tboBuffer != 0) return false;
deleteBuffers();
tboBuffer = DirectBuffers.createBuffer();
DirectBuffers.setEmptyBufferData(GL31.GL_TEXTURE_BUFFER, tboBuffer, GL15.GL_STATIC_DRAW);
tboTexture = GlStateManager._genTexture();
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, tboTexture);
GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer);
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, 0);
tboUniform = DirectBuffers.createBuffer();
DirectBuffers.setEmptyBufferData(GL31.GL_UNIFORM_BUFFER, tboUniform, GL15.GL_STATIC_DRAW);
addMonitor();
return true;
}
case VBO:
if (backgroundBuffer != null) return false;
deleteBuffers();
backgroundBuffer = new DirectVertexBuffer();
foregroundBuffer = new DirectVertexBuffer();
addMonitor();
return true;
default:
return false;
}
}
private void addMonitor() {
synchronized (allMonitors) {
allMonitors.add(this);
}
}
private void deleteBuffers() {
if (tboBuffer != 0) {
DirectBuffers.deleteBuffer(GL31.GL_TEXTURE_BUFFER, tboBuffer);
tboBuffer = 0;
}
if (tboTexture != 0) {
GlStateManager._deleteTexture(tboTexture);
tboTexture = 0;
}
if (tboUniform != 0) {
DirectBuffers.deleteBuffer(GL31.GL_UNIFORM_BUFFER, tboUniform);
tboUniform = 0;
}
if (backgroundBuffer != null) {
backgroundBuffer.close();
backgroundBuffer = null;
}
if (foregroundBuffer != null) {
foregroundBuffer.close();
foregroundBuffer = null;
}
}
@Override
public void close() {
if (tboBuffer != 0 || backgroundBuffer != null) {
synchronized (allMonitors) {
allMonitors.remove(this);
}
deleteBuffers();
}
}
public static void destroyAll() {
synchronized (allMonitors) {
for (var iterator = allMonitors.iterator(); iterator.hasNext(); ) {
var monitor = iterator.next();
monitor.deleteBuffers();
iterator.remove();
}
}
}
}

View File

@ -5,39 +5,19 @@
*/ */
package dan200.computercraft.shared.peripheral.monitor; package dan200.computercraft.shared.peripheral.monitor;
import com.mojang.blaze3d.platform.GlStateManager;
import dan200.computercraft.client.util.DirectBuffers;
import dan200.computercraft.client.util.DirectVertexBuffer;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.computer.terminal.TerminalState;
import net.minecraft.core.BlockPos;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.HashSet; import java.util.function.Supplier;
import java.util.Set;
public final class ClientMonitor { public final class ClientMonitor {
private static final Set<ClientMonitor> allMonitors = new HashSet<>();
private final TileMonitor origin; private final TileMonitor origin;
public long lastRenderFrame = -1;
public @Nullable BlockPos lastRenderPos = null;
public int tboBuffer;
public int tboTexture;
public int tboUniform;
public @Nullable DirectVertexBuffer backgroundBuffer;
public @Nullable DirectVertexBuffer foregroundBuffer;
private @Nullable NetworkedTerminal terminal; private @Nullable NetworkedTerminal terminal;
private boolean terminalChanged; private boolean terminalChanged;
private @Nullable RenderState state;
public ClientMonitor(TileMonitor origin) { public ClientMonitor(TileMonitor origin) {
this.origin = origin; this.origin = origin;
@ -48,103 +28,21 @@ public final class ClientMonitor {
} }
/** /**
* Create the appropriate buffer if needed. * Get or create the current render state.
* *
* @param renderer The renderer to use. This can be fetched from {@link MonitorRenderer#current()}. * @param create A factory to create the render state.
* @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer, * @param <T> The current render state. This type parameter should only be inhabited by a single class.
* or this mode does not require one. * @return This monitor's render state.
*/ */
@OnlyIn(Dist.CLIENT) @SuppressWarnings("unchecked")
public boolean createBuffer(MonitorRenderer renderer) { public <T extends RenderState> T getRenderState(Supplier<T> create) {
switch (renderer) { var state = this.state;
case TBO: { return (T) (state != null ? state : (this.state = create.get()));
if (tboBuffer != 0) return false;
deleteBuffers();
tboBuffer = DirectBuffers.createBuffer();
DirectBuffers.setEmptyBufferData(GL31.GL_TEXTURE_BUFFER, tboBuffer, GL15.GL_STATIC_DRAW);
tboTexture = GlStateManager._genTexture();
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, tboTexture);
GL31.glTexBuffer(GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer);
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, 0);
tboUniform = DirectBuffers.createBuffer();
DirectBuffers.setEmptyBufferData(GL31.GL_UNIFORM_BUFFER, tboUniform, GL15.GL_STATIC_DRAW);
addMonitor();
return true;
}
case VBO:
if (backgroundBuffer != null) return false;
deleteBuffers();
backgroundBuffer = new DirectVertexBuffer();
foregroundBuffer = new DirectVertexBuffer();
addMonitor();
return true;
default:
return false;
}
} }
private void addMonitor() { void destroy() {
synchronized (allMonitors) { if (state != null) state.close();
allMonitors.add(this); state = null;
}
}
private void deleteBuffers() {
if (tboBuffer != 0) {
DirectBuffers.deleteBuffer(GL31.GL_TEXTURE_BUFFER, tboBuffer);
tboBuffer = 0;
}
if (tboTexture != 0) {
GlStateManager._deleteTexture(tboTexture);
tboTexture = 0;
}
if (tboUniform != 0) {
DirectBuffers.deleteBuffer(GL31.GL_UNIFORM_BUFFER, tboUniform);
tboUniform = 0;
}
if (backgroundBuffer != null) {
backgroundBuffer.close();
backgroundBuffer = null;
}
if (foregroundBuffer != null) {
foregroundBuffer.close();
foregroundBuffer = null;
}
}
@OnlyIn(Dist.CLIENT)
public void destroy() {
if (tboBuffer != 0 || backgroundBuffer != null) {
synchronized (allMonitors) {
allMonitors.remove(this);
}
deleteBuffers();
}
}
@OnlyIn(Dist.CLIENT)
public static void destroyAll() {
synchronized (allMonitors) {
for (var iterator = allMonitors.iterator(); iterator.hasNext(); ) {
var monitor = iterator.next();
monitor.deleteBuffers();
iterator.remove();
}
}
} }
public boolean pollTerminalChanged() { public boolean pollTerminalChanged() {
@ -169,4 +67,15 @@ public final class ClientMonitor {
} }
} }
} }
/**
* An interface representing the current state of the monitor renderer.
* <p>
* This interface should only be inhabited by {@link dan200.computercraft.client.render.monitor.MonitorRenderState}:
* it exists solely to avoid referencing client-side classes in common code.
*/
public interface RenderState extends AutoCloseable {
@Override
void close();
}
} }

View File

@ -5,15 +5,10 @@
*/ */
package dan200.computercraft.shared.peripheral.monitor; package dan200.computercraft.shared.peripheral.monitor;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.shared.integration.ShaderMod;
import org.lwjgl.opengl.GL;
/** /**
* The render type to use for monitors. * The render type to use for monitors.
* *
* @see TileEntityMonitorRenderer * @see dan200.computercraft.client.render.TileEntityMonitorRenderer
* @see ClientMonitor * @see ClientMonitor
*/ */
public enum MonitorRenderer { public enum MonitorRenderer {
@ -34,30 +29,5 @@ public enum MonitorRenderer {
* *
* @see com.mojang.blaze3d.vertex.VertexBuffer * @see com.mojang.blaze3d.vertex.VertexBuffer
*/ */
VBO; VBO,
/**
* Get the current renderer to use.
*
* @return The current renderer. Will not return {@link MonitorRenderer#BEST}.
*/
public static MonitorRenderer current() {
var current = ComputerCraft.monitorRenderer;
if (current == BEST) current = ComputerCraft.monitorRenderer = best();
return current;
}
private static MonitorRenderer best() {
if (!GL.getCapabilities().OpenGL31) {
ComputerCraft.log.warn("Texture buffers are not supported on your graphics card. Falling back to VBO monitor renderer.");
return VBO;
}
if (ShaderMod.INSTANCE.isShaderMod()) {
ComputerCraft.log.warn("Optifine is loaded, assuming shaders are being used. Falling back to VBO monitor renderer.");
return VBO;
}
return TBO;
}
} }