Skip to content

Commit 80d41e3

Browse files
committed
ref: common buffer class
Also fixed the memory mapping caused by using un-allocated buffers
1 parent 31b24f1 commit 80d41e3

File tree

6 files changed

+85
-57
lines changed

6 files changed

+85
-57
lines changed

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

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import org.lwjgl.opengl.GL30C.*
2424
import org.lwjgl.opengl.GL44.glBufferStorage
2525
import java.nio.ByteBuffer
2626

27-
interface IBuffer {
27+
abstract class IBuffer(
2828
/**
2929
* Specifies how many buffer must be used
3030
*
@@ -40,8 +40,13 @@ interface IBuffer {
4040
*
4141
* Triple buffering helps maintain smoother frame rates, but if your app runs faster than the monitor's refresh rate, it offers little benefit as you eventually still wait for vblank synchronization.
4242
*/
43-
val buffers: Int
43+
val buffers: Int = 1,
4444

45+
/**
46+
* Edge case to handle vertex arrays
47+
*/
48+
val isVertexArray: Boolean = false,
49+
) {
4550
/**
4651
* Specifies how the buffers are used
4752
*
@@ -57,7 +62,7 @@ interface IBuffer {
5762
* | GL_DYNAMIC_READ | Data is modified repeatedly and used many times for reading. |
5863
* | GL_DYNAMIC_COPY | Data is modified repeatedly and used many times for copying. |
5964
*/
60-
val usage: Int
65+
abstract val usage: Int
6166

6267
/**
6368
* Specifies the target to which the buffer object is bound which must be one
@@ -80,7 +85,7 @@ interface IBuffer {
8085
* | GL_TRANSFORM_FEEDBACK_BUFFER | Transform feedback buffer |
8186
* | GL_UNIFORM_BUFFER | Uniform block storage |
8287
*/
83-
val target: Int
88+
abstract val target: Int
8489

8590
/**
8691
* Specifies a combination of access flags indicating the desired
@@ -97,22 +102,22 @@ interface IBuffer {
97102
* | GL_MAP_FLUSH_EXPLICIT_BIT | Requires explicit flushing of modified sub-ranges. | Only with GL_MAP_WRITE_BIT. Data may be undefined if skipped. |
98103
* | GL_MAP_UNSYNCHRONIZED_BIT | Skips synchronization before mapping. | May cause data corruption if regions overlap. |
99104
*/
100-
val access: Int
105+
abstract val access: Int
101106

102107
/**
103108
* Index of the current buffer
104109
*/
105-
var index: Int
110+
var index: Int = 0; private set
106111

107112
/**
108113
* List of all the buffers
109114
*/
110-
val bufferIds: IntArray
115+
private val bufferIds = IntArray(buffers)
111116

112117
/**
113118
* Binds the buffer id to the [target]
114119
*/
115-
fun bind(id: Int) = glBindBuffer(target, id)
120+
open fun bind(id: Int) = glBindBuffer(target, id)
116121

117122
/**
118123
* Binds current the buffer [index] to the [target]
@@ -130,7 +135,7 @@ interface IBuffer {
130135
* Update the current buffer without re-allocating
131136
* Alternative to [map]
132137
*/
133-
fun update(
138+
open fun update(
134139
data: ByteBuffer,
135140
offset: Long,
136141
): Throwable? {
@@ -151,15 +156,19 @@ interface IBuffer {
151156
*
152157
* @param data The data to put in the new allocated buffer
153158
*/
154-
fun allocate(data: ByteBuffer): Throwable? {
159+
open fun allocate(data: ByteBuffer): Throwable? {
155160
if (!bufferValid(target, access))
156161
return IllegalArgumentException("Target is not valid. Refer to the table in the documentation")
157162

158163
if (!bufferUsageValid(usage))
159164
return IllegalArgumentException("Buffer usage is invalid")
160165

161-
bind()
162-
glBufferData(target, data, usage)
166+
repeat(buffers) {
167+
bind()
168+
glBufferData(target, data, usage)
169+
swap()
170+
}
171+
163172
bind(0)
164173

165174
return null
@@ -171,15 +180,19 @@ interface IBuffer {
171180
*
172181
* @param size The size of the new buffer
173182
*/
174-
fun allocate(size: Long): Throwable? {
183+
open fun allocate(size: Long): Throwable? {
175184
if (!bufferValid(target, access))
176185
return IllegalArgumentException("Target is not valid. Refer to the table in the documentation")
177186

178187
if (!bufferUsageValid(usage))
179188
return IllegalArgumentException("Buffer usage is invalid")
180189

181-
bind()
182-
glBufferData(target, size.coerceAtLeast(0), usage)
190+
repeat(buffers) {
191+
bind()
192+
glBufferData(target, size.coerceAtLeast(0), usage)
193+
swap()
194+
}
195+
183196
bind(0)
184197

185198
return null
@@ -190,15 +203,19 @@ interface IBuffer {
190203
* This function cannot be called twice for the same buffer
191204
* This function handles the buffer binding
192205
*/
193-
fun storage(data: ByteBuffer): Throwable? {
206+
open fun storage(data: ByteBuffer): Throwable? {
194207
if (!bufferValid(target, access))
195208
return IllegalArgumentException("Target is not valid. Refer to the table in the documentation")
196209

197210
if (!bufferUsageValid(usage))
198211
return IllegalArgumentException("Buffer usage is invalid")
199212

200-
bind()
201-
glBufferStorage(target, data, usage)
213+
repeat(buffers) {
214+
bind()
215+
glBufferStorage(target, data, usage)
216+
swap()
217+
}
218+
202219
bind(0)
203220

204221
return null
@@ -211,15 +228,19 @@ interface IBuffer {
211228
*
212229
* @param size The size of the storage buffer
213230
*/
214-
fun storage(size: Long): Throwable? {
231+
open fun storage(size: Long): Throwable? {
215232
if (!bufferValid(target, access))
216233
return IllegalArgumentException("Target is not valid. Refer to the table in the documentation")
217234

218235
if (!bufferUsageValid(usage))
219236
return IllegalArgumentException("Buffer usage is invalid")
220237

221-
bind()
222-
glBufferStorage(target, size.coerceAtLeast(0), usage)
238+
repeat(buffers) {
239+
bind()
240+
glBufferStorage(target, size.coerceAtLeast(0), usage)
241+
swap()
242+
}
243+
223244
bind(0)
224245

225246
return null
@@ -228,14 +249,14 @@ interface IBuffer {
228249
/**
229250
* Maps all or part of a buffer object's data store into the client's address space
230251
*
231-
* @param offset Specifies the starting offset within the buffer of the range to be mapped.
232252
* @param size Specifies the length of the range to be mapped.
253+
* @param offset Specifies the starting offset within the buffer of the range to be mapped.
233254
* @param block Lambda scope with the mapped buffer passed in
234255
* @return Error encountered during the mapping process
235256
*/
236-
fun map(
237-
offset: Long,
257+
open fun map(
238258
size: Long,
259+
offset: Long,
239260
block: (ByteBuffer) -> Unit
240261
): Throwable? {
241262
if (
@@ -251,7 +272,7 @@ interface IBuffer {
251272

252273
if (
253274
offset + size > glGetBufferParameteri(target, GL_BUFFER_SIZE)
254-
) return IllegalArgumentException("Out of bound mapping: $offset + $size > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}")
275+
) return IllegalArgumentException("Out of bound (is the buffer initialized?) $size + $offset > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}")
255276

256277
if (
257278
glGetBufferParameteri(target, GL_BUFFER_MAPPED)
@@ -292,7 +313,7 @@ interface IBuffer {
292313
* @param offset The starting offset within the buffer of the range to be mapped
293314
* @return Error encountered during the mapping process
294315
*/
295-
fun upload(data: ByteArray, offset: Long): Throwable? =
316+
open fun upload(data: ByteArray, offset: Long): Throwable? =
296317
upload(ByteBuffer.wrap(data), offset)
297318

298319
/**
@@ -302,5 +323,13 @@ interface IBuffer {
302323
* @param offset The starting offset within the buffer of the range to be mapped
303324
* @return Error encountered during the mapping process
304325
*/
305-
fun upload(data: ByteBuffer, offset: Long): Throwable?
326+
abstract fun upload(data: ByteBuffer, offset: Long): Throwable?
327+
328+
init {
329+
// Special edge case for vertex arrays
330+
check(buffers > 0) { "Cannot generate less than one buffer" }
331+
332+
if (isVertexArray) glGenVertexArrays(bufferIds) // If there are more than 1 buffer you should expect undefined behavior, this is not the way to do it
333+
else glGenBuffers(bufferIds)
334+
}
306335
}

common/src/main/kotlin/com/lambda/graphics/buffer/pixel/PixelBuffer.kt

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,39 @@
1818
package com.lambda.graphics.buffer.pixel
1919

2020
import com.lambda.graphics.buffer.IBuffer
21+
import com.lambda.graphics.gl.putTo
2122
import com.lambda.graphics.texture.Texture
23+
import com.lambda.util.math.MathUtils.toInt
2224
import org.lwjgl.opengl.GL45C.*
2325
import java.nio.ByteBuffer
2426

2527
/**
2628
* Represents a Pixel Buffer Object (PBO) that facilitates asynchronous data transfer to the GPU.
27-
* This class manages the creation, usage, and cleanup of PBOs and provides methods to upload (map) data efficiently.
2829
*
29-
* **Process**:
3030
* Every function that performs a pixel transfer operation can use buffer objects instead of client memory.
3131
* Functions that perform an upload operation, a pixel unpack, will use the buffer object bound to the target GL_PIXEL_UNPACK_BUFFER.
3232
* If a buffer is bound, then the pointer value that those functions take is not a pointer, but an offset from the beginning of that buffer.
3333
*
34-
* @property width The width of the texture
35-
* @property height The height of the texture
36-
* @property texture The [Texture] instance
37-
* @property format The image format that will be uploaded
34+
* @property width The width of the texture
35+
* @property height The height of the texture
36+
* @property format The image format that will be uploaded
37+
* @property texture The [Texture] instance to use
38+
* @property asynchronous Whether to use 2 buffers or not
39+
* @property bufferMapping Whether to map a block in memory to upload or not
3840
*
39-
* @see <a href="https://www.khronos.org/opengl/wiki/Pixel_Buffer_Object">Pixel Buffer Object</a>
41+
* @see <a href="https://www.khronos.org/opengl/wiki/Pixel_Buffer_Object">Reference</a>
4042
*/
4143
class PixelBuffer(
4244
private val width: Int,
4345
private val height: Int,
44-
private val texture: Texture,
4546
private val format: Int,
46-
) : IBuffer {
47-
override val buffers: Int = 1
47+
private val texture: Texture,
48+
private val asynchronous: Boolean = false,
49+
private val bufferMapping: Boolean = false,
50+
) : IBuffer(buffers = asynchronous.toInt() + 1) {
4851
override val usage: Int = GL_STATIC_DRAW
4952
override val target: Int = GL_PIXEL_UNPACK_BUFFER
5053
override val access: Int = GL_MAP_WRITE_BIT
51-
override var index = 0
52-
override val bufferIds = IntArray(buffers).apply { glGenBuffers(this) }
5354

5455
private val channels = channelMapping[format] ?: throw IllegalArgumentException("Invalid image format, expected OpenGL format, got $format instead")
5556
private val internalFormat = reverseChannelMapping[channels] ?: throw IllegalArgumentException("Invalid internal image format, expected channels count, got $channels instead")
@@ -74,7 +75,12 @@ class PixelBuffer(
7475
0, // PBO offset (for asynchronous transfer)
7576
)
7677

77-
val error = update(data, offset)
78+
swap()
79+
bind()
80+
81+
val error =
82+
if (bufferMapping) map(size, offset, data::putTo)
83+
else update(data, offset)
7884

7985
bind(0)
8086

@@ -89,7 +95,7 @@ class PixelBuffer(
8995
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
9096
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
9197

92-
allocate(size)
98+
storage(size)
9399
}
94100

95101
companion object {

common/src/main/kotlin/com/lambda/graphics/buffer/vertex/ElementBuffer.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,12 @@ import com.lambda.graphics.gl.kibibyte
2323
import org.lwjgl.opengl.GL30C.*
2424
import java.nio.ByteBuffer
2525

26-
class ElementBuffer(mode: VertexMode) : IBuffer {
27-
override val buffers: Int = 1
26+
class ElementBuffer(mode: VertexMode) :
27+
IBuffer(buffers = 1)
28+
{
2829
override val usage: Int = GL_DYNAMIC_DRAW
2930
override val target: Int = GL_ELEMENT_ARRAY_BUFFER
3031
override val access: Int = GL_MAP_WRITE_BIT
31-
override var index = 0
32-
override val bufferIds = intArrayOf(glGenBuffers())
3332

3433
override fun upload(
3534
data: ByteBuffer,

common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexArray.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,21 @@ import net.minecraft.client.render.BufferRenderer
2222
import org.lwjgl.opengl.GL30C.*
2323
import java.nio.ByteBuffer
2424

25-
class VertexArray : IBuffer {
26-
override val buffers: Int = 1
25+
class VertexArray : IBuffer(isVertexArray = true) {
2726
override val usage: Int = -1
2827
override val target: Int = -1
2928
override val access: Int = -1
30-
override var index = 0
31-
override val bufferIds = intArrayOf(glGenVertexArrays())
3229

3330
override fun map(
34-
offset: Long,
3531
size: Long,
32+
offset: Long,
3633
block: (ByteBuffer) -> Unit
37-
): Throwable? = throw UnsupportedOperationException("Cannot map a vertex array object to memory")
34+
): Throwable = throw UnsupportedOperationException("Cannot map a vertex array object to memory")
3835

3936
override fun upload(
4037
data: ByteBuffer,
4138
offset: Long,
42-
): Throwable? = throw UnsupportedOperationException("Data cannot be uploaded to a vertex array object")
39+
): Throwable = throw UnsupportedOperationException("Data cannot be uploaded to a vertex array object")
4340

4441
override fun allocate(size: Long) = throw UnsupportedOperationException("Cannot grow a vertex array object")
4542

common/src/main/kotlin/com/lambda/graphics/buffer/vertex/VertexBuffer.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,10 @@ import java.nio.ByteBuffer
2828
class VertexBuffer(
2929
mode: VertexMode,
3030
attributes: VertexAttrib.Group,
31-
) : IBuffer {
32-
override val buffers: Int = 1
31+
) : IBuffer(buffers = 1) {
3332
override val usage: Int = GL_DYNAMIC_DRAW
3433
override val target: Int = GL_ARRAY_BUFFER
3534
override val access: Int = GL_MAP_WRITE_BIT
36-
override var index = 0
37-
override val bufferIds = intArrayOf(glGenBuffers())
3835

3936
override fun upload(
4037
data: ByteBuffer,

common/src/main/kotlin/com/lambda/graphics/texture/AnimatedTexture.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,6 @@ class AnimatedTexture(
9595

9696
init {
9797
readGif()
98-
pbo = PixelBuffer(width, height, this@AnimatedTexture, format = GL_RGBA)
98+
pbo = PixelBuffer(width, height, format = GL_RGBA, this@AnimatedTexture)
9999
}
100100
}

0 commit comments

Comments
 (0)