1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-09 08:50:29 +00:00

Use ARGB32 to store palette colours

Previously we used an RGBA byte array. However, this comes with some
overhead (extra memory reads, bounds checks).

Minecraft 1.21+ uses ARGB32 colours for rendering (well, in the public
code — internaly it converts to ABGR), so it makes sense to match that
here.

We also add some helper functions for dealing with ARGB32 colours. These
can be removed in 1.21, as Minecraft will have these builtin.
This commit is contained in:
Jonathan Coates 2024-09-11 09:00:06 +01:00
parent 370e5f92a0
commit ba36c69583
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
8 changed files with 94 additions and 78 deletions

View File

@ -13,6 +13,7 @@ import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.util.ARGB32;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f;
@ -92,16 +93,11 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
}
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
var r = (byte) ((colour >>> 16) & 0xFF);
var g = (byte) ((colour >>> 8) & 0xFF);
var b = (byte) (colour & 0xFF);
var c = new byte[]{ r, g, b, (byte) 255 };
var buffer = render.getBuffer(RenderTypes.TERMINAL);
FixedWidthFontRenderer.drawQuad(
FixedWidthFontRenderer.toVertexConsumer(transform, buffer),
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
c, RenderTypes.FULL_BRIGHT_LIGHTMAP
ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
);
}
}

View File

@ -13,6 +13,7 @@ import dan200.computercraft.core.terminal.Palette;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.util.FastColor;
import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer;
@ -41,7 +42,7 @@ public final class DirectFixedWidthFontRenderer {
private DirectFixedWidthFontRenderer() {
}
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour) {
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour) {
// Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return;
@ -158,8 +159,8 @@ public final class DirectFixedWidthFontRenderer {
return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2;
}
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
}
public interface QuadEmitter {
@ -167,7 +168,7 @@ public final class DirectFixedWidthFontRenderer {
ByteBuffer buffer();
void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2);
void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2);
}
public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter {
@ -177,12 +178,12 @@ public final class DirectFixedWidthFontRenderer {
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, colour, u1, v1, u2, v2);
}
}
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
// 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.
@ -196,16 +197,19 @@ public final class DirectFixedWidthFontRenderer {
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");
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
if (rgba.length != 4) throw new IllegalStateException();
var red = (byte) FastColor.ARGB32.red(colour);
var green = (byte) FastColor.ARGB32.green(colour);
var blue = (byte) FastColor.ARGB32.blue(colour);
var alpha = (byte) FastColor.ARGB32.alpha(colour);
memPutFloat(addr + 0, x1);
memPutFloat(addr + 4, y1);
memPutFloat(addr + 8, z);
memPutByte(addr + 12, rgba[0]);
memPutByte(addr + 13, rgba[1]);
memPutByte(addr + 14, rgba[2]);
memPutByte(addr + 15, (byte) 255);
memPutByte(addr + 12, red);
memPutByte(addr + 13, green);
memPutByte(addr + 14, blue);
memPutByte(addr + 15, alpha);
memPutFloat(addr + 16, u1);
memPutFloat(addr + 20, v1);
memPutShort(addr + 24, (short) 0xF0);
@ -214,10 +218,10 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 28, x1);
memPutFloat(addr + 32, y2);
memPutFloat(addr + 36, z);
memPutByte(addr + 40, rgba[0]);
memPutByte(addr + 41, rgba[1]);
memPutByte(addr + 42, rgba[2]);
memPutByte(addr + 43, (byte) 255);
memPutByte(addr + 40, red);
memPutByte(addr + 41, green);
memPutByte(addr + 42, blue);
memPutByte(addr + 43, alpha);
memPutFloat(addr + 44, u1);
memPutFloat(addr + 48, v2);
memPutShort(addr + 52, (short) 0xF0);
@ -226,10 +230,10 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 56, x2);
memPutFloat(addr + 60, y2);
memPutFloat(addr + 64, z);
memPutByte(addr + 68, rgba[0]);
memPutByte(addr + 69, rgba[1]);
memPutByte(addr + 70, rgba[2]);
memPutByte(addr + 71, (byte) 255);
memPutByte(addr + 68, red);
memPutByte(addr + 69, green);
memPutByte(addr + 70, blue);
memPutByte(addr + 71, alpha);
memPutFloat(addr + 72, u2);
memPutFloat(addr + 76, v2);
memPutShort(addr + 80, (short) 0xF0);
@ -238,10 +242,10 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 84, x2);
memPutFloat(addr + 88, y1);
memPutFloat(addr + 92, z);
memPutByte(addr + 96, rgba[0]);
memPutByte(addr + 97, rgba[1]);
memPutByte(addr + 98, rgba[2]);
memPutByte(addr + 99, (byte) 255);
memPutByte(addr + 96, red);
memPutByte(addr + 97, green);
memPutByte(addr + 98, blue);
memPutByte(addr + 99, alpha);
memPutFloat(addr + 100, u2);
memPutFloat(addr + 104, v1);
memPutShort(addr + 108, (short) 0xF0);

