HolyBlackCat
HolyBlackCat

Reputation: 96136

Is it ok to declare an oversized output array in fragment shader and leave some indices unused?

Some context:

I'm looking for a simple way to run 'old' shaders (let's say #version 120) on a 'new' GLSL (let's say #version 150 core).

So far I came up with adding following header to my fragment shaders when targeting #version 150:

#version 150 core
#define texture2D texture
out vec4 _gl_FragData[gl_MaxDrawBuffers];
#define gl_FragData _gl_Fragdata
#define gl_FragColor gl_Fragdata[0]

Question:

I'm a bit concenrned about this line:

// In a fragment shader
out vec4 _gl_FragData[gl_MaxDrawBuffers];

With most shaders, _gl_FragData[1 ... gl_MaxDrawBuffers-1] will be unused, i.e. not written by the shader, and lack framebuffer attachements to receive the values. I'm worried if presence of those unused elements makes my shaders ill-formed (or has any undesirable effects or overhead).

GLSL 1.50 specification says that there is an implicitly defined deprecated out vec4 gl_FragData[gl_MaxDrawBuffers];, so presumably what I'm doing is fine and incurs no overhead.
But I also know that builtin gl_FragData is in some ways 'magical', and a similar user-provided declaration might have a different effect.

TL;DR:

If my fragment shader output is an array, am I allowed to not write to some of the elements at the end of the array, given that the values of those elements are discarded (not received by any framebuffer attachment point)?

A bit too-broad-ish, but: Does the presence of those unused indices have any undersirable effects or overhead on common implementations?

Upvotes: 3

Views: 584

Answers (1)

derhass
derhass

Reputation: 45332

If my fragment shader output is an array, am I allowed to not write to some of the elements at the end of the array, given that the values of those elements are discarded (not received by any framebuffer attachment point)?

You are always allowed to not to write to any output variables, no matter if they are used or not.

Since you use #version 150, I'm referring to the GLSL 1.50 spec. Section 4.3.6 "Outputs" states (emphasis mine):

Output variables must be declared at global scope. During shader execution they will behave as normal unqualified global variables. Their values are copied out to the subsequent pipeline stage on shader exit.

If you declare a variable and never write to it, you will have an undefined value, but not undefined behavior. This is also backed up by the OpenGL 3.2 core profile specification, Section 3.9 "Fragment Shaders", Subsection "Shader Outputs"

Any colors, or color components, associated with a fragment that are not written by the fragment shader are undefined.

More modern versions of the GL spec, e.g. OpenGL 4.6 core profile, are even more clear about this point (Section 17.4 "Whole framebuffer operations"):

If a fragment shader writes to a user-defined output variable, DrawBuffers specifies a set of draw buffers into which each of the multiple output colors defined by these variables are separately written. If a fragment shader writes to no user-defined output variables, the values of the fragment colors following shader execution are undefined, and may differ for each fragment color. If some, but not all user-defined output variables are written, the values of fragment colors corresponding to unwritten variables are similarly undefined.

The GLSL 1.50 spec goes on with:

Only output variables that are read by the subsequent pipeline stage need to be written; it is allowed to have superfluous declarations of output variables.

It is perfectly fine to not write to output variables which aren't going to be used anyway.

Your second question is a bit more tricky:

Does the presence of those unused indices have any undersirable effects or overhead on common implementations?

The GL spec never makes any such guarantees. I can only say two things:

  1. I have never noticed any negative effects of having some additional unused FS outputs (but I also didn't do that very often). I also would not expect a reasonable implementation to introduce noticeable overhead in such a case.
  2. If you really need to be sure, you have to benchmark/profile on the actual implementations you care about.

Upvotes: 3

Related Questions