Bug report: Linker error when using bindless buffer handle in uniform buffer

I am currently investigating the possibilities of bindless uniform buffers in our renderer. The GL_NV_shader_buffer_load extension made it possible to upload a uvec2 or uint64_t address handle of a buffer and in GLSL, declare the uniform variable as a pointer that can be used to access the bindless buffer. This also works when storing multiple handles in another buffer. For example:

struct BufferStruct
{
    int* a;
    int* b;
};

uniform restrict readonly BufferStruct* myBuffers;

myBuffers is a shader storage buffer of type GLuint64 (buffer address handles), the translation from address handles to SSBOs a and b is done by OpenGL. This all works like a charm, but:

It does not work with uniform buffer objects. If I change the code to

struct BufferStruct
{
    int* a;
    int* b;
};

layout(std140) uniform TestBlock
{
    BufferStruct myBuffers;
};

I get the linker error (0) : fatal error C9999: unhandled type category 9 in CreateDag. As with my other pending bug report regarding bindless buffers, I find the number of nondescript linker errors disturbing when it comes to this extension.

The thing is, it is no problem to declare

struct TestStruct
{
    layout(bindless_sampler) sampler2D a;
    layout(bindless_sampler) sampler2D b;
};

so bindless textures are not a problem, only bindless buffers. It is also no problem to declare

struct BufferStruct
{
    int* a;
};

so the trouble only starts when using two or more.

Please note that for this bug, no host code is involved. It does not depend on any actual data in the buffers, the shader is not even executed, but fails upon linking.

The system is Windows 8.1 Pro 64 bit, the video card is a GeForce GTX 680, the driver version is 364.51, the requested OpenGL context is 4.5 compatibility profile.

The entire fragment shader code for reproducibility:

#version 450

#extension GL_ARB_bindless_texture : enable
#extension GL_ARB_gpu_shader5 : enable
#extension GL_NV_shader_buffer_load : enable

out vec4 fragment_color;

struct TestStruct
{
	//uint64_t a; // Good
	//uint64_t b; // Good

	//layout(bindless_sampler) sampler2D a; // Good
	//layout(bindless_sampler) sampler2D b; // Good

	//int* a; // Good
	
	int* a; // Nono
	int* b; // Nono
};

layout(std140) uniform TestBlock
{
	TestStruct test;
};

void main()
{
	fragment_color = vec4(1.0);

	if (test.a[0] > 0) // just so it is not removed as optimization
	{
		discard;
	}
}

An interesting observation, although not directly related to the bindless buffer problem: It also does not work with bindless images. However, I do not get any linker error, but the entire struct appears to have default values (i.e. all handles are 0ul):

struct TestStruct
{
    layout(bindless_image, rgba8) restrict image2D a;
    uint64_t b; // will also be 0ul when accessed
};

There seems to be a fundamental shortcoming of uniform buffers with bindless stuff. I don’t know if it is even supposed to work, but in any case, consistent behavior and descriptive error messages would be nice.

My workaround for the time being is to only declare uint64_ts in the structs and then cast on access, i.e.

struct TestStruct
{
    uint64_t a;
    uint64_t b;
};

layout(std140) uniform TestBlock
{
    TestStruct test;
};

void main()
{
    int* a = (int*)(test.a);
    int n = a[0];
}

One extra cast per buffer, not very beautiful, but working …

Hi,
the internal drivers have fixed this problem, not sure when exactly those changes will be made public, but the problem was solved.

That’s great news, thank you! I will keep an eye on the upcoming driver releases!

It seems the fix is already included in driver version 372.54. Thanks, this is a significant step towards a better world!