View File

@ -12,6 +12,7 @@ import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FastColor;
import org.joml.Matrix4f;
import org.joml.Vector3f;
@ -41,7 +42,7 @@ public final class FixedWidthFontRenderer {
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final byte[] BLACK = new byte[]{ byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), (byte) 255 };
private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()));
private static final float Z_OFFSET = 1e-3f;
private FixedWidthFontRenderer() {
@ -59,7 +60,7 @@ public final class FixedWidthFontRenderer {
return 15 - Terminal.getColour(c, def);
}
private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour, int light) {
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) {
// Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return;
@ -75,7 +76,7 @@ public final class FixedWidthFontRenderer {
);
}
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light) {
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) {
quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
}
@ -216,10 +217,10 @@ public final class FixedWidthFontRenderer {
return new QuadEmitter(transform.last().pose(), consumer);
}
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light) {
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) {
var poseMatrix = c.poseMatrix();
var consumer = c.consumer();
byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
int r = FastColor.ARGB32.red(colour), g = FastColor.ARGB32.green(colour), b = FastColor.ARGB32.blue(colour), a = FastColor.ARGB32.alpha(colour);
consumer.vertex(poseMatrix, x1, y1, z).color(r, g, b, a).uv(u1, v1).uv2(light).endVertex();
consumer.vertex(poseMatrix, x1, y2, z).color(r, g, b, a).uv(u1, v2).uv2(light).endVertex();

View File

@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.shared.util;
import net.minecraft.util.FastColor;
/**
* Utilities for working with 32-bit ARGB colours.
*
* @see FastColor.ARGB32
*/
public final class ARGB32 {
private ARGB32() {
}
/**
* Set the alpha channel to be fully opaque.
*
* @param colour The colour to make opaque.
* @return The fully-opaque colour
*/
public static int opaque(int colour) {
return 0xFF000000 | colour;
}
/**
* Convert an ARGB32 colour to a {@linkplain FastColor.ABGR32 ABGR32} one.
*
* @param colour The colour to convert.
* @return The converted colour.
*/
public static int toABGR32(int colour) {
// Swap B and R components, converting ARGB32 to ABGR32.
return colour & 0xFF00FF00 | (colour & 0xFF0000) >> 16 | (colour & 0xFF) << 16;
}
}

View File

@ -12,15 +12,13 @@ public class Palette {
private final boolean colour;
private final double[][] colours = new double[PALETTE_SIZE][3];
private final byte[][] byteColours = new byte[PALETTE_SIZE][4];
private final int[] byteColours = new int[PALETTE_SIZE];
public static final Palette DEFAULT = new Palette(true);
public Palette(boolean colour) {
this.colour = colour;
resetColours();
for (var i = 0; i < PALETTE_SIZE; i++) byteColours[i][3] = (byte) 255;
}
public void setColour(int i, double r, double g, double b) {
@ -30,15 +28,17 @@ public class Palette {
colours[i][2] = b;
if (colour) {
byteColours[i][0] = (byte) (int) (r * 255);
byteColours[i][1] = (byte) (int) (g * 255);
byteColours[i][2] = (byte) (int) (b * 255);
byteColours[i] = packColour((int) (r * 255), (int) (g * 255), (int) (b * 255));
} else {
var grey = (byte) (int) ((r + g + b) / 3 * 255);
byteColours[i][0] = byteColours[i][1] = byteColours[i][2] = grey;
var grey = (int) ((r + g + b) / 3 * 255);
byteColours[i] = packColour(grey, grey, grey);
}
}
private static int packColour(int r, int g, int b) {
return 255 << 24 | (r & 255) << 16 | (g & 255) << 8 | b & 255;
}
public void setColour(int i, Colour colour) {
setColour(i, colour.getR(), colour.getG(), colour.getB());
}
@ -48,26 +48,20 @@ public class Palette {
}
/**
* Get the colour as a set of RGB values suitable for rendering. Colours are automatically converted to greyscale
* Get the colour as a set of ARGB values suitable for rendering. Colours are automatically converted to greyscale
* when using a black and white palette.
* <p>
* This returns a byte array, suitable for being used directly by our terminal vertex format.
* This returns a packed 32-bit ARGB colour.
*
* @param i The colour index.
* @return The number as a tuple of bytes.
* @return The actual RGB colour.
*/
public byte[] getRenderColours(int i) {
public int getRenderColours(int i) {
return byteColours[i];
}
public void resetColour(int i) {
if (i >= 0 && i < PALETTE_SIZE) setColour(i, Colour.VALUES[i]);
}
public void resetColours() {
for (var i = 0; i < Colour.VALUES.length; i++) {
resetColour(i);
}
for (var i = 0; i < Colour.VALUES.length; i++) setColour(i, Colour.VALUES[i]);
}
public static int encodeRGB8(double[] rgb) {

View File

@ -4,8 +4,6 @@
package dan200.computercraft.core.util;
import javax.annotation.Nullable;
public enum Colour {
BLACK(0x111111),
RED(0xcc4c4c),
@ -30,15 +28,6 @@ public enum Colour {
return Colour.VALUES[colour];
}
@Nullable
public static Colour fromHex(int colour) {
for (var entry : VALUES) {
if (entry.getHex() == colour) return entry;
}
return null;
}
private final int hex;
private final float red, green, blue;

View File

@ -8,6 +8,7 @@ import com.google.auto.service.AutoService;
import com.mojang.blaze3d.vertex.VertexFormat;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import dan200.computercraft.shared.util.ARGB32;
import net.fabricmc.loader.api.FabricLoader;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.api.v0.IrisTextVertexSink;
@ -54,12 +55,8 @@ public class IrisShaderMod implements ShaderMod.Provider {
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, pack(rgba[0], rgba[1], rgba[2], rgba[3]), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
private static int pack(int r, int g, int b, int a) {
return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255;
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, ARGB32.toABGR32(colour), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
}
}

View File

@ -8,6 +8,7 @@ import com.google.auto.service.AutoService;
import com.mojang.blaze3d.vertex.VertexFormat;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.text.DirectFixedWidthFontRenderer;
import dan200.computercraft.shared.util.ARGB32;
import net.irisshaders.iris.api.v0.IrisApi;
import net.irisshaders.iris.api.v0.IrisTextVertexSink;
import net.minecraftforge.fml.ModList;
@ -57,12 +58,8 @@ public class IrisShaderMod implements ShaderMod.Provider {
}
@Override
public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, pack(rgba[0], rgba[1], rgba[2], rgba[3]), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
private static int pack(int r, int g, int b, int a) {
return (a & 255) << 24 | (b & 255) << 16 | (g & 255) << 8 | r & 255;
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) {
sink.quad(x1, y1, x2, y2, z, ARGB32.toABGR32(colour), u1, v1, u2, v2, RenderTypes.FULL_BRIGHT_LIGHTMAP);
}
}
}