GLSL icon indicating copy to clipboard operation
GLSL copied to clipboard

Format layout qualifiers on image function parameters?

Open jeffbolznv opened this issue 7 years ago • 17 comments

Implementations are inconsistent about handling format layout qualifiers on image function parameters, and whether they are included in parameter matching. The GLSL spec seems to have a clear statement about this, but it may not be the "right" answer.

Section 4.10 (Memory Qualifiers) includes this example code, claiming that it is "OK" to pass a qualified image to an unqualified parameter:

vec4 funcA(restrict image2D a)   { ... }
vec4 funcB(image2D a)            { ... }
layout(rgba32f) uniform image2D img1;
layout(rgba32f) coherent uniform image2D img2;
funcA(img1);              // OK, adding "restrict" is allowed
funcB(img2);              // illegal, stripping "coherent" is not

The next sentence says "Layout qualifiers cannot be used on formal function parameters, and layout qualification is not included in parameter matching."

That seems pretty clear, but if a function parameter can't be qualified then it can't be loaded from, because loads require a format qualifier (unless GL_EXT_shader_image_load_formatted is enabled). That also seems to require inlining or cloning functions in order to know the right type, which is something we've tried to avoid in SPIR-V. So this is an unexpected/concerning answer.

Consider the following example:

#version 450
layout(local_size_x = 1) in;

layout(binding=0, rgba16f) uniform image2D im;

void f(layout(rgba16f) image2D x)
{
    imageStore(x, ivec2(0,0), vec4(0,0,0,0));
    imageLoad(x, ivec2(0,0));
}

void g(image2D x)
{
    imageStore(x, ivec2(0,0), vec4(0,0,0,0));
}

void main (void)
{
    f(im);
    g(im);
}

glslang generates an error on the definition of f(): "cannot use layout qualifiers on a function parameter". If you remove f(), it successfully compiles the call to g() (passing qualified to nonqualified), but the resulting SPIR-V fails spirv-val "OpFunctionCall Argument '23[%im]'s type does not match Function '8[%_ptr_UniformConstant_7]'s parameter type". If the frontend compiler is supposed to insert a cast, is it supposed to be an OpBitcast? That sounds legal, but is possibly something that has never been tested.

NVIDIA's GLSL compiler generates an error on the call to g() "incompatible type for parameter 1". But it accepts the definition of f and call to f, and generates correct code (AFAICT).

I'm not sure what's the right way to resolve all this. It seems like we should allow format qualifiers on function parameters, so that loads can be supported. And it is fine to allow unqualified function parameters, but I would only expect them to support stores (spec says "it is a compile-time error to pass an image uniform variable or function parameter declared without a format layout qualifier to an image load or atomic function"). If we want to allow passing qualified variables to unqualified parameters, then we either need to relax the type matching rules in SPIR-V or clarify (in GL_KHR_vulkan_glsl?) how to perform the conversion?

jeffbolznv avatar Mar 02 '19 17:03 jeffbolznv

It seems like we should allow format qualifiers on function parameters, so that loads can be supported.

I think that's the right answer, and possibly to warn (or error) at the GLSL level if the argument type does not match the parameter type, including the format.

See https://github.com/KhronosGroup/glslang/issues/1720 for a recent complaint about this.

johnkslang avatar Mar 11 '19 04:03 johnkslang

We discussed on the call today that there was a very old bug (11466) about this issue. My recollection was that the driver was supposed to do whatever specialization of functions was necessary to make it work. Since most functions would be inlined, this would be trivial. It seems like the version of the shader with just g() should just work. SPIR-V makes things a bit more complicated.

ianromanick avatar Mar 13 '19 14:03 ianromanick

On AMD, the example compute shader above compiles with no errors.

NeilMonday avatar Mar 13 '19 18:03 NeilMonday

FWIW, the original issue was reported to me by an internal Vulkan developer. We should definitely answer the question for both GL and Vulkan, and as Ian says it is probably harder with SPIR-V.

jeffbolznv avatar Mar 13 '19 18:03 jeffbolznv

Since we can't bugfix the current GLSL/ESSL specs for this, I think we need an extension so applications that want to use format qualifiers on function parameters know that it's supported by the implementation. If that makes sense we need a volunteer to create the extension.

pdaniell-nv avatar Mar 13 '19 22:03 pdaniell-nv

Glslang is updated now to give an error if formats do not match between calling argument and formal parameter (unless the formal parameter is writeonly and at least one of caller/callee has an unknown format).

johnkslang avatar Apr 19 '19 14:04 johnkslang

in vulkan 1.1.121.2 this code:

layout(binding = 0, r32f) uniform image2D testImage;
float Func(image2D image)
{
  return imageLoad(image, ivec2(0, 0)).r;
}
void main()
{
  float test = Func(testImage);
}

gives this error:

'format' : image formats must match

Why is it happening? Is there a workaround to pass image2D argument to a function?

Raikiri avatar Oct 04 '19 06:10 Raikiri

I saw a whole bunch of changes and discussions around this topic as it keeps popping up for the past 3 years whenever anybody attempts to pass a readwrite texture into a function. Is there any way (even a workaround) to do this as of today?

Raikiri avatar Aug 04 '21 05:08 Raikiri

Why is it happening? Is there a workaround to pass image2D argument to a function?

There is no way, it's mostly hardware limitation.

How is this sort of thing a hardware limitation, when - at least my understanding is - everything is basically inlined before being executed on the hardware, and if you manually inline this, it is valid?

realazthat avatar Dec 24 '21 19:12 realazthat

I changed my mind about that question. I suppose that is language and/or compiler problem. At least, for Vulkan API, due SPIR-V. About OpenGL or OpenGL ES, I currently have no idea...

ghost avatar Mar 18 '22 14:03 ghost

Has there been any progress on this? I'm running into this exact issue today.

spencerkohan avatar Jun 19 '22 10:06 spencerkohan

Was a resolution reached for this? It's been four years and being able to pass a storage image to a function would make my code so much better

DethRaid avatar Feb 23 '23 00:02 DethRaid

As suggested in the original post, one horrible solution is to use GL_EXT_shader_image_load_formatted, then run your script through glslangValidator to convert it to SPV, then run spirv-opt with --inline-entry-points-exhaustive, which inlines everything. Then either load the SPV in vulkan or opengl (if implementation supports it) or, alternatively, use spirv-cross to convert it back to glsl, and then load it as normal. I've gotten most of such a pipeline working, though I can't say it will work for everyone or on all hardware. I also was simultaneously doing other horrible hacks, so take this all with a grain of salt until you try it and it works in all contexts.

realazthat avatar Feb 23 '23 04:02 realazthat

Just adding my +1 here. This seems like a very serious functionality gap in the language.

StilesCrisis avatar Sep 01 '23 13:09 StilesCrisis

+1

M-Gjerde avatar Nov 13 '23 08:11 M-Gjerde

Adding +1, it's been five years and it's still not possible to pass images around to functions.

graphitemaster avatar Apr 01 '24 19:04 graphitemaster