1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-13 19:50:31 +00:00

Don't render cursors separately

- For TBOs, we now pass cursor position, colour and blink state as
   variables to the shader, and use them to overlay the cursor texture
   in the right place.

   As we no longer need to render the cursor, we can skip the depth
   buffer, meaning we have to do one fewer upload+draw cycle.

 - For VBOs, we bake the cursor into the main VBO, and switch between
   rendering n and n+1 quads. We still need the depth blocker, but can
   save one upload+draw cycle when the cursor is visible.

This saves significant time on the TBO renderer - somewhere between 4
and 7ms/frame, which bumps us up from 35 to 47fps on my test world (480
full-sized monitors, changing every tick). [Taken on 1.18, but should be
similar on 1.16]
This commit is contained in:
Jonathan Coates 2022-04-26 20:01:45 +01:00
parent 77a00b14ae
commit 22e8b9b587
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
3 changed files with 60 additions and 33 deletions

View File

@ -9,6 +9,7 @@ import com.google.common.base.Strings;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
@ -29,7 +30,7 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.get
class MonitorTextureBufferShader
{
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4;
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
static final int TEXTURE_INDEX = GL13.GL_TEXTURE3;
@ -40,6 +41,7 @@ class MonitorTextureBufferShader
private static int uniformFont;
private static int uniformTbo;
private static int uniformMonitor;
private static int uniformCursorBlink;
private static boolean initialised;
private static boolean ok;
@ -53,6 +55,9 @@ class MonitorTextureBufferShader
RenderSystem.glUniformMatrix4( uniformMv, false, MATRIX_BUFFER );
GL31.glBindBufferBase( GL31.GL_UNIFORM_BUFFER, uniformMonitor, tboUniform );
int cursorAlpha = FrameInfo.getGlobalCursorBlink() ? 1 : 0;
RenderSystem.glUniform1i( uniformCursorBlink, cursorAlpha );
}
static boolean use()
@ -107,6 +112,7 @@ class MonitorTextureBufferShader
uniformTbo = getUniformLocation( program, "u_tbo" );
uniformMonitor = GL31.glGetUniformBlockIndex( program, "u_monitor" );
if( uniformMonitor == -1 ) throw new IllegalStateException( "Could not find uniformMonitor uniform." );
uniformCursorBlink = getUniformLocation( program, "u_cursorBlink" );
ComputerCraft.log.info( "Loaded monitor shader." );
return true;
@ -187,8 +193,12 @@ class MonitorTextureBufferShader
pos += 4 * 4; // std140 requires these are 4-wide
}
int width = terminal.getWidth(), height = terminal.getHeight();
buffer.putInt( pos, width ).putInt( pos + 4, height );
boolean showCursor = FixedWidthFontRenderer.isCursorVisible( terminal );
buffer
.putInt( pos, terminal.getWidth() ).putInt( pos + 4, terminal.getHeight() )
.putInt( pos + 8, showCursor ? terminal.getCursorX() : -2 )
.putInt( pos + 12, showCursor ? terminal.getCursorY() : -2 )
.putInt( pos + 16, 15 - terminal.getTextColour() );
buffer.limit( UNIFORM_SIZE );
}

View File

