Skip to content

Commit 6045b7e

Browse files
committed
Persistent byte puffers for VertexPipeline
1 parent b1b3e7f commit 6045b7e

36 files changed

+1071
-757
lines changed

common/src/main/java/com/lambda/mixin/render/VertexBufferMixin.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
package com.lambda.mixin.render;
1919

20-
import com.lambda.graphics.buffer.vertex.ElementBuffer;
20+
import com.lambda.graphics.buffer.Buffer;
2121
import com.mojang.blaze3d.systems.RenderSystem;
2222
import net.minecraft.client.gl.VertexBuffer;
2323
import net.minecraft.client.render.BufferBuilder;
@@ -37,6 +37,6 @@ public class VertexBufferMixin {
3737
@Inject(method = "uploadIndexBuffer", at = @At("RETURN"))
3838
private void onConfigureIndexBuffer(BufferBuilder.DrawParameters parameters, ByteBuffer vertexBuffer, CallbackInfoReturnable<RenderSystem.ShapeIndexBuffer> cir) {
3939
RenderSystem.ShapeIndexBuffer value = cir.getReturnValue();
40-
ElementBuffer.lastIbo = value == null ? this.indexBufferId : value.id;
40+
Buffer.lastIbo = value == null ? this.indexBufferId : value.id;
4141
}
4242
}

common/src/main/kotlin/com/lambda/graphics/RenderMain.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import com.lambda.graphics.gl.GlStateUtils.setupGL
2424
import com.lambda.graphics.gl.Matrices
2525
import com.lambda.graphics.gl.Matrices.resetMatrices
2626
import com.lambda.module.modules.client.GuiSettings
27+
import com.lambda.util.Communication.info
2728
import com.lambda.util.math.Vec2d
2829
import com.mojang.blaze3d.systems.RenderSystem.getProjectionMatrix
2930
import org.joml.Matrix4f

common/src/main/kotlin/com/lambda/graphics/buffer/Buffer.kt

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,30 @@ abstract class Buffer(
154154
return null
155155
}
156156

