3

I am having trouble reading out from a buffer mapped with glMapBufferRange

If I simply put some data in a buffer

    // Create input VBO and vertex format
    int bufferLength = 5 * 4; //5 floats 4 bytes each
    FloatBuffer data = ByteBuffer.allocateDirect(bufferLength)
            .order(ByteOrder.nativeOrder()).asFloatBuffer();
    float[] floatData = { 1.0f, 4.0f, 9.0f, 16.0f, 25.0f };
    data.put(floatData).position(0);

Generate a Array Buffer in GL and fill it with the data

    int[] vbo = new int[1];
    GLES30.glGenBuffers(1, vbo, 0);
    GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0]);
    GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, bufferLength, data, GLES30.GL_STATIC_DRAW);
    MyGLRenderer.checkGlError(TAG + " glBufferData GL_ARRAY_BUFFER");

When I map the buffer using glMapBufferRange and read the results

    Buffer mappedBuffer =  GLES30.glMapBufferRange(GLES30 .GL_ARRAY_BUFFER,
            0, bufferLength, GLES30.GL_MAP_READ_BIT);

    if (mappedBuffer!=null){
        FloatBuffer transformedBuffer = ((ByteBuffer) mappedBuffer).asFloatBuffer();
        MyGLRenderer.checkGlError(TAG + " glMapBufferRange");

        Log.d(TAG, String.format("pre-run input values = %f %f %f %f %f\n", transformedBuffer.get(),
                transformedBuffer.get(), transformedBuffer.get(), transformedBuffer.get(), transformedBuffer.get()));
        transformedBuffer.position(0);

    }
    GLES30.glUnmapBuffer(GLES30.GL_ARRAY_BUFFER);

The logcat reads all zeros

D/TransformFeedback﹕ pre-run input values = 0.000000 0.000000 0.000000 0.000000 0.000000

No other errors are generated and I am not really sure what else to check. I have tried GLES30.glCopyBufferSubData(GLES30.GL_ARRAY_BUFFER, GLES30.GL_COPY_READ_BUFFER, 0, 0, bufferLength); and mapping GL_COPY_READ_BUFFER and gotten the same results.

I am testing on a Nexus 6 which supports OpenGL ES 3.1 so this functionality should be present.

This question suggests removing the OPENGL_FORWARD_COMPAT hint in glfw, could there be something similar that needs to happen in MyGLSurfaceView?

Community
  • 1
  • 1
HPP
  • 1,074
  • 1
  • 13
  • 28
  • 1
    Can you check if the values are exactly zero, or if they only look like zero when you print them? For example, log the result of comparing the values `== 0.0f`. I have a theory... – Reto Koradi Jun 29 '15 at 22:40
  • That is correct sir, you are a genius! I am assuming your theory is the buffer needs to be flipped (or set to big/little edian), suggestions on how to do that are welcome, I am trying to figure it out now. `mappedBuffer.flip()` is causing a crash when try to cast to float buffer (underflow). – HPP Jun 29 '15 at 23:33
  • value of first element is 4.6006E-41 – HPP Jun 29 '15 at 23:41
  • Nothing to do with genius, I just happened to have the same problem a few weeks ago. In my case the buffer contained integers. So after some initial puzzlement, it became clear fairly quickly that I was getting byte swapped data. – Reto Koradi Jun 30 '15 at 00:34
  • Ya with floats it is weird because they are all zeros. I thought maybe they were in the wrong order at some point, but didn't follow up and check it out. And with OpenGL there is so much I do without understanding I was searching the wrong place. I seriously want to kiss you, I have been dealing with this for a couple of days and was going to give up and work on other stuff, but if I do that, this functionality won't make it into my app until next cycle, which could be a year or two. – HPP Jun 30 '15 at 00:53

2 Answers2

2

This looks like a byte order problem when you read back the data. The data will be in native byte order, while Java assumes by default that data in buffers is big endian. Since most architectures are little endian these days, that's the opposite of what you need.

To correct for this in a way that will work for both big and little endian architectures, you can do the following:

ByteBuffer byteBuf = (ByteBuffer)mappedBuffer;
byteBuf.order(ByteOrder.nativeOrder());
FloatBuffer floatBuf = byteBuf.asFloatBuffer();

Then when you read from floatBuf, you should get the correct values.

Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
0

Big thanks to Reto Koradi, if anyone wants to leave an answer I will mark it as correct.

The casting was incorrect. Needs to be in LITTLE_ENDIAN order:

ByteBuffer transformedBuffer = ((ByteBuffer) mappedBuffer);
transformedBuffer.order(ByteOrder.LITTLE_ENDIAN);

Log.d(TAG, String.format("pre-run input values = %f %f %f %f %f\n", transformedBuffer.getFloat(),
                transformedBuffer.getFloat(), transformedBuffer.getFloat(),
                transformedBuffer.getFloat(), transformedBuffer.getFloat()));
HPP
  • 1,074
  • 1
  • 13
  • 28
  • 1
    I think it's cleaner to use `nativeOrder()` instead of `LITTLE_ENDIAN`. Then it will work on all architectures. See my answer. – Reto Koradi Jun 30 '15 at 00:32