From fe55feb385d0edf568a829135a161bf7cdf11835 Mon Sep 17 00:00:00 2001 From: Chen Meng Date: Sun, 22 Feb 2026 21:20:13 +0800 Subject: [PATCH 1/2] round rectangle renderer --- .../lumin/graphics/LuminRenderPipelines.java | 9 ++ .../lumin/graphics/LuminVertexFormats.java | 24 ++++ .../lumin/graphics/buffer/LuminBuffer.java | 44 +++++++ .../graphics/renderers/RoundRectRenderer.java | 111 ++++++++++++++++++ .../lumin/modules/impl/visual/RenderTest.java | 20 +++- .../assets/lumin/shaders/round_rectangle.fsh | 28 +++++ .../assets/lumin/shaders/round_rectangle.vsh | 24 ++++ 7 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/github/lumin/graphics/LuminVertexFormats.java create mode 100644 src/main/java/com/github/lumin/graphics/buffer/LuminBuffer.java create mode 100644 src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java create mode 100644 src/main/resources/assets/lumin/shaders/round_rectangle.fsh create mode 100644 src/main/resources/assets/lumin/shaders/round_rectangle.vsh diff --git a/src/main/java/com/github/lumin/graphics/LuminRenderPipelines.java b/src/main/java/com/github/lumin/graphics/LuminRenderPipelines.java index 0e11cc4..f6238ff 100644 --- a/src/main/java/com/github/lumin/graphics/LuminRenderPipelines.java +++ b/src/main/java/com/github/lumin/graphics/LuminRenderPipelines.java @@ -35,6 +35,15 @@ public class LuminRenderPipelines { .withCull(false) .build(); + public final static RenderPipeline ROUND_RECT = RenderPipeline.builder(RenderPipelines.MATRICES_PROJECTION_SNIPPET) + .withLocation(ResourceLocationUtils.getIdentifier("pipelines/round_rectangle")) + .withVertexFormat(LuminVertexFormats.ROUND_RECT, VertexFormat.Mode.QUADS) + .withVertexShader(ResourceLocationUtils.getIdentifier("round_rectangle")) + .withFragmentShader(ResourceLocationUtils.getIdentifier("round_rectangle")) + .withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST) + .withCull(false) + .build(); + public static void onRegisterRenderPipelines(RegisterRenderPipelinesEvent event) { event.registerPipeline(RECTANGLE); } diff --git a/src/main/java/com/github/lumin/graphics/LuminVertexFormats.java b/src/main/java/com/github/lumin/graphics/LuminVertexFormats.java new file mode 100644 index 0000000..60f1ac7 --- /dev/null +++ b/src/main/java/com/github/lumin/graphics/LuminVertexFormats.java @@ -0,0 +1,24 @@ +package com.github.lumin.graphics; + +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; + +import static com.mojang.blaze3d.vertex.VertexFormatElement.register; + +public class LuminVertexFormats { + + public static final VertexFormatElement ROUND_INNER_RECT = + register(7, 2, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.UV, 4); + + public static final VertexFormatElement ROUND_RADIUS = + register(8, 3, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.UV, 1); + + + public static final VertexFormat ROUND_RECT = VertexFormat.builder() + .add("Position", VertexFormatElement.POSITION) + .add("Color", VertexFormatElement.COLOR) + .add("InnerRect", ROUND_INNER_RECT) + .add("Radius", ROUND_RADIUS) + .build(); + +} diff --git a/src/main/java/com/github/lumin/graphics/buffer/LuminBuffer.java b/src/main/java/com/github/lumin/graphics/buffer/LuminBuffer.java new file mode 100644 index 0000000..dd2f899 --- /dev/null +++ b/src/main/java/com/github/lumin/graphics/buffer/LuminBuffer.java @@ -0,0 +1,44 @@ +package com.github.lumin.graphics.buffer; + +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.systems.RenderSystem; + +import java.nio.ByteBuffer; + +public class LuminBuffer { + + private final GpuBuffer gpuBuffer; + + private GpuBuffer.MappedView mappedBuffer; + + public LuminBuffer(long size, @GpuBuffer.Usage int usage) { + + gpuBuffer = RenderSystem.getDevice().createBuffer( + () -> "lumin-buffer", + GpuBuffer.USAGE_MAP_WRITE | GpuBuffer.USAGE_COPY_DST | usage, + size + ); + + mappedBuffer = RenderSystem.getDevice().createCommandEncoder().mapBuffer( + gpuBuffer, false, true + ); + + } + + public ByteBuffer getMappedBuffer() { + return mappedBuffer.data(); + } + + public void unmap() { + mappedBuffer.close(); + } + + public GpuBuffer getGpuBuffer() { + return gpuBuffer; + } + + public void close() { + gpuBuffer.close(); + } + +} diff --git a/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java b/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java new file mode 100644 index 0000000..060c100 --- /dev/null +++ b/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java @@ -0,0 +1,111 @@ +package com.github.lumin.graphics.renderers; + +import com.github.lumin.graphics.LuminRenderPipelines; +import com.github.lumin.graphics.LuminRenderSystem; +import com.github.lumin.graphics.buffer.LuminBuffer; +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.rendertype.TextureTransform; +import net.minecraft.util.ARGB; +import org.joml.Vector3f; +import org.joml.Vector4f; +import org.lwjgl.system.MemoryUtil; + +import java.awt.*; +import java.util.OptionalDouble; +import java.util.OptionalInt; + +public class RoundRectRenderer implements IRenderer { + + private static final long BUFFER_SIZE = 8 * 1024 * 1024; + private final LuminBuffer buffer = new LuminBuffer(BUFFER_SIZE, GpuBuffer.USAGE_VERTEX); + + public void addRoundRect(float x, float y, float width, float height, float radius, Color color) { + float x2 = x + width; + float y2 = y + height; + + float expand = radius + 1.0f; + float vx1 = x - expand; + float vy1 = y - expand; + float vx2 = x2 + expand; + float vy2 = y2 + expand; + + int argb = color.getRGB(); + + addVertex(vx1, vy1, x, y, x2, y2, radius, argb); + addVertex(vx1, vy2, x, y, x2, y2, radius, argb); + addVertex(vx2, vy2, x, y, x2, y2, radius, argb); + addVertex(vx2, vy1, x, y, x2, y2, radius, argb); + } + + private long currentOffset = 0; + private int vertexCount = 0; + + private void addVertex(float vx, float vy, float x1, float y1, float x2, float y2, float radius, int color) { + long baseAddr = MemoryUtil.memAddress(buffer.getMappedBuffer()); + long p = baseAddr + currentOffset; + + MemoryUtil.memPutFloat(p, vx); + MemoryUtil.memPutFloat(p + 4, vy); + MemoryUtil.memPutFloat(p + 8, 0.0f); + + MemoryUtil.memPutInt(p + 12, ARGB.toABGR(color)); + + MemoryUtil.memPutFloat(p + 16, x1); + MemoryUtil.memPutFloat(p + 20, y1); + MemoryUtil.memPutFloat(p + 24, x2); + MemoryUtil.memPutFloat(p + 28, y2); + + MemoryUtil.memPutFloat(p + 32, radius); + + currentOffset += 36; + vertexCount++; + } + + @Override + public void draw() { + LuminRenderSystem.applyOrthoProjection(); + + RenderTarget target = Minecraft.getInstance().getMainRenderTarget(); + if (target.getColorTextureView() == null) return; + + final var indexCount = vertexCount / 4 * 6; + + RenderSystem.AutoStorageIndexBuffer autoIndices = + RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); + GpuBuffer ibo = autoIndices.getBuffer(indexCount); + + try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( + () -> "Round Rect Draw", + target.getColorTextureView(), OptionalInt.empty(), + target.getDepthTextureView(), OptionalDouble.empty()) + ) { + GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( + RenderSystem.getModelViewMatrix(), + new Vector4f(1, 1, 1, 1), + new Vector3f(0, 0, 0), + TextureTransform.DEFAULT_TEXTURING.getMatrix() + ); + + pass.setPipeline(LuminRenderPipelines.ROUND_RECT); + + RenderSystem.bindDefaultUniforms(pass); + pass.setUniform("DynamicTransforms", dynamicUniforms); + pass.setVertexBuffer(0, buffer.getGpuBuffer()); + pass.setIndexBuffer(ibo, autoIndices.type()); + + pass.drawIndexed(0, 0, indexCount, 1); + } + } + + @Override + public void clear() { + vertexCount = 0; + currentOffset = 0; + } +} diff --git a/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java b/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java index 4f5ec01..9e4b8bb 100644 --- a/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java +++ b/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java @@ -1,10 +1,13 @@ package com.github.lumin.modules.impl.visual; import com.github.lumin.graphics.renderers.RectRenderer; +import com.github.lumin.graphics.renderers.RoundRectRenderer; import com.github.lumin.graphics.renderers.TextRenderer; import com.github.lumin.modules.AbstractModule; import com.github.lumin.modules.Category; import com.github.lumin.settings.impl.IntSetting; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.RenderGuiEvent; import org.lwjgl.glfw.GLFW; @@ -26,19 +29,24 @@ public static RenderTest getInstance() { return INSTANCE; } - private final RectRenderer rectRenderer = new RectRenderer(); - private final TextRenderer textRenderer = new TextRenderer(); + private final Supplier rectRenderer = Suppliers.memoize(RectRenderer::new); + private final Supplier textRenderer = Suppliers.memoize(TextRenderer::new); + private final Supplier roundRectRenderer = Suppliers.memoize(RoundRectRenderer::new); private final IntSetting rectX = intSetting("rect_x", 10, 0, 100, 5); private final IntSetting rectY = intSetting("rect_y", 10, 0, 100, 5); @SubscribeEvent public void onRenderGui(RenderGuiEvent.Post event) { - rectRenderer.addRect(rectX.getValue(), rectY.getValue(), 200, 200, Color.WHITE); - rectRenderer.drawAndClear(); + rectRenderer.get().addRect(rectX.getValue(), rectY.getValue(), 200, 200, Color.WHITE); + rectRenderer.get().drawAndClear(); + + textRenderer.get().addText("Minecraft 原神 启动!", 10.0f, 10.0f, Color.BLACK, 1.0f); + textRenderer.get().drawAndClear(); + + roundRectRenderer.get().addRoundRect(100, 100, 100, 100, 10.0f, Color.BLUE); + roundRectRenderer.get().drawAndClear(); - textRenderer.addText("hello 你好 我真的很新欢你 我想要草席你手动阀", 10.0f, 10.0f, Color.BLACK, 1.0f); - textRenderer.drawAndClear(); } } diff --git a/src/main/resources/assets/lumin/shaders/round_rectangle.fsh b/src/main/resources/assets/lumin/shaders/round_rectangle.fsh new file mode 100644 index 0000000..8d7c433 --- /dev/null +++ b/src/main/resources/assets/lumin/shaders/round_rectangle.fsh @@ -0,0 +1,28 @@ +#version 450 core + +smooth in vec2 f_Position; +smooth in vec4 f_Color; +flat in vec4 f_InnerRect; +flat in float f_Radius; + +layout(location = 0) out vec4 fragColor; + +float aastep(float x) { + vec2 grad = vec2(dFdx(x), dFdy(x)); + float afwidth = 0.7 * length(grad); + return smoothstep(-afwidth, afwidth, x); +} + +void main() { + vec2 tl = f_InnerRect.xy - f_Position; + vec2 br = f_Position - f_InnerRect.zw; + + vec2 dis = max(tl, br); + + float v = length(max(vec2(0.0), dis)) - f_Radius; + + float alpha = 1.0 - aastep(v); + + if (alpha < 0.001) discard; + fragColor = vec4(f_Color.rgb, f_Color.a * alpha); +} \ No newline at end of file diff --git a/src/main/resources/assets/lumin/shaders/round_rectangle.vsh b/src/main/resources/assets/lumin/shaders/round_rectangle.vsh new file mode 100644 index 0000000..6a0d0ca --- /dev/null +++ b/src/main/resources/assets/lumin/shaders/round_rectangle.vsh @@ -0,0 +1,24 @@ +#version 460 core + +#moj_import +#moj_import + +layout(location = 0) in vec3 a_Position; +layout(location = 1) in vec4 a_Color; +layout(location = 2) in vec4 a_InnerRect; // ROUND_INNER_RECT +layout(location = 3) in float a_Radius; // ROUND_RADIUS + +out vec2 f_Position; +out vec4 f_Color; +flat out vec4 f_InnerRect; +flat out float f_Radius; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(a_Position, 1.0); + + f_Position = a_Position.xy; + f_Color = a_Color; + + f_InnerRect = a_InnerRect; + f_Radius = a_Radius; +} \ No newline at end of file From 32983846b5549faeee113fe6a229924227e56ad8 Mon Sep 17 00:00:00 2001 From: Chen Meng Date: Sun, 22 Feb 2026 21:38:32 +0800 Subject: [PATCH 2/2] use LuminBuffer to replace BufferBuilder --- .../graphics/renderers/RectRenderer.java | 84 ++++++++-- .../graphics/renderers/RoundRectRenderer.java | 16 +- .../graphics/text/ttf/TtfTextRenderer.java | 151 ++++++++++-------- .../lumin/modules/impl/visual/RenderTest.java | 7 +- 4 files changed, 164 insertions(+), 94 deletions(-) diff --git a/src/main/java/com/github/lumin/graphics/renderers/RectRenderer.java b/src/main/java/com/github/lumin/graphics/renderers/RectRenderer.java index f4eac28..a6b2921 100644 --- a/src/main/java/com/github/lumin/graphics/renderers/RectRenderer.java +++ b/src/main/java/com/github/lumin/graphics/renderers/RectRenderer.java @@ -1,36 +1,94 @@ package com.github.lumin.graphics.renderers; +import com.github.lumin.graphics.LuminRenderPipelines; import com.github.lumin.graphics.LuminRenderSystem; -import com.github.lumin.graphics.LuminRenderTypes; +import com.github.lumin.graphics.buffer.LuminBuffer; +import com.mojang.blaze3d.buffers.GpuBuffer; +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.systems.RenderPass; +import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.rendertype.TextureTransform; +import net.minecraft.util.ARGB; +import org.joml.Vector3f; +import org.joml.Vector4f; +import org.lwjgl.system.MemoryUtil; import java.awt.*; +import java.util.OptionalDouble; +import java.util.OptionalInt; public class RectRenderer implements IRenderer { - - private BufferBuilder bufferBuilder = Tesselator.getInstance() - .begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + private static final long BUFFER_SIZE = 1024 * 1024; + private final LuminBuffer buffer = new LuminBuffer(BUFFER_SIZE, GpuBuffer.USAGE_VERTEX); + private long currentOffset = 0; + private int vertexCount = 0; public void addRect(float x, float y, float width, float height, Color color) { - bufferBuilder.addVertex(x, y, 0.0f).setColor(color.getRGB()); - bufferBuilder.addVertex(x + width, y, 0.0f).setColor(color.getRGB()); - bufferBuilder.addVertex(x + width, y + height, 0.0f).setColor(color.getRGB()); - bufferBuilder.addVertex(x, y + height, 0.0f).setColor(color.getRGB()); + int argb = ARGB.toABGR(color.getRGB()); + + addVertex(x, y, argb); + addVertex(x, y + height, argb); + addVertex(x + width, y + height, argb); + addVertex(x + width, y, argb); + } + + private void addVertex(float vx, float vy, int color) { + long baseAddr = MemoryUtil.memAddress(buffer.getMappedBuffer()); + long p = baseAddr + currentOffset; + + MemoryUtil.memPutFloat(p, vx); + MemoryUtil.memPutFloat(p + 4, vy); + MemoryUtil.memPutFloat(p + 8, 0.0f); + MemoryUtil.memPutInt(p + 12, color); + + currentOffset += 16; + vertexCount++; } @Override public void draw() { + if (vertexCount == 0) return; LuminRenderSystem.applyOrthoProjection(); - MeshData meshData = bufferBuilder.build(); - if (meshData == null) return; + RenderTarget target = Minecraft.getInstance().getMainRenderTarget(); + if (target.getColorTextureView() == null) return; - LuminRenderTypes.RECTANGLE.draw(meshData); + final var indexCount = vertexCount / 4 * 6; + + RenderSystem.AutoStorageIndexBuffer autoIndices = + RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); + GpuBuffer ibo = autoIndices.getBuffer(indexCount); + + GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( + RenderSystem.getModelViewMatrix(), + new Vector4f(1, 1, 1, 1), + new Vector3f(0, 0, 0), + TextureTransform.DEFAULT_TEXTURING.getMatrix() + ); + + if (target.getColorTextureView() == null) return; + try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( + () -> "Rect Draw", + target.getColorTextureView(), OptionalInt.empty(), + target.getDepthTextureView(), OptionalDouble.empty()) + ) { + pass.setPipeline(LuminRenderPipelines.RECTANGLE); + + RenderSystem.bindDefaultUniforms(pass); + pass.setUniform("DynamicTransforms", dynamicUniforms); + + pass.setVertexBuffer(0, buffer.getGpuBuffer()); + pass.setIndexBuffer(ibo, autoIndices.type()); + pass.drawIndexed(0, 0, indexCount, 1); + } } @Override public void clear() { - bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + vertexCount = 0; + currentOffset = 0; } - } diff --git a/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java b/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java index 060c100..9c90b5b 100644 --- a/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java +++ b/src/main/java/com/github/lumin/graphics/renderers/RoundRectRenderer.java @@ -22,7 +22,7 @@ public class RoundRectRenderer implements IRenderer { - private static final long BUFFER_SIZE = 8 * 1024 * 1024; + private static final long BUFFER_SIZE = 2 * 1024 * 1024; private final LuminBuffer buffer = new LuminBuffer(BUFFER_SIZE, GpuBuffer.USAGE_VERTEX); public void addRoundRect(float x, float y, float width, float height, float radius, Color color) { @@ -80,18 +80,18 @@ public void draw() { RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); GpuBuffer ibo = autoIndices.getBuffer(indexCount); + GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( + RenderSystem.getModelViewMatrix(), + new Vector4f(1, 1, 1, 1), + new Vector3f(0, 0, 0), + TextureTransform.DEFAULT_TEXTURING.getMatrix() + ); + try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( () -> "Round Rect Draw", target.getColorTextureView(), OptionalInt.empty(), target.getDepthTextureView(), OptionalDouble.empty()) ) { - GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( - RenderSystem.getModelViewMatrix(), - new Vector4f(1, 1, 1, 1), - new Vector3f(0, 0, 0), - TextureTransform.DEFAULT_TEXTURING.getMatrix() - ); - pass.setPipeline(LuminRenderPipelines.ROUND_RECT); RenderSystem.bindDefaultUniforms(pass); diff --git a/src/main/java/com/github/lumin/graphics/text/ttf/TtfTextRenderer.java b/src/main/java/com/github/lumin/graphics/text/ttf/TtfTextRenderer.java index c4f2463..5b21152 100644 --- a/src/main/java/com/github/lumin/graphics/text/ttf/TtfTextRenderer.java +++ b/src/main/java/com/github/lumin/graphics/text/ttf/TtfTextRenderer.java @@ -1,6 +1,8 @@ package com.github.lumin.graphics.text.ttf; import com.github.lumin.graphics.LuminRenderPipelines; +import com.github.lumin.graphics.LuminRenderSystem; +import com.github.lumin.graphics.buffer.LuminBuffer; import com.github.lumin.graphics.text.GlyphDescriptor; import com.github.lumin.graphics.text.ITextRenderer; import com.github.lumin.utils.resources.ResourceLocationUtils; @@ -11,134 +13,145 @@ import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.DefaultVertexFormat; -import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.rendertype.TextureTransform; +import net.minecraft.util.ARGB; import org.joml.Vector3f; import org.joml.Vector4f; +import org.lwjgl.system.MemoryUtil; import java.awt.*; -import java.util.HashMap; -import java.util.Map; -import java.util.OptionalDouble; -import java.util.OptionalInt; +import java.util.*; public class TtfTextRenderer implements ITextRenderer { private static final float DEFAULT_SCALE = 0.35f; private static final float SPACING = 2f; + private static final int STRIDE = 24; // Pos(12) + UV(8) + Color(4) + + private static final long BUFFER_SIZE = 4 * 1024 * 1024; // 4MB + private final LuminBuffer buffer = new LuminBuffer(BUFFER_SIZE, GpuBuffer.USAGE_VERTEX); + + private final Map batches = new LinkedHashMap<>(); - private final HashMap buffers = new HashMap<>(); private final TtfFontLoader fontLoader = new TtfFontLoader(ResourceLocationUtils.getIdentifier("font/pingfang.ttf")); private GpuBuffer ttfInfoUniformBuf = null; + private long currentOffset = 0; + private int totalVertexCount = 0; + + private record AtlasBatch(long startOffset, int vertexCount) {} @Override public void addText(String text, float x, float y, Color color, float scale) { scale = scale * DEFAULT_SCALE; - fontLoader.checkAndLoadChars(text); + int argb = ARGB.toABGR(color.getRGB()); float xOffset = 0f; for (char ch : text.toCharArray()) { GlyphDescriptor glyph = fontLoader.getGlyph(ch); if (glyph == null) continue; - BufferBuilder buffer = buffers.computeIfAbsent(glyph.atlas(), a -> - Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR)); - + TtfGlyphAtlas atlas = glyph.atlas(); float baselineY = y + (fontLoader.fontFile.pixelAscent * scale); float x1 = x + xOffset; float x2 = x1 + glyph.width() * scale; - float y1 = baselineY + glyph.yOffset() * scale; float y2 = y1 + glyph.height() * scale; - buffer.addVertex(x1, y1, 0).setUv(glyph.uv().u0(), glyph.uv().v0()).setColor(color.getRGB()); - buffer.addVertex(x1, y2, 0).setUv(glyph.uv().u0(), glyph.uv().v1()).setColor(color.getRGB()); - buffer.addVertex(x2, y2, 0).setUv(glyph.uv().u1(), glyph.uv().v1()).setColor(color.getRGB()); - buffer.addVertex(x2, y1, 0).setUv(glyph.uv().u1(), glyph.uv().v0()).setColor(color.getRGB()); + long glyphOffset = currentOffset; + + addVertex(x1, y1, glyph.uv().u0(), glyph.uv().v0(), argb); + addVertex(x1, y2, glyph.uv().u0(), glyph.uv().v1(), argb); + addVertex(x2, y2, glyph.uv().u1(), glyph.uv().v1(), argb); + addVertex(x2, y1, glyph.uv().u1(), glyph.uv().v0(), argb); + + // Update the batch information of the atlas + batches.merge(atlas, new AtlasBatch(glyphOffset, 4), + (old, val) -> new AtlasBatch(old.startOffset(), old.vertexCount() + 4)); xOffset += glyph.advance() * scale + SPACING; } } + private void addVertex(float vx, float vy, float u, float v, int color) { + long baseAddr = MemoryUtil.memAddress(buffer.getMappedBuffer()); + long p = baseAddr + currentOffset; + + MemoryUtil.memPutFloat(p, vx); + MemoryUtil.memPutFloat(p + 4, vy); + MemoryUtil.memPutFloat(p + 8, 0.0f); + MemoryUtil.memPutFloat(p + 12, u); + MemoryUtil.memPutFloat(p + 16, v); + MemoryUtil.memPutInt(p + 20, color); + + currentOffset += STRIDE; + totalVertexCount++; + } + @Override public void draw() { - if (ttfInfoUniformBuf == null) { - final var size = new Std140SizeCalculator() - .putFloat() - .get(); + if (totalVertexCount == 0) return; + LuminRenderSystem.applyOrthoProjection(); + if (ttfInfoUniformBuf == null) { + final var size = new Std140SizeCalculator().putFloat().get(); ttfInfoUniformBuf = RenderSystem.getDevice().createBuffer( - () -> "Lumin TTF UBO", - GpuBuffer.USAGE_UNIFORM | GpuBuffer.USAGE_MAP_WRITE, - size - ); - - try (GpuBuffer.MappedView mappedView = RenderSystem - .getDevice() - .createCommandEncoder() - .mapBuffer(ttfInfoUniformBuf, false, true) - ) { - Std140Builder.intoBuffer(mappedView.data()) - .putFloat(0.5f); + () -> "Lumin TTF UBO", GpuBuffer.USAGE_UNIFORM | GpuBuffer.USAGE_MAP_WRITE, size); + + try (GpuBuffer.MappedView mappedView = RenderSystem.getDevice().createCommandEncoder() + .mapBuffer(ttfInfoUniformBuf, false, true)) { + Std140Builder.intoBuffer(mappedView.data()).putFloat(0.5f); } } - for (Map.Entry entry : buffers.entrySet()) { - TtfGlyphAtlas atlas = entry.getKey(); - BufferBuilder buffer = entry.getValue(); - - try (final var meshData = buffer.build()) { - if (meshData == null) continue; + RenderTarget target = Minecraft.getInstance().getMainRenderTarget(); + if (target.getColorTextureView() == null) return; - GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( - RenderSystem.getModelViewMatrix(), - new Vector4f(1, 1, 1, 1), - new Vector3f(0, 0, 0), - TextureTransform.DEFAULT_TEXTURING.getMatrix() - ); + GpuBufferSlice dynamicUniforms = RenderSystem.getDynamicUniforms().writeTransform( + RenderSystem.getModelViewMatrix(), new Vector4f(1, 1, 1, 1), + new Vector3f(0, 0, 0), TextureTransform.DEFAULT_TEXTURING.getMatrix() + ); - GpuBuffer vbo = DefaultVertexFormat.POSITION_TEX_COLOR - .uploadImmediateVertexBuffer(meshData.vertexBuffer()); - RenderSystem.AutoStorageIndexBuffer autoIndices = - RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - GpuBuffer ibo = autoIndices.getBuffer(meshData.drawState().indexCount()); + for (Map.Entry entry : batches.entrySet()) { + TtfGlyphAtlas atlas = entry.getKey(); + AtlasBatch batch = entry.getValue(); - RenderTarget target = Minecraft.getInstance().getMainRenderTarget(); - if (target.getColorTextureView() == null) return; + int indexCount = batch.vertexCount() / 4 * 6; + int vertexOffset = (int) (batch.startOffset() / STRIDE); - try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( - () -> "Lumin TTF Draw", - target.getColorTextureView(), OptionalInt.empty(), - target.getDepthTextureView(), OptionalDouble.empty()) - ) { + RenderSystem.AutoStorageIndexBuffer autoIndices = + RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); + GpuBuffer ibo = autoIndices.getBuffer(indexCount); - pass.setPipeline(LuminRenderPipelines.TTF_FONT); + try (RenderPass pass = RenderSystem.getDevice().createCommandEncoder().createRenderPass( + () -> "Lumin TTF Draw", + target.getColorTextureView(), OptionalInt.empty(), + target.getDepthTextureView(), OptionalDouble.empty()) + ) { + pass.setPipeline(LuminRenderPipelines.TTF_FONT); - RenderSystem.bindDefaultUniforms(pass); - pass.setUniform("DynamicTransforms", dynamicUniforms); - pass.setUniform("TtfInfo", ttfInfoUniformBuf); - pass.setVertexBuffer(0, vbo); - pass.setIndexBuffer(ibo, autoIndices.type()); + RenderSystem.bindDefaultUniforms(pass); + pass.setUniform("DynamicTransforms", dynamicUniforms); + pass.setUniform("TtfInfo", ttfInfoUniformBuf); - pass.bindTexture("Sampler0", atlas.getTexture().textureView(), atlas.getTexture().sampler()); + pass.setVertexBuffer(0, buffer.getGpuBuffer()); + pass.setIndexBuffer(ibo, autoIndices.type()); + pass.bindTexture("Sampler0", atlas.getTexture().textureView(), atlas.getTexture().sampler()); - pass.drawIndexed(0, 0, meshData.drawState().indexCount(), 1); - } + pass.drawIndexed(0, vertexOffset, indexCount, 1); } } } @Override public void clear() { - buffers.clear(); + currentOffset = 0; + totalVertexCount = 0; + batches.clear(); } - -} +} \ No newline at end of file diff --git a/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java b/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java index 9e4b8bb..53ed217 100644 --- a/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java +++ b/src/main/java/com/github/lumin/modules/impl/visual/RenderTest.java @@ -39,13 +39,12 @@ public static RenderTest getInstance() { @SubscribeEvent public void onRenderGui(RenderGuiEvent.Post event) { rectRenderer.get().addRect(rectX.getValue(), rectY.getValue(), 200, 200, Color.WHITE); - rectRenderer.get().drawAndClear(); - textRenderer.get().addText("Minecraft 原神 启动!", 10.0f, 10.0f, Color.BLACK, 1.0f); - textRenderer.get().drawAndClear(); - roundRectRenderer.get().addRoundRect(100, 100, 100, 100, 10.0f, Color.BLUE); + + rectRenderer.get().drawAndClear(); roundRectRenderer.get().drawAndClear(); + textRenderer.get().drawAndClear(); }