11package com.lambda.graphics.buffer.pbo
22
3+ import com.lambda.Lambda.LOG
34import com.lambda.graphics.buffer.BufferUsage
5+ import org.lwjgl.opengl.GL
46import org.lwjgl.opengl.GL45C.*
5- import org.lwjgl.opengl.GREMEDYStringMarker
67import java.nio.ByteBuffer
78
89class PixelBuffer (
@@ -17,24 +18,51 @@ class PixelBuffer(
1718
1819 private val queryId = glGenQueries() // Used to measure the time taken to upload data to the PBO
1920 val uploadTime get() = IntArray (1 ).also { glGetQueryObjectiv(queryId, GL_QUERY_RESULT , it) }[0 ]
21+ var transferRate = 0L // The transfer rate in bytes per second
22+ private set
23+
24+ var pboSupported = false
25+ private set
2026
2127 fun mapTexture (id : Int , buffer : ByteBuffer ) =
2228 upload(buffer) {
2329 // Bind the texture
2430 glBindTexture(GL_TEXTURE_2D , id)
2531
26- // Perform the actual data transfer to the GPU
27- glTextureSubImage2D(GL_TEXTURE_2D , 0 , 0 , 0 , width, height, GL_RGBA , GL_UNSIGNED_BYTE , 0 )
32+ if (buffers > 0 && pboSupported) {
33+ // Bind the next PBO to update pixel values
34+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER , pboIds[writeIdx])
35+
36+ // Perform the actual data transfer to the GPU
37+ glTexSubImage2D(GL_TEXTURE_2D , 0 , 0 , 0 , width, height, GL_RGBA , GL_UNSIGNED_BYTE , 0 )
38+ }
39+ else {
40+ // Perform the actual data transfer to the GPU
41+ glTexSubImage2D(GL_TEXTURE_2D , 0 , 0 , 0 , width, height, GL_RGBA , GL_UNSIGNED_BYTE , buffer)
42+ }
2843 }
2944
3045 fun upload (data : ByteBuffer , process : () -> Unit ) =
3146 recordTransfer {
32- uploadIdx = (writeIdx + 1 ) % buffers
47+ if (buffers >= 2 )
48+ uploadIdx = (writeIdx + 1 ) % buffers
3349
34- GREMEDYStringMarker .glStringMarkerGREMEDY(" Data transfer to buffer" )
50+ // Copy the pixel values from the PBO to the texture
51+ process()
3552
36- // Bind the next PBO to update pixel values
37- glBindBuffer(GL_PIXEL_UNPACK_BUFFER , pboIds[writeIdx])
53+ if (! pboSupported) return @recordTransfer
54+
55+ // Bind the current PBO for writing
56+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER , pboIds[uploadIdx])
57+
58+ // Note that glMapBuffer() causes sync issue.
59+ // If GPU is working with this buffer, glMapBuffer() will wait(stall)
60+ // until GPU to finish its job. To avoid waiting (idle), you can call
61+ // first glBufferData() with NULL pointer before glMapBuffer().
62+ // If you do that, the previous data in PBO will be discarded and
63+ // glMapBuffer() returns a new allocated pointer immediately
64+ // even if GPU is still working with the previous data.
65+ glBufferData(GL_PIXEL_UNPACK_BUFFER , width * height * 4L , bufferUsage.gl)
3866
3967 // Map the buffer into the memory
4068 val bufferData = glMapBuffer(GL_PIXEL_UNPACK_BUFFER , GL_WRITE_ONLY )
@@ -43,17 +71,7 @@ class PixelBuffer(
4371
4472 // Release the buffer
4573 glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER )
46- } else {
47- println (" Failed to map the PBO" )
48- }
49-
50- // Bind the current PBO for writing
51- glBindBuffer(GL_PIXEL_UNPACK_BUFFER , pboIds[uploadIdx])
52-
53- GREMEDYStringMarker .glStringMarkerGREMEDY(" Data transfer to GPU" )
54-
55- // Copy the pixel values from the PBO to the texture
56- process()
74+ } else throw IllegalStateException (" Failed to map the buffer" )
5775
5876 // Unbind the PBO
5977 glBindBuffer(GL_PIXEL_UNPACK_BUFFER , 0 )
@@ -63,6 +81,11 @@ class PixelBuffer(
6381 }
6482
6583 private fun recordTransfer (block : () -> Unit ) {
84+ if (! pboSupported) {
85+ block()
86+ return
87+ }
88+
6689 // Start the timer
6790 glBeginQuery(GL_TIME_ELAPSED , queryId)
6891
@@ -71,6 +94,12 @@ class PixelBuffer(
7194
7295 // Stop the timer
7396 glEndQuery(GL_TIME_ELAPSED )
97+
98+ // Calculate the transfer rate
99+ val time = uploadTime
100+ if (time > 0 ) {
101+ transferRate = (width * height * 4L * 1_000_000_000 ) / time
102+ }
74103 }
75104
76105 // Called when no references to the object exist
@@ -80,7 +109,13 @@ class PixelBuffer(
80109 }
81110
82111 init {
83- if (buffers < 1 ) throw IllegalArgumentException (" Buffers must be greater than or equal to 1" )
112+ // Check if the PBO is supported
113+ GL .getCapabilities().let { pboSupported = it.OpenGL30 || it.GL_ARB_pixel_buffer_object }
114+
115+ if (! pboSupported && buffers > 0 )
116+ LOG .warn(" Client tried to utilize PBOs, but they are not supported on the machine, falling back to direct buffer upload" )
117+
118+ if (buffers < 0 ) throw IllegalArgumentException (" Buffers must be greater than or equal to 0" )
84119
85120 // Generate the PBOs
86121 glGenBuffers(pboIds)
0 commit comments