6

My question is similar to this one but part of the (useful) given answer isn't compatible with compiling GLSL for vulkan based on OpenGL ES ESSL 3.10.

In order to use a separate section of the push constant memory in the vertex shader and the fragment shader, the suggested solution is to use layout(offset = #) before the first member of the push constant structure.

Attempting to do this in GLSL ES 310 code leads to the error "'offset on block member': not supported with this profile: es".

Is there a supported way to declare such an offset that is compatible with es?

The only workaround I've found is to declare a bunch of dummy variables in the fragment shader. When I do so, I get validation layer errors if I don't declare the full range of the fragment shader's push constant buffer in VkPipelineLayoutCreateInfo. After fixing that, I get validation layer warnings about "vkCreatePipelineLayout() call has push constants with overlapping ranges".

Obviously I can ignore warnings, but if there's a tidier solution, then that would be much more preferable.

Simple example, this compiles successfully with VulkanSDK\1.0.13.0\Bin\glslangValidator.exe:

#version 430
#extension GL_ARB_enhanced_layouts: enable

layout(std140, push_constant) uniform PushConstants
{
        layout(offset=64) mat4 matWorldViewProj;
} ubuf;

layout(location = 0) in vec4 i_Position;

void main() {
    gl_Position = ubuf.matWorldViewProj * i_Position;
}

Whereas this does not:

#version 310 es
#extension GL_ARB_enhanced_layouts: enable

layout(std140, push_constant) uniform PushConstants
{
        layout(offset=64) mat4 matWorldViewProj;
} ubuf;

layout(location = 0) in vec4 i_Position;

void main() {
    gl_Position = ubuf.matWorldViewProj * i_Position;
}

Converting all my 310 ES shader code to 430 would solve my problem, but that wouldn't be ideal. GL_ARB_enhanced_layouts doesn't apply to 310 ES code, so my question is not about why it doesn't work, but rather, do I have any options in ES to achieve the same goal?

Community
  • 1
  • 1
Columbo
  • 6,648
  • 4
  • 19
  • 30
  • "*Converting all my 310 ES shader code to 430 would solve my problem, but that wouldn't be ideal.*" Your shaders are written to be consumed by *Vulkan*, not OpenGL ES. So why is it not "ideal" that you have to make changes to them in order for it to work? Why are you attached to your shaders using ES version 3.10? – Nicol Bolas Jun 17 '16 at 17:33
  • As this question continues to get occasional views and upvotes, I thought I'd follow up. I'm targeting mobile, including OpenGLES2 and Metal devices, and wanted to use SPIR-V cross to cross-compile a single shader set to all my target platforms. I stuck with 310 ES+GL_KHR_vulkan_glsl, and hacked glslang and SPIRV-Cross to allow both layout offsets (for Vulkan) and lowp (for GLES2). Glslang and SPIRV-Cross are both open source and were relatively easy to modify to meet my needs - YMMV. – Columbo Jul 04 '17 at 06:36

2 Answers2

3

I would consider this an error in the GLSL compiler.

What's happening is this. There are some things which compiling GLSL for Vulkan adds to the language, as defined by KHR_vulkan_glsl. The push_constant layout, for example, is explicitly added to GLSL syntax.

However, there are certain things which it does not add to the language. Important to your use case is the ability to apply offsets to members of uniform blocks. Oh yes, KHR_vulkan_glsl uses that information when building the shader's block layout. But the grammar that allows you to say layout(offset=#) is defined by GLSL, not by KHR_vulkan_glsl.

And that grammar is not a part of any version of GLSL-ES. Nor is it provided by any ES extension I am aware of. So you can't use it.

I would say that the reference compiler should, when compiling a shader for Vulkan, either fail to compile any GLSL-ES-based version, or silently ignore any version and extension declarations, and just assume desktop GLSL 4.50.

As for what you can do about it... nothing. Short of hacking that solution into the compiler yourself, your primary solution is to write your code against versions of desktop OpenGL. Like 4.50.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I might be misunderstanding your answer, but you seem to be saying that it's only a bug that GLSL-ES 310 is allowed at all by the reference compiler. However, GL_KHR_vulkan_glsl does specify that it can be applied to GLSL-ES 310 (in the dependencies section). Without any way of achieving layout offset though, GLSL-ES 310 does seem to be something of a second class citizen, so I will probably follow your advice and switch to a desktop version. – Columbo Jun 18 '16 at 17:15
  • @Columbo: I would consider that a bug in the "specification". VKSL should be one language, some sort of pseudo-extension that may or may not support all of its features. – Nicol Bolas Jun 18 '16 at 17:19
  • Yes, maybe it was a mis-step for GL_KHR_vulkan_glsl to try to support too wide a set of GLSL versions. Certainly for me it would have been better if GL_KHR_vulkan_glsl had never supported GLSL-ES. Then I wouldn't have spent time getting a lot of shaders working in one dialect before discovering I'd backed the wrong horse. Still, probably not too much effort to rework them. To any people trying to decide which language to base their vulkan shader code, I'd recommend picking desktop GLSL. – Columbo Jun 18 '16 at 19:32
2

If you compile SPIR-V for Vulkan there is a "VULKAN" define set in your shaders (see GL_KHR_VULKAN_glsl), so you could do something like this:

#ifdef VULKAN
    layout(push_constant) uniform pushConstants {
        vec4 (offset = 12) pos;
    } pushConstBlock;
#else
    // GLES stuff
#endif
Sascha Willems
  • 5,280
  • 1
  • 14
  • 21
  • Thanks for the answer. I'm not trying to compile the shader for GLES, I'm trying to compile the shader for vulkan using the 310 ES+GL_KHR_vulkan_glsl dialect. I have added a couple of examples to my question to hopefully make the problem clearer. What I'm trying to do works with 430+GL_KHR_vulkan_glsl, but not with 310 ES+GL_KHR_vulkan_glsl. – Columbo Jun 17 '16 at 16:02