@ -7,7 +7,6 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
@ -115,18 +114,11 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
transform.scale( (float) xScale, (float) -yScale, 1.0f );
Matrix4f matrix = transform.last().pose();
renderTerminal( renderer, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
// for now.
IVertexBuilder buffer = renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState();
renderTerminal( matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) );
// We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is
// reasonable.
FixedWidthFontRenderer.drawCursor( matrix, buffer, 0, 0, terminal, !originTerminal.isColour() );
// Force a flush of the buffer. WorldRenderer.updateCameraAndRender will "finish" all the built-in buffers
// before calling renderer.finish, which means the blocker isn't actually rendered at that point!
renderer.getBuffer( RenderType.solid() );
transform.popPose();
}
@ -139,22 +131,14 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
);
}
FixedWidthFontRenderer.drawBlocker(
transform.last().pose(), renderer.getBuffer( RenderTypes.TERMINAL_BLOCKER ),
-MARGIN, MARGIN,
(float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2)
);
// Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in
// buffers before calling renderer.finish, which means the blocker isn't actually rendered at that point!
renderer.getBuffer( RenderType.solid() );
transform.popPose();
}
private static void renderTerminal( Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
private static void renderTerminal( IRenderTypeBuffer bufferSource, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin )
{
Terminal terminal = monitor.getTerminal();
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
MonitorRenderer renderType = MonitorRenderer.current();
boolean redraw = monitor.pollTerminalChanged();
@ -166,9 +150,6 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
{
if( !MonitorTextureBufferShader.use() ) return;
int width = terminal.getWidth(), height = terminal.getHeight();
int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT;
if( redraw )
{
ByteBuffer terminalBuffer = getBuffer( width * height * 3 );
@ -180,6 +161,12 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
DirectBuffers.setBufferData( GL31.GL_UNIFORM_BUFFER, monitor.tboUniform, uniformBuffer, GL20.GL_STATIC_DRAW );
}
// Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate
// render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick
// for now.
bufferSource.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH );
RenderTypes.TERMINAL_WITH_DEPTH.setupRenderState();
// Nobody knows what they're doing!
GlStateManager._activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX );
GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture );
@ -214,14 +201,31 @@ public class TileEntityMonitorRenderer extends TileEntityRenderer<TileMonitor>
);
int termIndexes = buffer.position() / vertexSize;
// If the cursor is visible, we append it to the end of our buffer. When rendering, we can either
// render n or n+1 quads and so toggle the cursor on and off.
DirectFixedWidthFontRenderer.drawCursor( buffer, 0, 0, terminal, !monitor.isColour() );
buffer.flip();
vbo.upload( termIndexes, RenderTypes.TERMINAL_WITHOUT_DEPTH.format(), buffer );
}
vbo.draw( matrix, vbo.getIndexCount() );
// As with the TBO backend we use getBuffer to flush existing buffers. This time we use TERMINAL_WITHOUT_DEPTH
// instead and render a separate depth blocker.
bufferSource.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH );
RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState();
RenderTypes.TERMINAL_WITHOUT_DEPTH.format().clearBufferState();
vbo.draw(
matrix,
// As mentioned in the uploading block, render the extra cursor quad if it is visible this frame.
// Each quad has an index count of 6.
FixedWidthFontRenderer.isCursorVisible( terminal ) && FrameInfo.getGlobalCursorBlink() ? vbo.getIndexCount() + 6 : vbo.getIndexCount()
);
FixedWidthFontRenderer.drawBlocker(
matrix, bufferSource.getBuffer( RenderTypes.TERMINAL_BLOCKER ),
-xMargin, -yMargin, pixelWidth + xMargin, pixelHeight + yMargin
);
break;
}
}

View File

@ -10,7 +10,10 @@ layout(std140) uniform u_monitor {
vec3 u_palette[16];
int u_width;
int u_height;
ivec2 u_cursorPos;
int u_cursorColour;
};
uniform int u_cursorBlink;
in vec2 f_pos;
@ -22,6 +25,10 @@ vec2 texture_corner(int index) {
return vec2(x, y);
}
vec4 recolour(vec4 texture, int colour) {
return vec4(texture.rgb * u_palette[colour], texture.rgba);
}
void main() {
vec2 term_pos = vec2(f_pos.x / FONT_WIDTH, f_pos.y / FONT_HEIGHT);
vec2 corner = floor(term_pos);
@ -38,6 +45,12 @@ void main() {
int bg = int(texelFetch(u_tbo, index + 2).r);
vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT);
vec4 img = texture(u_font, (texture_corner(character) + pos) / 256.0);
colour = vec4(mix(u_palette[bg], img.rgb * u_palette[fg], img.a * mult), 1.0);
vec4 charTex = recolour(texture(u_font, (texture_corner(character) + pos) / 256.0), fg);
// Applies the cursor on top of the current character if we're blinking and in the current cursor's cell. We do it
// this funky way to avoid branches.
vec4 cursorTex = recolour(texture(u_font, (texture_corner(95) + pos) / 256.0), u_cursorColour); // 95 = '_'
vec4 img = mix(charTex, cursorTex, cursorTex.a * float(u_cursorBlink) * (u_cursorPos == cell ? 1.0 : 0.0));
colour = vec4(mix(u_palette[bg], img.rgb, img.a * mult), 1.0);
}