gl.bufferData seems not working correctly when floating value was passed as ByteBuffer

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

gl.bufferData seems not working correctly when floating value was passed as ByteBuffer

kyasbal
I couldn't access the bugzilla because of internal server error.  I think I found a bug.
I will copy all of this bug report to this post, but this is originally written with markdown so you can view this bug report in pretty with following URL also.

https://www.notion.so/kyasbal/gl-bufferData-seems-not-working-correctly-when-floating-value-was-passed-as-ByteBuffer-0f68ce5b8ed843eaa9671b0d7acac484

# gl.bufferData seems not working correctly when floating value was passed as ByteBuffer

## 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.

## 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)

SCREEN SHOT IS ATTACHED EXTERNAL LINK THAT WAS PLACED HEAD OF THIS POST.
(Because this forum seems not accepting posting images)

## 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.

- `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.
- `TestExecutor` is a class to run GLRunnable. This basically perform window creation task , and that is all this class do.
- `gl.createBuffer` is extensional method to generate a single buffer. This method just perform `gl.glGenBuffers(1,array,0)`   then it return the first element of the array

position-only-es2.vp

    attribute vec3 pos;
   
    void main(){
       gl_PointSize = 1000000000.;
       gl_Position = vec4(pos,1.0);
    }

single-color-es2.fp

    precision mediump float;
   
    void main(){
       gl_FragColor = vec4(1,0,0,1);
    }

single-color-es2-blue.fp

    precision mediump float;
   
    void main(){
       gl_FragColor = vec4(0,0,1,1);
    }

Output of this program

    [0.5 ,0.5 ,0.5 ,0.5 ,-0.5 ,0.5 ,];
    [0.5 ,0.5 ,0.5 ,0.5 ,-0.5 ,0.5 ,];
    [0.5 ,0.5 ,0.5 ,0.5 ,-0.5 ,0.5 ,];
    [0.5 ,0.5 ,0.5 ,0.5 ,-0.5 ,0.5 ,];
    ....
Reply | Threaded
Open this post in threaded view
|

Re: gl.bufferData seems not working correctly when floating value was passed as ByteBuffer

kyasbal
I conclude this was not bug of JOGL. I apologize I have doubted this library.

## Solution and why this problem was 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.**
Reply | Threaded
Open this post in threaded view
|

Re: gl.bufferData seems not working correctly when floating value was passed as ByteBuffer

gouessej
Administrator
Hello

Why not using Buffers.newDirectFloatBuffer(float[]) instead? It just does the right job.
Julien Gouesse | Personal blog | Website