PDA

View Full Version : Buffer sanity checks with NV_shader_buffer_load



nostalgic
07-31-2015, 05:21 AM
Hello guys, I am about to switch some of our application's code to use bindless graphics to reduce all the binding overhead, which really is a lot in our case. The NV_shader_buffer_load (https://www.opengl.org/registry/specs/NV/shader_buffer_load.txt) extension provides almost everything we need, but I miss a few things here. For example, we do have quite generic shaders for instanced mesh rendering. Some of the meshes have textures, some don't, which currently is distinguished by the shader storage buffer having .length() > 0 or not. This way, we can either bind a filled texture handle buffer or an empty one and use the same shader. This does not seem possible with the bindless buffers in GLSL. I could not find any information on how to check 1) if the buffer is resident, 2) if the buffer address is valid, 3) the length of the buffer.

As a small example:

// Application code
glBindBuffer(GL_SHADER_STORAGE_BUFFER, myBufferId);
glMakeBufferResidentNV(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
glGetBufferParameterui64vNV(GL_SHADER_STORAGE_BUFF ER, GL_BUFFER_GPU_ADDRESS_NV, &myBufferHandle);
glProgramUniform1ui64NV(myProgramId, glGetUniformLocation(myProgramId, "myBuffer"), myBufferHandle);
glProgramUniform1ui64NV(myProgramId, glGetUniformLocation(myProgramId, "nullptr"), (GLuint64) 0);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);


// Shader code
#version 450
#extension GL_NV_gpu_shader5 : enable
#extension GL_NV_shader_buffer_load : enable

uniform restrict readonly int* myBuffer;
uniform restrict readonly int* nullptr;

void main()
{
if (!myBuffer) // error C1020: invalid operands to "!"
// something

if (myBuffer == (void*) 0) // error C1020: invalid operands to "=="
// something

if (myBuffer == nullptr) // error C1020: invalid operands to "=="
// something

if (myBuffer.length() > 0) // error C1010: expression left of ."length" is not a struct or array; use -> instead
// something

if (myBuffer->length() > 0) // error C1105: cannot call a non-function
// something
}

It seems the equality operator is not even defined for the pointer I get when setting the buffer handle uniform. So, no null pointer checks, not even with an additional dummy buffer which I set to 0. I also cannot get the length of the buffer as it is possible with shader storage buffers. I could provide additional uniforms for each buffer specifying whether the buffer is resident and valid and its size, but this feels like a significant step backwards. If you look at the bindless textures for example, you can provide the uint64_t texture handle (as handle, not the data type pointer), check != 0, then create a sampler via sampler2D(uint64_t) and use that to call textureSize. With bindless buffers, this does not work because - as far as I can tell - you cannot convert a uint64_t buffer handle with something like buffer(uint64_t) or restrict readonly int* buffer = some_uint64_t_handle;.

I just want to check whether I access a valid buffer in the valid range and, if this is not the case, fetch a default value instead. Right now, when I try to access an unset buffer, the application terminates, and it seems like I have no way of preventing this with bindless buffers. Unless I use additional uniforms for buffer length and validity, in which case I will have even more host-device communication than I had with regularly bound buffers. Do you have any recommendations what I could do to make working with bindless buffers a bit more convenient other than "Just make sure you always have a valid buffer handle"?

Regards,
nostalgic

Alfonse Reinheart
07-31-2015, 06:47 AM
I also cannot get the length of the buffer as it is possible with shader storage buffers.

Of course not. You don't have "buffers"; you have raw memory addresses. You can no more take the length of a raw pointer in bindless GLSL than you can in C++.


I could provide additional uniforms for each buffer specifying whether the buffer is resident and valid and its size, but this feels like a significant step backwards.

You're not talking about adding a buffer binding; you're just changing uniform state. Which you're already doing. So providing another uniform (the size. A size of 0 would mean invalid) seems to me to be a reasonable solution to the problem.

Alternatively, simply provide a pointer that is always valid. For example:



struct data
{
int length;
int *ptr;
};

uniform restrict readonly data *my_data;


You should always pass valid memory to `my_data`. If the data is invalid, `my_data.length` would be zero.

nostalgic
07-31-2015, 08:32 AM
Of course not. You don't have "buffers"; you have raw memory addresses. You can no more take the length of a raw pointer in bindless GLSL than you can in C++.

Thank you for your reply! In hindsight, this was a stupid request. Of course this cannot reasonably work like that.


You're not talking about adding a buffer binding; you're just changing uniform state. Which you're already doing. So providing another uniform (the size. A size of 0 would mean invalid) seems to me to be a reasonable solution to the problem.

Alternatively, simply provide a pointer that is always valid.

This is what I have implemented now and it is working fine. I think the main reason for my discomfort was that the address alone does not specify all the information I need and that I manually have to add something. But then, this is quite flexible and for most applications, buffers are guaranteed to be valid and have a known size.