Description of the bug

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.

The solution and why this happen

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.

Screen shot

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)

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/300d0369-effd-4c68-b4a8-4b03ed681f74/Screenshotfrom2018-10-0217-20-51.png

Source code

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.