157+
/**
158+
* Update the current buffer without re-allocating
159+
* This function handles the buffer binding
160+
* Alternative to [map]
161+
*/
162+
open fun update(
163+
offset: Long,
164+
size: Long,
165+
dataPointer: Long
166+
): Throwable? {
167+
if (!bufferValid(target, access))
168+
return IllegalArgumentException("Target is not valid. Refer to the table in the documentation")
169+
170+
repeat(buffers) {
171+
bind()
172+
nglBufferSubData(target, offset, size, dataPointer)
173+
swap()
174+
}
175+
176+
bind(0)
177+
178+
return null
179+
}
180+
157181
/**
158182
* Allocates a region of memory for the buffer
159183
* This function handles the buffer binding
@@ -332,4 +356,32 @@ abstract class Buffer(
332356
if (isVertexArray) glGenVertexArrays(bufferIds) // If there are more than 1 buffer you should expect undefined behavior, this is not the way to do it
333357
else glGenBuffers(bufferIds)
334358
}
335-
}
359+
360+
companion object {
361+
@JvmField
362+
var lastIbo = 0
363+
var prevIbo = 0
364+
365+
fun createPipelineBuffer(bufferTarget: Int) = object : Buffer(buffers = 1) {
366+
override val target: Int = bufferTarget
367+
368+
override val usage: Int = GL_STATIC_DRAW
369+
override val access: Int = GL_MAP_WRITE_BIT
370+
371+
override fun bind(id: Int) {
372+
if (bufferTarget != GL_ELEMENT_ARRAY_BUFFER) {
373+
super.bind(id)
374+
return
375+
}
376+
377+
if (id != 0) prevIbo = lastIbo
378+
super.bind(if (id != 0) id else prevIbo)
379+
}
380+
381+
override fun upload(
382+
data: ByteBuffer,
383+
offset: Long,
384+
): Throwable = UnsupportedOperationException()
385+
}
386+
}
387+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.graphics.buffer
19+
20+
import org.lwjgl.BufferUtils.createByteBuffer
21+
import org.lwjgl.system.MemoryUtil.memAddress0
22+
import org.lwjgl.system.MemoryUtil.memCopy
23+
import org.lwjgl.system.MemoryUtil.memPutByte
24+
import org.lwjgl.system.MemoryUtil.memPutFloat
25+
import org.lwjgl.system.MemoryUtil.memPutInt
26+
import java.awt.Color
27+
import java.nio.ByteBuffer
28+
29+
/**
30+
* Dynamically resizable byte buffer designed for efficient vertex building.
31+
* Automatically grows capacity when needed, and provides
32+
* convenience methods for common data types used in vertex attributes.
33+
*
34+
* @property data The underlying [java.nio.ByteBuffer] storing the vertex data
35+
* @property capacity Current maximum capacity of the buffer in bytes
36+
* @property pointer Base memory address of the buffer
37+
* @property position Current write position in memory address space
38+
*/
39+
class DynamicByteBuffer private constructor(initialCapacity: Int) {
40+
var data: ByteBuffer = createByteBuffer(initialCapacity); private set
41+
var capacity = initialCapacity; private set
42+
43+
var pointer = memAddress0(data); private set
44+
var position = pointer; private set
45+
46+
/**
47+
* Gets the total number of bytes written to the buffer since last reset
48+
*/
49+
val bytesPut get() = position - pointer
50+
51+
/**
52+
* Resets the write position to the beginning of the buffer while maintaining current capacity,
53+
* allowing for buffer reuse without reallocation
54+
*/
55+
fun resetPosition() {
56+
position = pointer
57+
}
58+
59+
/**
60+
* Writes a single byte value at the current position
61+
* @param value The byte value to write
62+
*/
63+
fun putByte(value: Byte) {
64+
require(1)
65+
memPutByte(position, value)
66+
position += 1
67+
}
68+
69+
/**
70+
* Writes a 4-byte integer value at the current position
71+
* @param value The integer value to write
72+
*/
73+
fun putInt(value: Int) {
74+
require(4)
75+
memPutInt(position, value)
76+
position += 4
77+
}
78+
79+
/**
80+
* Writes a 4-byte floating point value at the current position
81+
* @param value The double-precision value to write (will be converted to float)
82+
*/
83+
fun putFloat(value: Double) {
84+
require(4)
85+
memPutFloat(position, value.toFloat())
86+
position += 4
87+
}
88+
89+
/**
90+
* Writes a 2-component vector as two consecutive 4-byte floats
91+
* @param x X-axis component
92+
* @param y Y-axis component
93+
*/
94+
fun putVec2(x: Double, y: Double) {
95+
require(8)
96+
memPutFloat(position + 0, x.toFloat())
97+
memPutFloat(position + 4, y.toFloat())
98+
position += 8
99+
}
100+
101+
/**
102+
* Writes a 3-component vector as three consecutive 4-byte floats
103+
* @param x X-axis component
104+
* @param y Y-axis component
105+
* @param z Z-axis component
106+
*/
107+
fun putVec3(x: Double, y: Double, z: Double) {
108+
require(12)
109+
memPutFloat(position + 0, x.toFloat())
110+
memPutFloat(position + 4, y.toFloat())
111+
memPutFloat(position + 8, z.toFloat())
112+
position += 12
113+
}
114+
115+
/**
116+
* Writes a color as four consecutive bytes in RGBA format
117+
* @param color The color to write
118+
*/
119+
fun putColor(color: Color) {
120+
require(4)
121+
memPutByte(position + 0, color.red.toByte())
122+
memPutByte(position + 1, color.green.toByte())
123+
memPutByte(position + 2, color.blue.toByte())
124+
memPutByte(position + 3, color.alpha.toByte())
125+
position += 4
126+
}
127+
128+
/**
129+
* Ensures the buffer has enough remaining space for the requested number of bytes.
130+
* Automatically grows the buffer if insufficient space remains.
131+
* @param size Number of bytes required for the next write operation
132+
*/
133+
fun require(size: Int) {
134+
if (capacity - bytesPut > size) return
135+
grow(capacity * 2)
136+
}
137+
138+
/**
139+
* Increases buffer capacity while preserving existing data. New capacity must be greater than current.
140+
* @param newCapacity New buffer capacity in bytes
141+
* @throws IllegalArgumentException if newCapacity is not greater than current capacity
142+
*/
143+
fun grow(newCapacity: Int) {
144+
check(newCapacity > capacity) {
145+
"Cannot grow buffer beyond its capacity"
146+
}
147+
148+
val newBuffer = createByteBuffer(newCapacity)
149+
val newPointer = memAddress0(newBuffer)
150+
val offset = position - pointer
151+
152+
memCopy(pointer, newPointer, offset)
153+
data = newBuffer
154+
155+
pointer = newPointer
156+
position = newPointer + offset
157+
capacity = newCapacity
158+
}
159+
160+
/**
161+
* Reallocates the buffer with exact new capacity, resetting position and discarding existing data
162+
* @param newCapacity New buffer capacity in bytes
163+
*/
164+
fun realloc(newCapacity: Int) {
165+
if (newCapacity == capacity) {
166+
resetPosition()
167+
return
168+
}
169+
170+
val newBuffer = createByteBuffer(newCapacity)
171+
val newPointer = memAddress0(newBuffer)
172+
173+
data = newBuffer
174+
pointer = newPointer
175+
position = newPointer
176+
capacity = newCapacity
177+
}
178+
179+
companion object {
180+
/**
181+
* Creates a new DynamicByteBuffer with specified initial capacity
182+
* @param initialCapacity Starting buffer size in bytes
183+
*/
184+
fun dynamicByteBuffer(initialCapacity: Int) =
185+
DynamicByteBuffer(initialCapacity)
186+
}
187+
}

common/src/main/kotlin/com/lambda/graphics/buffer/IRenderContext.kt

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)