Skip to content

Commit beddf73

Browse files
committed
ref: buffer orphaning over mapping
1 parent 7b37548 commit beddf73

File tree

8 files changed

+63
-82
lines changed

8 files changed

+63
-82
lines changed

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

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,35 @@ interface IBuffer {
144144
return null
145145
}
146146

147+
/**
148+
* Allocates a region of memory for the buffer
149+
* This function handles the buffer binding
150+
*
151+
* @param data The data to put in the new allocated buffer
152+
*/
153+
fun allocate(data: ByteBuffer): Throwable? {
154+
// FixMe: If access contains any of GL_MAP_PERSISTENT_BIT or GL_MAP_COHERENT_BIT and the buffer was not initialized using glBufferStorage, glMapBufferRange will fail
155+
if (!bufferValid(target))
156+
return IllegalArgumentException("Target is not valid. Refer to the table in the documentation")
157+
158+
if (!bufferUsageValid(usage))
159+
return IllegalArgumentException("Buffer usage is invalid")
160+
161+
bind()
162+
glBufferData(target, data, usage)
163+
bind(0)
164+
165+
return null
166+
}
167+
147168
/**
148169
* Grows the backing buffers
149-
* This function should not be called frequently
170+
* This function handles the buffer binding
150171
*
151172
* @param size The size of the new buffer
152173
*/
153-
fun grow(size: Long): Throwable? {
154-
if (
155-
size < 0
156-
) return IllegalArgumentException("Invalid size parameter: $size")
174+
fun allocate(size: Long): Throwable? {
175+
if (size < 0) return IllegalArgumentException("Invalid size parameter: $size")
157176

158177
// FixMe: If access contains any of GL_MAP_PERSISTENT_BIT or GL_MAP_COHERENT_BIT and the buffer was not initialized using glBufferStorage, glMapBufferRange will fail
159178
if (!bufferValid(target))
@@ -162,16 +181,9 @@ interface IBuffer {
162181
if (!bufferUsageValid(usage))
163182
return IllegalArgumentException("Buffer usage is invalid")
164183

165-
bufferIds.forEach { bufferId ->
166-
// Orphan the buffer and allocate a new one
167-
bind(bufferId)
168-
169-
// Only resize if the new size is bigger than the bound buffer capacity
170-
if (size > glGetBufferParameteri(target, GL_BUFFER_SIZE))
171-
glBufferData(target, size, usage)
172-
173-
bind(0) // Don't forget to unbind to avoid accidental buffer modification
174-
}
184+
bind()
185+
glBufferData(target, size, usage)
186+
bind(0)
175187

176188
return null
177189
}
@@ -202,14 +214,7 @@ interface IBuffer {
202214

203215
if (
204216
offset + size > glGetBufferParameteri(target, GL_BUFFER_SIZE)
205-
) return IllegalArgumentException(
206-
"Out of bound mapping: $offset + $size > ${
207-
glGetBufferParameteri(
208-
target,
209-
GL_BUFFER_SIZE
210-
)
211-
}"
212-
)
217+
) return IllegalArgumentException("Out of bound mapping: $offset + $size > ${glGetBufferParameteri(target, GL_BUFFER_SIZE)}")
213218

214219
if (
215220
glGetBufferParameteri(target, GL_BUFFER_MAPPED)
@@ -222,11 +227,11 @@ interface IBuffer {
222227
) return IllegalArgumentException("Neither GL_MAP_READ_BIT nor GL_MAP_WRITE_BIT is set")
223228

224229
if (
225-
access and GL_MAP_READ_BIT != 0 &&
230+
access and GL_MAP_READ_BIT != 0 &&
226231
(access and GL_MAP_INVALIDATE_RANGE_BIT == 0 ||
227-
access and GL_MAP_INVALIDATE_BUFFER_BIT == 0 ||
228-
access and GL_MAP_UNSYNCHRONIZED_BIT == 0
229-
)
232+
access and GL_MAP_INVALIDATE_BUFFER_BIT == 0 ||
233+
access and GL_MAP_UNSYNCHRONIZED_BIT == 0
234+
)
230235
) return IllegalArgumentException("GL_MAP_READ_BIT is set and any of GL_MAP_INVALIDATE_RANGE_BIT, GL_MAP_INVALIDATE_BUFFER_BIT or GL_MAP_UNSYNCHRONIZED_BIT is set.")
231236

232237
// Map the buffer into the client's memory

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,6 @@ class VertexPipeline(
138138
val newVertices = byteBuffer(newSize)
139139
Memory.copy(address(vertices), address(newVertices), offset)
140140

141-
vbo.grow(newSize.toLong())
142-
143141
vertices = newVertices
144142
verticesPointer = address(vertices)
145143
verticesPosition = verticesPointer + offset
@@ -154,8 +152,6 @@ class VertexPipeline(
154152

155153
Memory.copy(address(indices), address(newIndices), indicesCount * 4L)
156154

157-
ebo.grow(newSize.toLong())
158-
159155
indices = newIndices
160156
indicesPointer = address(indices)
161157
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class PixelBuffer(
122122
glBindTexture(GL_TEXTURE_2D, 0)
123123

124124
// Fill the buffers with null data to allocate the memory spaces
125-
grow(size)
125+
allocate(size)
126126
}
127127

128128
companion object {

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,7 @@ class ElementBuffer(mode: VertexMode) : IBuffer {
3535
override fun upload(
3636
data: ByteBuffer,
3737
offset: Long,
38-
): Throwable? {
39-
// Bind the buffer
40-
bind()
41-
42-
// Map the buffer into the client's memory
43-
val error = map(offset, data.limit().toLong(), data::putTo)
44-
45-
// Unbind
46-
bind(0)
47-
48-
return error
49-
}
38+
): Throwable? = allocate(data)
5039

5140
override fun bind(id: Int) {
5241
if (id != 0) prevIbo = lastIbo
@@ -56,8 +45,7 @@ class ElementBuffer(mode: VertexMode) : IBuffer {
5645
}
5746

5847
init {
59-
// Fill the buffer with null data
60-
grow(mode.indicesCount * 2L.kibibyte)
48+
allocate(mode.indicesCount * 2L.kibibyte)
6149
}
6250

6351
companion object {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class VertexArray : IBuffer {
4141
offset: Long,
4242
): Throwable? = throw UnsupportedOperationException("Data cannot be uploaded to a vertex array object")
4343

44-
override fun grow(size: Long) = throw UnsupportedOperationException("Cannot grow a vertex array object")
44+
override fun allocate(size: Long) = throw UnsupportedOperationException("Cannot grow a vertex array object")
4545

4646
override fun bind(id: Int) {
4747
glBindVertexArray(id); BufferRenderer.currentVertexBuffer = null

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

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,19 @@ class VertexBuffer(
2929
mode: VertexMode,
3030
attributes: VertexAttrib.Group,
3131
) : IBuffer {
32-
override val buffers: Int = 2
32+
override val buffers: Int = 1
3333
override val usage: Int = GL_DYNAMIC_DRAW
3434
override val target: Int = GL_ARRAY_BUFFER
3535
override val access: Int = GL_MAP_WRITE_BIT
3636
override var index = 0
37-
override val bufferIds = IntArray(buffers).apply { glGenBuffers(this) }
37+
override val bufferIds = intArrayOf(glGenBuffers())
3838

39-
override fun upload(data: ByteBuffer, offset: Long): Throwable? {
40-
// Bind the buffer
41-
bind()
42-
43-
// Update the buffer data
44-
val error = map(offset, data.limit().toLong(), data::putTo)
45-
46-
// We need to swap the index because our memory mapping requires
47-
// synchronization between the GPU and CPU
48-
// The GL_MAP_COHERENT bit tells OpenGL to synchronize the transfer
49-
// to the buffer
50-
swap()
51-
52-
// Unbind
53-
bind(0)
54-
55-
return error
56-
}
39+
override fun upload(
40+
data: ByteBuffer,
41+
offset: Long,
42+
): Throwable? = allocate(data)
5743

5844
init {
59-
// Fill the buffer with null data
60-
grow(attributes.stride * mode.indicesCount * 1L.kibibyte)
45+
allocate(attributes.stride * mode.indicesCount * 1L.kibibyte)
6146
}
6247
}

common/src/main/kotlin/com/lambda/util/extension/Other.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,25 @@
1717

1818
package com.lambda.util.extension
1919

20+
import kotlin.contracts.ExperimentalContracts
21+
import kotlin.contracts.InvocationKind
22+
import kotlin.contracts.contract
23+
24+
/**
25+
* Executes the given block only if the object receiver is null
26+
* Opposite of `Any?.let {}`
27+
*/
28+
@OptIn(ExperimentalContracts::class)
29+
inline fun <T> T?.ifNull(block: () -> Unit): T? {
30+
contract {
31+
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
32+
}
33+
34+
if (this == null) block()
35+
36+
return this
37+
}
38+
2039
val Class<*>.isObject: Boolean
2140
get() = declaredFields.any { it.name == "INSTANCE" }
2241

common/src/main/kotlin/com/lambda/util/math/MathUtils.kt

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,7 @@ object MathUtils {
8080
return nextDouble(min, max)
8181
}
8282

83-
/**
84-
* @return The smallest power of two that is greater than or equal to the input integer.
85-
*/
86-
fun Int.ceilToPOT(): Int {
87-
var i = this
88-
i--
89-
i = i or (i shr 1)
90-
i = i or (i shr 2)
91-
i = i or (i shr 4)
92-
i = i or (i shr 8)
93-
i = i or (i shr 16)
94-
return ++i
95-
}
83+
fun Int.nextPowerOf2() = 2f.pow(ceil(log2(toFloat()))).toInt()
9684

9785
inline val Int.sq: Int get() = this * this
9886
}

0 commit comments

Comments
 (0)