Understanding sampler objects with SPIRV and OpenGL 4.6

I’ve had a bit of a nightmare getting SPIRV shaders running on OpenGL 4.6
That’s probably a story for a different thread.

For now, I’ve progressed to a point where (on Intel at least) my shaders all appear to be executing and rendering correctly.

There is one aspect that I don’t understand, that I’m hoping someone can help illuminate (I’m not well versed in GLSL, this may be obvious to others).

At first, one of my shaders was failing because I had two seperate sampler objects defined at different locations, like so:

layout(binding = 0) uniform sampler2D DiffuseSampler;
layout(binding = 1) uniform sampler2D NormalMapSampler;

It turns out, this isn’t supported. Something about seperable samplers not being supported in SPIRV.
Does that mean I can’t have multiple sampler objects targeting different texture units?

There seem to be only two combinations that result in an operational shader:

A) I could not specify a location at all. As I understand it, the SPIRV compilation process will emit these samplers to the same binding:

    uniform sampler2D DiffuseSampler;
    uniform sampler2D NormalMapSampler;

B) Explicitly define these samplers to the same binding:

    layout(binding = 0) uniform sampler2D DiffuseSampler;
    layout(binding = 0) uniform sampler2D NormalMapSampler;

Either approach should boil down to the same thing as I understand it.

A few initial questions…

  1. Does binding in this case refer to the texture unit, or something else entirely?
  2. Does it follow, that I can only bind to a single texture unit in any given shader?

Rewinding back to the traditional method of loading/compiling/binding shaders at runtime, I could use introspection to determine the location of those sampler uniforms, and then bind them to specific texture units via glUniformiv

That all made sense, I just bind the diffuse texture to GL_TEXTURE0 and the normal map texture to GL_TEXTURE1, tell the sampler objects which one is which (or just hardcode it) and we’re good.

It seems like this isn’t possible with SPIRV shaders.
So, how exactly do I indicate to the sampler2D objects which texture units to sample from?

I think the answer is to use GL_TEXTURE_2D_ARRAY and then I can explicitly reference each layer in shader code - but, is this the correct use case? Is there some other way to do it or have I got things entirely backward?

I would greatly appreciate input from wiser folk.

What do you mean by that? What error message are you getting, and from what are you getting that message?

That’s a difficult one to answer.
On Windows using Intel drivers, the failure status is not accompanied by any error or warning diagnostics whatsoever by glGetProgramInfoLog() and GL_INFO_LOG_LENGTH is 0. Debugging shaders without any kind of diagnostic is… hard.

On AMD drivers, nothing explicitly fails at all - but equally, I can’t get any SPIRV shader to render on AMD drivers. The very same shader source runs just fine when not compiled as SPIRV.

As for the comment about separable samplers. I don’t know if I’m reading it correctly but discussion item 13 in ARB_gl_spirv indicates:

  1. Can we really handle separate textures and samplers?

    DISCUSSION: AMD: No, can’t do this because OpenGL has features that need
    cross validation that can’t be done.

    RESOLVED: Remove separate textures and samplers.

Perhaps it’s refers to some other functionality.
I only went looking for it because I found this which, isn’t exactly the same problem - but it’s seems along the same theme.

It’s referring to sampler objects (glBindSampler etc). In Vulkan, images and samplers are distinct object types, which can be combined either in the client or a shader to form a sampled image (equivalent to a texture in GLSL).

So, a little more testing.
To recap…

GLSL shaders on either AMD or Intel drivers, on both Linux and Windows all compile and run as expected. No problem.

SPIRV shaders on AMD drivers on Windows.
No errors are reported whatsoever, they all load - but not even the most basic shader will render anything. Renderdoc suggests nothing is being passed from the vertex stage to the fragment stage.

SPIRV shaders on Intel on Windows
Aside from the issue my original post was about, these appear to work just fine (though if there is an error, getProgramInfoLog() returns an empty string).


I’ve now tried these same shaders on Linux (same hardware). Here’s what I found:

SPIRV shaders on AMD drivers on Linux.
Most of the time the AMD drivers will seg fault somewhere within glLinkProgram. The stack trace is and the end of this post. I’m chalking this up to be a race condition deep in the driver stack, as stepping through the code and literally just waiting for a few moments between each shader that is loaded will increase the chances of being able to load everything without a seg fault.

In fact… If I can get it to this point, everything renders beautifully!!

Is it at all possible I’m doing something that might trigger the driver to seg-fault?

SPIRV shaders on Intel drivers on Linux.
The shaders appear to do the right thing. In fact, on Linux I am able to specify those sampler objects to different binding locations and it still works. Recall that this was the original problem, I was unable to specify different binding locations with Intel drivers on Windows without glLinkProgram blowing up (as mentioned, without any diagnostic information to speak of)


Conclusions:

  1. Based on the responses so far, I think its safe to say that I should be able to specify different binding locations for sampler objects and that these binding locations do map directly to the texture unit.

  2. Intel drivers on Windows are missing getProgramInfoLog() diagnostics.

  3. AMD drivers on Windows will happily do nothing with anything you throw at it, no errors, no diagnostics, no render.

  4. Neither AMD or Intel drivers (on either Windows or Linux) will report GL_SHADER_BINARY_FORMAT_SPIR_V when querying GL_SHADER_BINARY_FORMATS - though they clearly have some level of support. I’m assuming that this should be a given with an OpenGL 4.6 context (eg: it’s not optional, is it?)

  5. Intel drivers on Windows don’t like me specifying 2 different sampler objects (in a given shader) to two different binding points.

  6. Intel drivers on Linux are completely okay with specifying 2 different sampler objects (in a given shader) to two different binding points.

  7. AMD drivers on Linux seem ok and happy about life (some minor artifacts yet to be understood).

  8. SPIRV on OpenGL is poorly supported across vendors (to be fair, I don’t currently have an nVidia device to test with. At this point, I’m not sure it matters).

Questions
I know others have had problems with SPIRV on OpenGL.

Are the problems I’ve listed above consistent with others peoples experiences?

Do I just have some horrendous memory stomping bug elsewhere that is causing untold amounts of pain in unpredictable ways on various platforms in various places - and which only presents when I’m using GLSL shaders compiled as SPIRV - but not when they’re compiled at runtime by OpenGL? I’m starting to think perhaps this is the case but for the life of me… I’m beginning to feel beaten.


Here’s that stack trace I mentioned:

radeonsi_dri.so!nir_shader_gather_info (Unknown Source:0)
radeonsi_dri.so!st_nir_preprocess(st_context*, gl_program*, gl_shader_program*, gl_shader_stage) [clone .constprop.0] (U
nknown Source:0)
radeonsi_dri.so!st_link_nir (Unknown Source:0)
radeonsi_dri.so!_mesa_glsl_link_shader (Unknown Source:0)
radeonsi_dri.so!link_program_error.part (Unknown Source:0)

Perhaps excuse my ignorance/cynicism… but… is this kind of stuff even worth reporting to the various vendors, or will I end up wishing I never even tried?

If there are channels that are receptive to bug reports, I’ll gladly provide whatever information I can.