glGetQueryObjectuiv, "Bound query buffer is not large enough to store result."

484 Views Asked by At

I am trying to solve an error I get when I run this sample.

It regards query occlusion, essentially it renders four times a square changing everytime viewport but only the central two times it will actually render something since the first and the last viewport are outside the monitor area on purpose.

    viewports[0] = new Vec4(windowSize.x * -0.5f, windowSize.y * -0.5f, windowSize.x * 0.5f, windowSize.y * 0.5f);
    viewports[1] = new Vec4(0, 0, windowSize.x * 0.5f, windowSize.y * 0.5f);
    viewports[2] = new Vec4(windowSize.x * 0.5f, windowSize.y * 0.5f, windowSize.x * 0.5f, windowSize.y * 0.5f);
    viewports[3] = new Vec4(windowSize.x * 1.0f, windowSize.y * 1.0f, windowSize.x * 0.5f, windowSize.y * 0.5f);

Each of this time, it will glBeginQuery with a different query and render a first time and then I query GL_ANY_SAMPLES_PASSED

    // Samples count query
    for (int i = 0; i < viewports.length; ++i) {

        gl4.glViewportArrayv(0, 1, viewports[i].toFA_(), 0);

        gl4.glBeginQuery(GL_ANY_SAMPLES_PASSED, queryName.get(i));
        {
            gl4.glDrawArraysInstanced(GL_TRIANGLES, 0, vertexCount, 1);
        }
        gl4.glEndQuery(GL_ANY_SAMPLES_PASSED);
    }

Then I try to read the result

    gl4.glBindBuffer(GL_QUERY_BUFFER, bufferName.get(Buffer.QUERY));
    IntBuffer params = GLBuffers.newDirectIntBuffer(1);
    for (int i = 0; i < viewports.length; ++i) {
        params.put(0, i);
        gl4.glGetQueryObjectuiv(queryName.get(i), GL_QUERY_RESULT, params);
    }

But I get:

GlDebugOutput.messageSent(): GLDebugEvent[ id 0x502
    type Error
    severity High: dangerous undefined behavior
    source GL API
    msg GL_INVALID_OPERATION error generated. Bound query buffer is not large enough to store result.
    when 1455696348371
    source 4.5 (Core profile, arb, debug, compat[ES2, ES3, ES31, ES32], FBO, hardware) - 4.5.0 NVIDIA 356.39 - hash 0x238337ea]

If I look on the api doc they say:

params

    If a buffer is bound to the GL_QUERY_RESULT_BUFFER target, then params is treated as an offset to a location within that buffer's data store to receive the result of the query. If no buffer is bound to GL_QUERY_RESULT_BUFFER, then params is treated as an address in client memory of a variable to receive the resulting data. 

I guess there is an error in that phrase, I think they meant GL_QUERY_BUFFER instead of GL_QUERY_RESULT_BUFFER, indeed they use GL_QUERY_BUFFER also here for example.. Anyway, if anything is bound there, then params is interpreted as offset, ok

but my buffer is big enough:

    gl4.glBindBuffer(GL_QUERY_BUFFER, bufferName.get(Buffer.QUERY));
    gl4.glBufferData(GL_QUERY_BUFFER, Integer.BYTES * queryName.capacity(), null, GL_DYNAMIC_COPY);
    gl4.glBindBuffer(GL_QUERY_BUFFER, 0);

So what's the problem?

I tried to write a big number, such as 500, for the buffer size, but no success...

I guess the error lies somewhere else.. could you see it?

1

There are 1 best solutions below

1
On BEST ANSWER

if I have to answer, I say I expect that if I bind a buffer to GL_QUERY_BUFFER target, then OpenGL should read the value inside params and interpreter that as the offset (in bytes) where it should save the result of the query to.

No, that's not how it works.

In C/C++, the value taken by glGetQueryObject is a pointer, which normally is a pointer to a client memory buffer. For this particular function, this would often be a stack variable:

GLuint val;
glGetQueryObjectuiv(obj, GL_QUERY_RESULT, &val);

val is declared by client code (ie: the code calling into OpenGL). This code passes a pointer to that variable, and glGetQueryObjectuiv will write data to this pointer.

This is emulated in C# bindings by using *Buffer types. These represent contiguous arrays of values from which C# can extract a pointer that is compatible with C and C++ pointers-to-arrays.

However, when a buffer is bound to GL_QUERY_BUFFER, the meaning of the parameter changes. As you noted, it goes from being a client pointer to memory into an offset. But please note what that says. It does not say a "client pointer to an offset".

That is, the pointer value itself ceases being a pointer to actual memory. Instead, the numerical value of the pointer is treated as an offset.

In C++ terms, that's this:

glBindBuffer(GL_QUERY_BUFFER, buff);
glGetQueryObjectuiv(obj, GL_QUERY_RESULT, reinterpret_cast<void*>(16));

Note how it takes the offset of 16 bytes and pretends that this value is actually a void* who's numerical value is 16. That's what the reinterpret cast does.

How do you do that in C#? I have no idea; it would depend on the binding you're using, and you never specified what that was. Tao's long-since dead, and OpenTK looks to be heading that way too. But I did find out how to do this in OpenTK.

What you need to do is this:

gl4.glBindBuffer(GL_QUERY_BUFFER, bufferName.get(Buffer.QUERY));
for (int i = 0; i < viewports.length; ++i)
{
    gl4.glGetQueryObjectuiv(queryName.get(i), GL_QUERY_RESULT,
        (IntPtr)(i * Integer.BYTES));
}

You multiply times Integer.BYTES because the value is a byte offset into the buffer, not the integer index into an array of ints.