1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-26 07:03:22 +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.ItemPocketRenderer;
import dan200.computercraft.client.render.ItemPrintoutRenderer;
import dan200.computercraft.client.render.MonitorHighlightRenderer;
import dan200.computercraft.client.render.monitor.MonitorRenderState;
import dan200.computercraft.client.sound.SpeakerManager;
import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.ServerContext;
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.pocket.items.ItemPocketComputer;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
@ -58,7 +58,7 @@ public static void onRenderTick() {
}
public static void onWorldUnload() {
ClientMonitor.destroyAll();
MonitorRenderState.destroyAll();
SpeakerManager.reset();
ClientPocketComputers.reset();
}

View File

@ -17,6 +17,7 @@
import com.mojang.math.Vector3f;
import dan200.computercraft.ComputerCraft;
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.FixedWidthFontRenderer;
import dan200.computercraft.client.util.DirectBuffers;
@ -31,6 +32,7 @@
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
@ -64,18 +66,19 @@ public void render(TileMonitor monitor, float partialTicks, PoseStack transform,
if (originTerminal == null) return;
var origin = originTerminal.getOrigin();
var renderState = originTerminal.getRenderState(MonitorRenderState::new);
var monitorPos = monitor.getBlockPos();
// 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
// pass multiple times.
var renderFrame = FrameInfo.getRenderFrame();
if (originTerminal.lastRenderFrame == renderFrame && !monitorPos.equals(originTerminal.lastRenderPos)) {
if (renderState.lastRenderFrame == renderFrame && !monitorPos.equals(renderState.lastRenderPos)) {
return;
}
originTerminal.lastRenderFrame = renderFrame;
originTerminal.lastRenderPos = monitorPos;
renderState.lastRenderFrame = renderFrame;
renderState.lastRenderPos = monitorPos;
var originPos = origin.getBlockPos();
@ -116,7 +119,7 @@ public void render(TileMonitor monitor, float partialTicks, PoseStack transform,
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();
} else {
@ -130,34 +133,36 @@ public void render(TileMonitor monitor, float partialTicks, PoseStack transform,
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 pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
var renderType = MonitorRenderer.current();
var renderType = currentRenderer();
var redraw = monitor.pollTerminalChanged();
if (monitor.createBuffer(renderType)) redraw = true;
if (renderState.createBuffer(renderType)) redraw = true;
switch (renderType) {
case TBO -> {
if (redraw) {
var terminalBuffer = getBuffer(width * height * 3);
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);
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!
var active = GlStateManager._getActiveTexture();
RenderSystem.activeTexture(MonitorTextureBufferShader.TEXTURE_INDEX);
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, monitor.tboTexture);
GL11.glBindTexture(GL31.GL_TEXTURE_BUFFER, renderState.tboTexture);
RenderSystem.activeTexture(active);
var shader = RenderTypes.getMonitorTextureBufferShader();
shader.setupUniform(monitor.tboUniform);
shader.setupUniform(renderState.tboUniform);
var buffer = Tesselator.getInstance().getBuilder();
buffer.begin(RenderTypes.MONITOR_TBO.mode(), RenderTypes.MONITOR_TBO.format());
@ -168,8 +173,8 @@ private static void renderTerminal(Matrix4f matrix, ClientMonitor monitor, Termi
RenderTypes.MONITOR_TBO.end(buffer, 0, 0, 0);
}
case VBO -> {
var backgroundBuffer = assertNonNull(monitor.backgroundBuffer);
var foregroundBuffer = assertNonNull(monitor.foregroundBuffer);
var backgroundBuffer = assertNonNull(renderState.backgroundBuffer);
var foregroundBuffer = assertNonNull(renderState.foregroundBuffer);
if (redraw) {
var size = DirectFixedWidthFontRenderer.getVertexCount(terminal);
@ -238,7 +243,6 @@ private static void tboVertex(VertexConsumer builder, Matrix4f matrix, float x,
}
private static ByteBuffer getBuffer(int capacity) {
var buffer = backingBuffer;
if (buffer == null || buffer.capacity() < capacity) {
buffer = backingBuffer = buffer == null ? MemoryTracker.create(capacity) : MemoryTracker.resize(buffer, capacity);
@ -252,4 +256,30 @@ private static ByteBuffer getBuffer(int capacity) {
public int getViewDistance() {
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;
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.shared.computer.terminal.NetworkedTerminal;
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 java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
public final class ClientMonitor {
private static final Set<ClientMonitor> allMonitors = new HashSet<>();
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 boolean terminalChanged;
private @Nullable RenderState state;
public ClientMonitor(TileMonitor origin) {
this.origin = origin;
@ -48,103 +28,21 @@ public TileMonitor getOrigin() {
}
/**
* 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()}.
* @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.
* @param create A factory to create the render state.
* @param <T> The current render state. This type parameter should only be inhabited by a single class.
* @return This monitor's render state.
*/
@OnlyIn(Dist.CLIENT)
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;
}
@SuppressWarnings("unchecked")
public <T extends RenderState> T getRenderState(Supplier<T> create) {
var state = this.state;
return (T) (state != null ? state : (this.state = create.get()));
}
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;
}
}
@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();
}
}
void destroy() {
if (state != null) state.close();
state = null;
}
public boolean pollTerminalChanged() {
@ -169,4 +67,15 @@ void read(TerminalState state) {
}
}
}
/**
* 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;
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.
*
* @see TileEntityMonitorRenderer
* @see dan200.computercraft.client.render.TileEntityMonitorRenderer
* @see ClientMonitor
*/
public enum MonitorRenderer {
@ -34,30 +29,5 @@ public enum MonitorRenderer {
*
* @see com.mojang.blaze3d.vertex.VertexBuffer
*/
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;
}
VBO,
}