When gl.bufferData receives ByteBuffer which was converted from FloatBuffer, the buffer seems to be filled with 0 in GPU side. But even it can be read correctly with gl.glGetBufferSubData
I've created 2 buffers which was created in different way. 1 of that is created by FloatBuffer.wrap
, and another one is created by ByteBuffer.allocate
then fill that with FloatBuffer#put
I thought even I had passed ByteBuffer into bufferData method, it should be correctly passed since that should perform just passing byte array into GPU side memory. And there is one more weird thing. If that buffer was read with gl.glGetBufferSubData
, it seems read the buffer correctly even that was not passed into GPU actually.
I found a solution for this problem. I conclude this problem was happen by difference of byte order between java and native environment.
The floating buffer created as a byte buffer must be ordered with native order with following buffer creation code.
val bufferSource2 = ByteBuffer.allocate(4 * 6)
bufferSource2.order(ByteOrder.nativeOrder())
val floatingSource2 = bufferSource2.asFloatBuffer()
floatingSource2.put(FloatBuffer.wrap(floatArrayOf(0.5f,0.5f,0.5f,0.5f,-0.5f,0.5f)))
val buffer2 = gl.createBuffer()
gl.glBindBuffer(GL3.GL_ARRAY_BUFFER,buffer2)
gl.glBufferData(GL3.GL_ARRAY_BUFFER,bufferSource2.limit().toLong(),bufferSource2,GL3.GL_STATIC_DRAW)
I think JOGL will consider the order of passed buffer if it was passed as typed buffers. If the floating values are represented as LITTLE_ENDIAN and it should be swapped for native environment, this seems to be converted as BIG_ENDIAN automatically during calling bufferData method. But, if I passed as a byte buffer, it is obviously the library will not be able to know which is floating value and should be swapped. This was a reason this problem was happen.
Therefore, this was not a problem of JOGL actually.
The 2 blue dot on the right side is the buffer which was created with FloatBuffer.wrap
The 1 red dot on the center is the buffer which was created with ByteBuffer.allocate
. These points are placed in clip space coordinate. Therefore, red dot seems placed in origin of the clip space.(This is the reason I think the red buffer is filled with zero in GPU side memory)
The main source code.
package img.kakeru
import com.jogamp.opengl.GL3
import com.jogamp.opengl.GLAutoDrawable
import img.kakeru.graphics.DynamicSimpleShaderProgram
import img.kakeru.graphics.glex.createBuffer
import java.nio.ByteBuffer
import java.nio.FloatBuffer
import kotlin.coroutines.experimental.buildIterator
class JOGLBug : GLRunnable() {
override fun getSchedule(p0: GLAutoDrawable): Iterator<Int> {
val gl = p0.gl.gL3
val window = this;
// BEGINING OF INITIALIZE
// Disable several flags
gl.glDisable(GL3.GL_BLEND)
gl.glDisable(GL3.GL_CULL_FACE)
gl.glDisable(GL3.GL_SCISSOR_TEST)
gl.glDisable(GL3.GL_DEPTH_TEST)
gl.glDisable(GL3.GL_STENCIL_TEST)
gl.glEnable(GL3.GL_VERTEX_PROGRAM_POINT_SIZE)
// create buffers
// BUFFER1: Correctly shown buffer (Created with FloatBuffer.wrap)
val bufferSource1 = FloatBuffer.wrap(floatArrayOf(-0.5f,0.5f,0.5f,-0.5f,-0.5f,0.5f))
val buffer1 = gl.createBuffer()
gl.glBindBuffer(GL3.GL_ARRAY_BUFFER,buffer1)
gl.glBufferData(GL3.GL_ARRAY_BUFFER,bufferSource1.limit() * 4L, bufferSource1,GL3.GL_STATIC_DRAW)
// BUFFER2: Incorrectly shown buffer (Created with Bytebuffer.allocate then it will be filled with put method of floatbuffer)
val bufferSource2 = ByteBuffer.allocate(4 * 6);
bufferSource2.asFloatBuffer().put(floatArrayOf(0.5f,0.5f,0.5f,0.5f,-0.5f,0.5f))
val buffer2 = gl.createBuffer()
gl.glBindBuffer(GL3.GL_ARRAY_BUFFER,buffer2)
gl.glBufferData(GL3.GL_ARRAY_BUFFER,bufferSource2.limit().toLong(),bufferSource2,GL3.GL_STATIC_DRAW)
// create materials
val materialRed = DynamicSimpleShaderProgram("./kakeru/position-only-es2.vp","./kakeru/single-color-es2.fp")
val materialBlue = DynamicSimpleShaderProgram("./kakeru/position-only-es2.vp","./kakeru/single-color-es2-blue.fp")
materialRed.setup(gl)
materialBlue.setup(gl)
// END OF INITIALIZE
return buildIterator {
while(true){
// BEGINNING OF A FRAME
// RENDERING LOOP
gl.glViewport(0,0,window.width,window.height)
gl.glClearColor(1f,1f,1f,1f)
gl.glClear(GL3.GL_COLOR_BUFFER_BIT or GL3.GL_DEPTH_BUFFER_BIT)
// draw buffer1
materialBlue.use(mapOf())
gl.glBindBuffer(GL3.GL_ARRAY_BUFFER,buffer1)
val posLoc1 = gl.glGetAttribLocation(materialBlue.program.id,"pos")
gl.glEnableVertexAttribArray(posLoc1)
gl.glVertexAttribPointer(posLoc1,3,GL3.GL_FLOAT,false,0,0L)
gl.glDrawArrays(GL3.GL_POINTS,0,2)
// draw buffer2
materialRed.use(mapOf())
gl.glBindBuffer(GL3.GL_ARRAY_BUFFER,buffer2)
val posLoc2 = gl.glGetAttribLocation(materialBlue.program.id,"pos")
gl.glEnableVertexAttribArray(posLoc2)
gl.glVertexAttribPointer(posLoc2,3,GL3.GL_FLOAT,false,0,0L)
gl.glDrawArrays(GL3.GL_POINTS,0,2)
// Try to get Buffer2 contained elements
val subBuffer = ByteBuffer.allocateDirect(6 * 4).asFloatBuffer()
gl.glGetBufferSubData(GL3.GL_ARRAY_BUFFER,0,6 * 4,subBuffer)
// Stringify bufferData
var bufferStr = "["
for(i in 0 until 6){
bufferStr += (subBuffer[i]);
bufferStr += " ,";
}
bufferStr += "];"
println(bufferStr)
// Finalize the frame
gl.glFinish()
// END OF A FRAME
yield(0)
}
}
}
}
fun main(args:Array<String>){
val testInstance = TestExecutor(JOGLBug())
testInstance.start()
}
The following description is supplemental information for reading the code above. But, the basically there is no way to need omitted source codes.
DynamicSimpleShaderProgram
is just a class to provide feature for compiling shader, using program and so on. Even I haven't attach source code of this class, this class is just perform that should do. There are no related code about buffers.GLRunnable
which is inherited by the main class of this source code. GLRunnable will call the getSchedule
method on initialization timing when the window was created. Then it will call next() method of returned iterator for each frame.