GLSL Implicit Conversion Rules: Int, Uint, and Float
Modern OpenGL Shading Language (GLSL) version 4.00 and above—or
systems supporting the GL_ARB_shader_implicit_conversions
extension—allows automatic type promotion between basic numeric types.
These implicit conversions eliminate the need for verbose explicit
casting when mixing signed integers (int), unsigned
integers (uint), and single-precision floating-point
numbers (float). This article covers the hierarchy of
implicit type conversions, the rules governing mixed-type expressions
and assignments, and the limitations designed to prevent unintended
precision loss.
The Type Hierarchy and Promotion Direction
Implicit conversion in GLSL follows a strict unidirectional hierarchy based on data representation and value preservation. Conversions are allowed only in the direction of increasing capacity or flexibility, moving strictly from left to right:
\[\text{int} \longrightarrow \text{uint} \longrightarrow \text{float}\]
inttouint: A signed integer can be implicitly promoted to an unsigned integer.inttofloat: A signed integer can be implicitly promoted to a single-precision float.uinttofloat: An unsigned integer can be implicitly promoted to a single-precision float.
GLSL strictly prohibits reverse conversions (narrowing). A
float will never implicitly convert to an int
or uint, and a uint will never implicitly
convert to an int. Any attempt to assign a higher-ranked
type to a lower-ranked variable without explicit casting (such as
int(myFloat)) results in a compilation error.
Binary Operations and Arithmetic
When arithmetic, relational, or bitwise binary operators encounter operands of differing types, GLSL applies the type hierarchy to determine a common type before executing the operation:
- Mixed
floatand Integer (intoruint): The integer operand is promoted tofloat, and the operation evaluates to afloat. - Mixed
intanduint: Theintoperand is promoted touint, and the operation evaluates to auint.
Examples:
int i = 2;
uint u = 4u;
float f = 1.5;
auto a = i + f; // 'i' becomes float(2.0); result is float (3.5)
auto b = u * f; // 'u' becomes float(4.0); result is float (6.0)
auto c = i + u; // 'i' becomes uint(2u); result is uint (6u)Care must be taken when mixing signed negative integers with unsigned
integers; an int with a negative value will be
reinterpreted via standard two's complement when implicitly promoted to
uint, yielding a very large positive number.
Assignment and Initialization
Implicit conversions apply automatically during variable declaration, initialization, and assignment, provided the value on the right-hand side can be safely promoted to match the type of the left-hand side.
float f1 = 10; // Valid: 'int' literal 10 promoted to 'float' (10.0)
float f2 = 10u; // Valid: 'uint' literal 10u promoted to 'float' (10.0)
uint u1 = 10; // Valid: 'int' literal 10 promoted to 'uint' (10u)
int i1 = 10u; // Error: Cannot implicitly demote 'uint' to 'int'
int i2 = 10.0; // Error: Cannot implicitly demote 'float' to 'int'
uint u2 = 10.0; // Error: Cannot implicitly demote 'float' to 'uint'Function Overload Resolution
When calling overloaded functions, the GLSL compiler selects the function whose signature most closely matches the supplied argument types:
- Exact Match: If an exact signature matches the parameter types without conversion, that overload is selected.
- Promotion Match: If no exact match exists, the compiler searches for overloads that can be satisfied using implicit conversions based on the promotion chain.
- Ambiguity: If an invocation matches multiple overloads equally well via conversions, the compiler raises a compilation error due to ambiguity.
void compute(float x);
void compute(uint x);
compute(5); // Ambiguous: 'int' can implicitly convert to both 'uint' and 'float'In such ambiguous cases, the call must be disambiguated using an
explicit cast, such as compute(float(5)) or
compute(uint(5)).
Vector and Matrix Conversions
Implicit conversion rules extend component-wise to vectors containing these scalar types:
ivecimplicitly converts touvecorvecof matching dimensions (e.g.,ivec3tovec3).uvecimplicitly converts tovecof matching dimensions (e.g.,uvec2tovec2).
Vector conversions require strict dimension equality. An
int cannot implicitly expand to a vec2, nor
can an ivec2 implicitly convert to a vec3. All
scalar-to-vector promotions or dimensional changes must be handled
through explicit constructors.