I have been trying to get a 16bit float (half-floating point) as an attribute into my GLSL vertex shader. It won't let me compile saying:
error C7506: OpenGL does not define the global type half
but my #version is 410 and so it should support half? Am I missing something obvious?
OpenGL and OpenGL ES define two concurrent types of precision.
Storage precision in buffers
Minimum computational precision used in shaders.
Storage precision is defined by your vertex attribute upload, such as GL_FLOAT or GL_HALF_FLOAT. This will be the precision used to store the data in memory.
Usage precision is defined in the shader as highp (at least 32-bit), mediump (at least 16-bit), and lowp (at least 9-bit). These are minimum precisions; it is perfectly legal for a shader to specify a variable as mediump and for the shader compiler to generate fp32 data types. Desktop GPUs tend to only support fp32 computation, so the use of highp, mediump and lowp all map to fp32 data types (the precision qualifiers are only included to keep compatibility with OpenGL ES shaders, and can legally be ignored by the compiler). Mobile GPUs implementing OpenGL ES tend to map highp to fp32, and mediump and lowp to fp16.
Detailed information can be found in the GLSL ES 3.0 Specification, Section 4.5.1
When binding a vertex attribute in memory to an input shader variable the storage and usage precisions are not required to match; the API will include transparent attribute precision conversion. It is perfectly legal for a user to upload e.g. a GL_FLOAT and then use it in a shader as a mediump fp16 variable, although it would be a waste of memory bandwidth to do so.
In the absence of a MCVE demonstrating otherwise I assume you tried something like:
half float aHalfFloat;
However, "half" is a reserved keyword in #version 410:
OpenGL Shading Language 4.10 Specification, page 15 (emphasis mine):
The following are the keywords reserved for future use. Using them
will result in an error:
common partition active asm class union enum typedef template this
packed goto inline noinline volatile public static extern external
interface long short half fixed unsigned superp input output hvec2
hvec3 hvec4 fvec2 fvec3 fvec4 sampler3DRect filter image1D image2D
image3D imageCube iimage1D iimage2D iimage3D iimageCube uimage1D
uimage2D uimage3D uimageCube image1DArray image2DArray iimage1DArray
iimage2DArray uimage1DArray uimage2DArray image1DShadow image2DShadow
image1DArrayShadow image2DArrayShadow imageBuffer iimageBuffer
uimageBuffer sizeof cast namespace using row_major In addition, all
identifiers
Related
I've put my snippet HLSL code here: https://shader-playground.timjones.io/d9011ef7826a68ed93394792c2edb732
I compile HLSL with DXC to SPIR-V and then use SPIRV-Cross to get the GLSL code.
The GLSL constant buffer is tagged with std140 and it contains vec3 and float.
This according to my knowledge will not work. Shouldn't the GL_EXT_scalar_block_layout be used here? The constant block should be tagged with scalar instead of std140. Am I missing something obvious here? Thanks.
For an arbitrary input buffer, there isn't a generic OpenGL memory layout that is exactly equivalent to the DX constant buffer layout.
DX constant buffers will add padding needed to stop single variables spanning 16 byte boundaries, but variables themselves are only 4 byte aligned.
GL std140 uniform buffers will always align vec3 on a 16 byte boundary. This has no equivalent in DX.
GL std430 uniform buffers (if supported via GL_EXT_scalar_block_layout) will always align vec3 on a 16 byte boundary. This has no equivalent in DX.
GL scalar uniform buffers (if supported via GL_EXT_scalar_block_layout) will only pad to component element size, and don't care about 16 byte boundaries. This has no equivalent in DX.
Things get even more fun if you start throwing around struct and array types ...
TLDR, if you want a fixed binary memory layout that is portable between DX and GL/GLES and Vulkan you take some responsibility for designing a portable memory layout for your constant buffers. You can't throw arbitrary layouts around and expect it to work.
The OpenGL specification lies (or is this a bug?)... Referring to the layout for std140, with shared uniform buffers, it states:
"The set of rules shown in Tabl e L-1 are used by the GLSL compiler to
layout members in a std140-qualified uniform block. The offsets of
members in the block are accumulated based on the sizes of the
previous members in the block (those declared before the variable in
question), and the starting offset. The starting offset of the first
member is always zero.
Scalar variable type (bool, int, uint, float) - Size of the scalar in
basic machine types"
(http://www.opengl-redbook.com/appendices/AppL.pdf)
So, armed with this information, I setup a uniform block in my shader that looks something like this:
// Spotlight.
layout (std140) uniform Spotlight
{
float Light_Intensity;
vec4 Light_Ambient;
vec3 Light_Position;
};
... only to discover it doesn't work with the subsequent std140 layout I setup on the CPU side. That is the first 4 bytes are a float (size of the machine scalar type for GLfloat), the next 16 bytes are a vec4 and the following 12 bytes are a vec3 (with 4 bytes left over on the end to take account of the rule that a vec3 is really a vec4).
When I change the CPU side to specify a float as being the same size as a vec4, i.e. 16 bytes, and do my offsets and buffer size making this assumption, the shader works as intended.
So, either the spec is wrong or I've misunderstood the meaning of "scalar" in this context, or ATI have a driver bug. Can anyone shed any light on this mystery?
That PDF you linked to is not the OpenGL specification. I don't know where you got it from, but that is certainly not the full list of rules. Always check your sources; the spec is not as unreadable as many claim it to be.
Yes, the size of variables of basic types is the same size as the basic machine type (ie: 4 bytes). But size alone does not determine the position of the variable.
Each type has a base alignment, and no matter where that type is found in a uniform block, it's overall byte offset must fit that alignment. The base alignment of a vec4 is 4 * the alignment of its basic type (ie: float). So the base alignment of a vec4 is 16.
Because Light_Intensity ends after 4 bytes, the compiler must insert 12 bytes of padding, because Light_Ambient cannot be on a 4-byte boundary. It must be on a 16-byte boundary, so the compiler uses 12 bytes of empty space.
ATI does have a few driver bugs around std140 layout, but this isn't one of them.
As a general rule, I like to explicitly put padding into my structures, and I avoid vec3 (because it has 16 byte alignment). Doing these generally cuts down on compiler bugs as well as accidental misunderstanding about where things go and how much room they actually take.
I've just read through this answer and its comments and have some related questions:
Does this rule apply to uniform variables too?
Can I call glUniform3fv for vec4 uniform and expect the W component to be 1.0, etc.?
Where does the OpenGL spec mention about this topic?
The specs in (7.6. UNIFORMVARIABLES) say that the Uniform* would result in INVALID_OPERATION error if:
the size indicated in the name of the Uniform* command used does not match the size of the uniform declared in the shader,
or
the component type and count indicated in the name of the Uniform* command used does not match the type of the uniform declared in the shader, where a boolean uniform component type is considered to match any of the Uniform*i{v}, Uniform*ui{v}, or Uniform*f{v} commands.
And testing to call glUniform3f for a vec4 uniform will indeed result in a INVALID_OPERATION error.
No, these rules do not apply to uniforms. Consequently, you are not allowed to call glUniform3fv on a vec4.
The Spec states in Section 7.6.1:
For all [...] uniform types loadable with Uniform* commands, the command
used must match the size and type of the uniform, as declared in the shader, and
no type conversions are done.
I'm using OpenGL 3.3 GLSL 1.5 compatibility. I'm getting a strange problem with my vertex data. I'm trying to pass an index value to the fragment shader, but the value seems to change based on my camera position.
This should be simple : I pass a GLfloat through the vertex shader to the fragment shader. I then convert this value to an unsigned integer. The value is correct the majority of the time, except for the edges of the fragment. No matter what I do the same distortion appears. Why is does my camera position change this value? Even in the ridiculous example below, tI erratically equals something other than 1.0;
uint i;
if (tI == 1.0) i = 1;
else i = 0;
vec4 color = texture2D(tex[i], t) ;
If I send integer data instead of float data I get the exact same problem. It does not seem to matter what I enter as vertex Data. The value I enter into the data is not consistent across the fragment. The distortion even looks the exact same each time.
What you are doing here is invalid in OpenGL/GLSL 3.30.
Let me quote the GLSL 3.30 specification, section 4.1.7 "Samplers" (emphasis mine):
Samplers aggregated into arrays within a shader (using square brackets
[ ]) can only be indexed with integral constant expressions (see
section 4.3.3 “Constant Expressions”).
Using a varying as index to a texture does not represent a constant expression as defined by the spec.
Beginning with GL 4.0, this was somewhat relaxed. The GLSL 4.00 specification states now the following (still my emphasis):
Samplers aggregated into arrays within a shader (using square brackets
[ ]) can only be indexed with a dynamically uniform integral
expression, otherwise results are undefined.
With dynamically uniform being defined as follows:
A fragment-shader expression is dynamically uniform if all fragments
evaluating it get the same resulting value. When loops are involved,
this refers to the expression's value for the same loop iteration.
When functions are involved, this refers to calls from the same call
point.
So now this is a bit tricky. If all fragment shader invocations actaully get the same value for that varying, it would be allowed, I guess. But it is unclear that your code guarantees that. You should also take into account that the fragment might be even sampled outside of the primitive.
However, you should never check floats for equality. There will be numerical issues. I don't know what exactly you are trying to achieve here, but you might use some simple rounding behavior, or use an integer varying. You also should disable the interpolation of the value in any case using the flat qualifier (which is required for the integer case anyway), which should greatly improve the changes of that construct to become dynamically uniform.
As it says on the tin: Is there any reason, ever, to use gl_FragColor instead of gl_FragData[0]? If not, then why does gl_FragColor even exist? Is it mere legacy from a time when gl_FragData did not exist?
(Yes, I know that both are deprecated in the latest GLSL versions, but I still want to write code that can run on older cards.)
I will refer you to the OpenGL specification for GLSL 1.1, as it makes very little distinction between the two except to say that they are mutually exclusive.
The OpenGL Shading Language (version 1.1) - 7.2 Fragment Shader Special Variables - pp. 43
If a shader statically assigns a value to gl_FragColor, it may not assign a value to any element of gl_FragData. If a shader statically writes a value to any element of gl_FragData, it may not assign a value to gl_FragColor. That is, a shader may assign values to either gl_FragColor or gl_FragData, but not both.
Given this language, gl_FragColor should probably be preferred in shaders that do not use MRT (multiple render targets). For shaders that output to multiple buffers, use gl_FragData [n]. But never mix-and-match, even though you might logically assume that gl_FragColor is an alias to gl_FragData [0].
The GLSL specification pre-dates FBOs, so having an array of locations to output fragment data did not always make sense. Since GLSL and FBOs are both core in OpenGL 3.0, it is easy to take this for granted. The old ARB extension specification for fragment shaders has a blurb on this very subject:
14) What is the interaction with a possible MRT (Multiple Render Target)
extension?
The OpenGL Shading Language defines the array gl_FragData[] to output
values to multiple buffers. There are two situations to consider.
1) There is no MRT extension support. A shader can statically assign a
value to either gl_FragColor or gl_FragData[0] (but not both).
Either way the same buffer will be targeted.
2) There is MRT support. In this case what happens is defined in the
relevant MRT extension documentation.
They were thinking ahead, but did not quite have all the pieces in place yet. This is ancient history in any case.