/*     Copyright 2015-2018 Egor Yusov
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
 *
 *  In no event and under no legal theory, whether in tort (including negligence), 
 *  contract, or otherwise, unless required by applicable law (such as deliberate 
 *  and grossly negligent acts) or agreed to in writing, shall any Contributor be
 *  liable for any damages, including any direct, indirect, special, incidental, 
 *  or consequential damages of any character arising as a result of this License or 
 *  out of the use or inability to use the software (including but not limited to damages 
 *  for loss of goodwill, work stoppage, computer failure or malfunction, or any and 
 *  all other commercial damages or losses), even if such Contributor has been advised 
 *  of the possibility of such damages.
 */

#ifndef _GLSL_DEFINITIONS_
#define _GLSL_DEFINITIONS_

#define GLSL

#ifdef GL_ES
// From GLES 3.1 spec:
//    Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui,
//    image variables must specify either memory qualifier readonly or the memory qualifier writeonly.
#   define IMAGE_WRITEONLY writeonly
#else
#   define IMAGE_WRITEONLY
#endif 

#define float4 vec4
#define float3 vec3
#define float2 vec2

#define int4 ivec4
#define int3 ivec3
#define int2 ivec2

#define uint4 uvec4
#define uint3 uvec3
#define uint2 uvec2

#define bool4 bvec4
#define bool3 bvec3
#define bool2 bvec2

// OpenGL matrices in GLSL are always as column-major 
// (this is not related to how they are stored)
#define float2x2 mat2x2
#define float2x3 mat3x2
#define float2x4 mat4x2

#define float3x2 mat2x3
#define float3x3 mat3x3
#define float3x4 mat4x3

#define float4x2 mat2x4
#define float4x3 mat3x4
#define float4x4 mat4x4
#define matrix mat4x4

#define static

#define SamplerState int
#define SamplerComparisonState int

// https://www.opengl.org/wiki/Memory_Model#Incoherent_memory_access
// Shared variable access uses the rules for incoherent memory access. 
// This means that the user must perform certain synchronization in 
// order to ensure that shared variables are visible.
// At the same time, shared variables are all implicitly declared coherent, 
// so one don't need to (and can't) use that qualifier.
#define groupshared shared

#ifdef FRAGMENT_SHADER
#   define ddx dFdx
#   define ddy dFdy
#else
#   define ddx(x) (x) // GLSL compiler fails when it sees derivatives 
#   define ddy(x) (x) // in any shader but fragment
#endif

#define ddx_coarse ddx
#define ddy_coarse ddy
#define ddx_fine ddx
#define ddy_fine ddy

#define mul(a, b) ((a)*(b))
#define frac fract
#define atan2 atan
#define rsqrt inversesqrt
#define fmod mod
#define lerp mix
#define dst distance
#define countbits bitCount
#define firstbithigh findMSB
#define firstbitlow findLSB
#define reversebits bitfieldReverse

float rcp( float x ){ return 1.0 / x; }
vec2  rcp( vec2  x ){ return vec2(1.0,1.0) / x; }
vec3  rcp( vec3  x ){ return vec3(1.0,1.0,1.0) / x; }
vec4  rcp( vec4  x ){ return vec4(1.0,1.0,1.0,1.0) / x; }

float saturate( float x ){ return clamp( x, 0.0,                      1.0 ); }
vec2  saturate( vec2  x ){ return clamp( x, vec2(0.0, 0.0),           vec2(1.0, 1.0) ); }
vec3  saturate( vec3  x ){ return clamp( x, vec3(0.0, 0.0, 0.0),      vec3(1.0, 1.0, 1.0) ); }
vec4  saturate( vec4  x ){ return clamp( x, vec4(0.0, 0.0, 0.0, 0.0), vec4(1.0, 1.0, 1.0, 1.0) ); }

void sincos( float x, out float s, out float c ){ s = sin( x ); c = cos( x ); }
void sincos( vec2  x, out vec2  s, out vec2  c ){ s = sin( x ); c = cos( x ); }
void sincos( vec3  x, out vec3  s, out vec3  c ){ s = sin( x ); c = cos( x ); }
void sincos( vec4  x, out vec4  s, out vec4  c ){ s = sin( x ); c = cos( x ); }


// Bit conversion operations

float asfloat( float x ){ return x; }
vec2  asfloat( vec2  x ){ return x; }
vec3  asfloat( vec3  x ){ return x; }
vec4  asfloat( vec4  x ){ return x; }

float asfloat( int   x ){ return intBitsToFloat(x); }
vec2  asfloat( ivec2 x ){ return intBitsToFloat(x); }
vec3  asfloat( ivec3 x ){ return intBitsToFloat(x); }
vec4  asfloat( ivec4 x ){ return intBitsToFloat(x); }

float asfloat( uint  x ){ return uintBitsToFloat(x); }
vec2  asfloat( uvec2 x ){ return uintBitsToFloat(x); }
vec3  asfloat( uvec3 x ){ return uintBitsToFloat(x); }
vec4  asfloat( uvec4 x ){ return uintBitsToFloat(x); }


int   asint( int   x ){ return x; }
ivec2 asint( ivec2 x ){ return x; }
ivec3 asint( ivec3 x ){ return x; }
ivec4 asint( ivec4 x ){ return x; }

int   asint( uint  x ){ return int(x);   }
ivec2 asint( uvec2 x ){ return ivec2(x); }
ivec3 asint( uvec3 x ){ return ivec3(x); }
ivec4 asint( uvec4 x ){ return ivec4(x); }

int   asint( float x ){ return floatBitsToInt(x); }
ivec2 asint( vec2  x ){ return floatBitsToInt(x); }
ivec3 asint( vec3  x ){ return floatBitsToInt(x); }
ivec4 asint( vec4  x ){ return floatBitsToInt(x); }


uint  asuint( uint  x ){ return x; }
uvec2 asuint( uvec2 x ){ return x; }
uvec3 asuint( uvec3 x ){ return x; }
uvec4 asuint( uvec4 x ){ return x; }

uint  asuint( int  x  ){ return  uint(x); }
uvec2 asuint( ivec2 x ){ return uvec2(x); }
uvec3 asuint( ivec3 x ){ return uvec3(x); }
uvec4 asuint( ivec4 x ){ return uvec4(x); }

uint  asuint( float x ){ return floatBitsToUint(x); }
uvec2 asuint( vec2  x ){ return floatBitsToUint(x); }
uvec3 asuint( vec3  x ){ return floatBitsToUint(x); }
uvec4 asuint( vec4  x ){ return floatBitsToUint(x); }

#if defined(GL_ES) && (__VERSION__>=310) || !defined(GL_ES) && (__VERSION__>=420)
float f16tof32( uint u1 )
{
    return unpackHalf2x16( u1 ).x;
}
vec2 f16tof32( uvec2 u2 )
{ 
    uint u2PackedHalf = (u2.x & 0x0ffffu) | ((u2.y & 0x0ffffu) << 16u);
    return unpackHalf2x16( u2PackedHalf ); 
}
vec3 f16tof32( uvec3 u3 )
{ 
    return vec3( f16tof32( u3.xy ), f16tof32( u3.z ) );
}
vec4 f16tof32( uvec4 u4 )
{ 
    return vec4( f16tof32( u4.xy ), f16tof32( u4.zw ) );
}
float f16tof32( int   i1 ){ return f16tof32( uint ( i1 ) ); }
vec2  f16tof32( ivec2 i2 ){ return f16tof32( uvec2( i2 ) ); }
vec3  f16tof32( ivec3 i3 ){ return f16tof32( uvec3( i3 ) ); }
vec4  f16tof32( ivec4 i4 ){ return f16tof32( uvec4( i4 ) ); }

uint f32tof16( float f )
{ 
    return packHalf2x16( vec2( f, 0.0 ) ) & 0x0ffffu;
}
uvec2 f32tof16( vec2 f2 )
{ 
    uint u2PackedHalf = packHalf2x16( f2 );
    return uvec2( u2PackedHalf & 0x0ffffu, u2PackedHalf >> 16u );
}
uvec3 f32tof16( vec3 f3 )
{
    return uvec3( f32tof16( f3.xy ), f32tof16( f3.z ) );
}
uvec4 f32tof16( vec4 f4 )
{
    return uvec4( f32tof16( f4.xy ), f32tof16( f4.zw ) );
}
#endif

// Use #define as double is not supported on GLES
#define asdouble(lowbits, highbits) packDouble2x32( uvec2( lowbits, highbits ) )

// Floating point functions

bool isfinite( float x )
{
    return !isinf( x ) && !isnan( x );
}

bool2 isfinite( vec2 f2 )
{
    return bool2( isfinite( f2.x ), isfinite( f2.y ) );
}

bool3 isfinite( vec3 f3 )
{
    return bool3( isfinite( f3.xy ), isfinite( f3.z ) );
}

bool4 isfinite( vec4 f4 )
{
    return bool4( isfinite( f4.xyz ), isfinite( f4.w ) );
}

#ifndef GL_ES
    float noise( float x ){ return noise1( x ); }
    vec2  noise( vec2  x ){ return noise2( x ); }
    vec3  noise( vec3  x ){ return noise3( x ); }
    vec4  noise( vec4  x ){ return noise4( x ); }
#else
    float noise( float x ){ return 0.0; }
    vec2  noise( vec2  x ){ return vec2(0.0, 0.0); }
    vec3  noise( vec3  x ){ return vec3(0.0, 0.0, 0.0); }
    vec4  noise( vec4  x ){ return vec4(0.0, 0.0, 0.0, 0.0); }
#endif

float log10( float x )
{
    return log( x ) / log( 10.0 );
}
vec2 log10( vec2 x )
{
    float _lg10 = log( 10.0 );
    return log( x ) / vec2(_lg10, _lg10);
}
vec3 log10( vec3 x )
{
    float _lg10 = log( 10.0 );
    return log( x ) / vec3(_lg10, _lg10, _lg10);
}
vec4 log10( vec4 x )
{
    float _lg10 = log( 10.0 );
    return log( x ) / vec4(_lg10, _lg10, _lg10, _lg10);
}


#ifdef GL_ES
#   define mad(a,b,c) ((a)*(b)+(c))
#else
#   define mad fma
#endif


// Relational and logical operators
#define Less lessThan
#define LessEqual lessThanEqual
#define Greater greaterThan
#define GreaterEqual greaterThanEqual
#define Equal equal
#define NotEqual notEqual
#define Not not
bool4 And(bool4 L, bool4 R)
{
    return bool4(L.x && R.x,
                 L.y && R.y,
                 L.z && R.z,
                 L.w && R.w);
}
bool3 And(bool3 L, bool3 R)
{
    return bool3(L.x && R.x,
                 L.y && R.y,
                 L.z && R.z);
}
bool2 And(bool2 L, bool2 R)
{
    return bool2(L.x && R.x,
                 L.y && R.y);
}
bool And(bool L, bool R)
{
    return (L && R);
}


bool4 Or(bool4 L, bool4 R)
{
    return bool4(L.x || R.x,
                 L.y || R.y,
                 L.z || R.z,
                 L.w || R.w);
}
bool3 Or(bool3 L, bool3 R)
{
    return bool3(L.x || R.x,
                 L.y || R.y,
                 L.z || R.z);
}
bool2 Or(bool2 L, bool2 R)
{
    return bool2(L.x || R.x,
                 L.y || R.y);
}
bool Or(bool L, bool R)
{
    return (L || R);
}

float4 BoolToFloat( bool4 b4 )
{
    return float4(b4.x ? 1.0 : 0.0,
                  b4.y ? 1.0 : 0.0,
                  b4.z ? 1.0 : 0.0,
                  b4.w ? 1.0 : 0.0);
}
float3 BoolToFloat( bool3 b3 )
{
    return float3(b3.x ? 1.0 : 0.0,
                  b3.y ? 1.0 : 0.0,
                  b3.z ? 1.0 : 0.0);
}
float2 BoolToFloat( bool2 b2 )
{
    return float2(b2.x ? 1.0 : 0.0,
                  b2.y ? 1.0 : 0.0);
}
float BoolToFloat( bool b )
{
    return b ? 1.0 : 0.0;
}


// Synchronization functions

#ifdef COMPUTE_SHADER

// https://www.opengl.org/wiki/Memory_Model#Incoherent_memory_access

// MSDN: GroupMemoryBarrier() blocks execution of all threads 
// in a group until all group SHARED accesses have been completed.
void GroupMemoryBarrier()
{
    // OpenGL.org: groupMemoryBarrier() waits on the completion of all memory accesses 
    // performed by an invocation of a compute shader relative to the same access performed 
    // by other invocations in the same work group and then returns with no other effect.

    // groupMemoryBarrier() acts like memoryBarrier(), ordering memory writes for all kinds 
    // of variables, but it only orders read/writes for the current work group.
    groupMemoryBarrier();

    // OpenGL.org: memoryBarrierShared() waits on the completion of 
    // all memory accesses resulting from the use of SHARED variables
    // and then returns with no other effect. 
    memoryBarrierShared();
}

// MSDN: GroupMemoryBarrierWithGroupSync() blocks execution of all 
// threads in a group until all memory accesses have been completed 
// and all threads in the group have reached this call.
void GroupMemoryBarrierWithGroupSync()
{
    // Issue memory barrier first!
    GroupMemoryBarrier();
    barrier();
}

// MSDN: DeviceMemoryBarrier() blocks execution of all threads 
// in a group until all device memory accesses have been completed.
void DeviceMemoryBarrier()
{
    // Call all memory barriers except for shared memory
    
    // Do we need to call groupMemoryBarrier() ????? 

    // OpenGL.org: memoryBarrierBuffer() waits on the completion of 
    // all memory accesses resulting from the use of BUFFER variables 
    // and then returns with no other effect
    memoryBarrierBuffer();

    // OpenGL.org: memoryBarrierImage() waits on the completion of all 
    // memory accesses resulting from the use of IMAGE variables and then 
    // returns with no other effect. 
    memoryBarrierImage();

    // OpenGL.org: memoryBarrierAtomicCounter() waits on the completion of 
    // all accesses resulting from the use of ATOMIC COUNTERS and then returns 
    // with no other effect. 
    memoryBarrierAtomicCounter();
}

// MSDN: DeviceMemoryBarrierWithGroupSync() blocks execution of 
// all threads in a group until all device memory accesses have 
// been completed and all threads in the group have reached this call.
void DeviceMemoryBarrierWithGroupSync()
{
    DeviceMemoryBarrier();
    barrier();
}

// MSDN: AllMemoryBarrier() blocks execution of all threads in a 
// group until all memory accesses have been completed.
void AllMemoryBarrier()
{
    // OpenGL.org: memoryBarrier() waits on the completion of ALL 
    // memory accesses resulting from the use of IMAGE variables or 
    // ATOMIC COUNTERS and then returns with no other effect.
    memoryBarrier();
    // NOTE: nothing is said about buffer memory and shared memory,
    // so call memoryBarrierBuffer() and memoryBarrierShared() for safety

    // OpenGL.org: memoryBarrierBuffer() waits on the completion of 
    // all memory accesses resulting from the use of BUFFER variables 
    // and then returns with no other effect
    memoryBarrierBuffer();

    // OpenGL.org: memoryBarrierShared() waits on the completion of 
    // all memory accesses resulting from the use of SHARED variables
    // and then returns with no other effect. 
    memoryBarrierShared();

    // Call all memory barrier functions. They should have no effect
    // if everything is synchronized.
    
    // OpenGL.org: memoryBarrierImage() waits on the completion of all 
    // memory accesses resulting from the use of IMAGE variables and then 
    // returns with no other effect. 
    memoryBarrierImage();

    // OpenGL.org: memoryBarrierAtomicCounter() waits on the completion of 
    // all accesses resulting from the use of ATOMIC COUNTERS and then returns 
    // with no other effect. 
    memoryBarrierAtomicCounter();

    // groupMemoryBarrier waits on the completion of all memory accesses performed 
    // by an invocation of a compute shader relative to the same access performed by 
    // other invocations in the same work group and then returns with no other effect.
    groupMemoryBarrier();
}

// MSDN: AllMemoryBarrierWithGroupSync() blocks execution of all 
// threads in a group until all memory accesses have been completed 
// and all threads in the group have reached this call.
void AllMemoryBarrierWithGroupSync()
{
    AllMemoryBarrier();
    barrier();
}

#else

void AllMemoryBarrier(){}
void AllMemoryBarrierWithGroupSync(){}
void DeviceMemoryBarrier(){}
void DeviceMemoryBarrierWithGroupSync(){}
void GroupMemoryBarrier(){}
void GroupMemoryBarrierWithGroupSync(){}

#endif


// Type conversion functions

vec4 _ExpandVector( float x ){ return vec4(    x,    x,    x,    x ); }
vec4 _ExpandVector( vec2 f2 ){ return vec4( f2.x, f2.y,  0.0,  0.0 ); }
vec4 _ExpandVector( vec3 f3 ){ return vec4( f3.x, f3.y, f3.z,  0.0 ); }
vec4 _ExpandVector( vec4 f4 ){ return vec4( f4.x, f4.y, f4.z, f4.w ); }

ivec4 _ExpandVector( int    x ){ return ivec4(    x,    x,    x,    x ); }
ivec4 _ExpandVector( ivec2 i2 ){ return ivec4( i2.x, i2.y,    0,    0 ); }
ivec4 _ExpandVector( ivec3 i3 ){ return ivec4( i3.x, i3.y, i3.z,    0 ); }
ivec4 _ExpandVector( ivec4 i4 ){ return ivec4( i4.x, i4.y, i4.z, i4.w ); }

uvec4 _ExpandVector( uint   x ){ return uvec4(    x,    x,    x,    x ); }
uvec4 _ExpandVector( uvec2 u2 ){ return uvec4( u2.x, u2.y,   0u,   0u ); }
uvec4 _ExpandVector( uvec3 u3 ){ return uvec4( u3.x, u3.y, u3.z,   0u ); }
uvec4 _ExpandVector( uvec4 u4 ){ return uvec4( u4.x, u4.y, u4.z, u4.w ); }

bvec4 _ExpandVector( bool   x ){ return bvec4(    x,    x,     x,     x ); }
bvec4 _ExpandVector( bvec2 b2 ){ return bvec4( b2.x, b2.y, false, false ); }
bvec4 _ExpandVector( bvec3 b3 ){ return bvec4( b3.x, b3.y,  b3.z, false ); }
bvec4 _ExpandVector( bvec4 b4 ){ return bvec4( b4.x, b4.y,  b4.z,  b4.w ); }

void _ResizeVector(out vec4 outVec4, in vec4 inVec4){outVec4 = inVec4;}
void _ResizeVector(out vec3 outVec3, in vec4 inVec4){outVec3 = inVec4.xyz;}
void _ResizeVector(out vec2 outVec2, in vec4 inVec4){outVec2 = inVec4.xy;}
void _ResizeVector(out float outFlt, in vec4 inVec4){outFlt  = inVec4.x;}

void _ResizeVector(out vec4 outVec4, in vec3 inVec3){outVec4 = vec4(inVec3, 0.0);}
void _ResizeVector(out vec3 outVec3, in vec3 inVec3){outVec3 = inVec3;}
void _ResizeVector(out vec2 outVec2, in vec3 inVec3){outVec2 = inVec3.xy;}
void _ResizeVector(out float outFlt, in vec3 inVec3){outFlt  = inVec3.x;}

void _ResizeVector(out vec4 outVec4, in vec2 inVec2){outVec4 = vec4(inVec2, 0.0, 0.0);}
void _ResizeVector(out vec3 outVec3, in vec2 inVec2){outVec3 = vec3(inVec2, 0.0);}
void _ResizeVector(out vec2 outVec2, in vec2 inVec2){outVec2 = inVec2;}
void _ResizeVector(out float outFlt, in vec2 inVec2){outFlt  = inVec2.x;}

void _ResizeVector(out vec4 outVec4, in float v){outVec4 = vec4(v, 0.0, 0.0, 0.0);}
void _ResizeVector(out vec3 outVec3, in float v){outVec3 = vec3(v, 0.0, 0.0);}
void _ResizeVector(out vec2 outVec2, in float v){outVec2 = vec2(v, 0.0);}
void _ResizeVector(out float outFlt, in float v){outFlt  = v;}


void _TypeConvertStore( out float Dst, in int   Src ){ Dst = float( Src ); }
void _TypeConvertStore( out float Dst, in uint  Src ){ Dst = float( Src ); }
void _TypeConvertStore( out float Dst, in float Src ){ Dst = float( Src ); }
void _TypeConvertStore( out uint  Dst, in int   Src ){ Dst = uint( Src ); }
void _TypeConvertStore( out uint  Dst, in uint  Src ){ Dst = uint( Src ); }
void _TypeConvertStore( out uint  Dst, in float Src ){ Dst = uint( Src ); }
void _TypeConvertStore( out int   Dst, in int   Src ){ Dst = int( Src ); }
void _TypeConvertStore( out int   Dst, in uint  Src ){ Dst = int( Src ); }
void _TypeConvertStore( out int   Dst, in float Src ){ Dst = int( Src ); }


int _ToInt( int x )    { return int(x);     }
int _ToInt( ivec2 v )  { return int(v.x);   }
int _ToInt( ivec3 v )  { return int(v.x);   }
int _ToInt( ivec4 v )  { return int(v.x);   }

int _ToInt( uint x )   { return int(x);     }
int _ToInt( uvec2 v )  { return int(v.x);   }
int _ToInt( uvec3 v )  { return int(v.x);   }
int _ToInt( uvec4 v )  { return int(v.x);   }

int _ToInt( float x )  { return int(x);     }
int _ToInt( vec2 v )   { return int(v.x);   }
int _ToInt( vec3 v )   { return int(v.x);   }
int _ToInt( vec4 v )   { return int(v.x);   }

int _ToInt( bool x )   { return x   ? 1 : 0;}
int _ToInt( bvec2 v )  { return v.x ? 1 : 0;}
int _ToInt( bvec3 v )  { return v.x ? 1 : 0;}
int _ToInt( bvec4 v )  { return v.x ? 1 : 0;}



float _ToFloat( int x )  { return float(x);     }
float _ToFloat( ivec2 v ){ return float(v.x);   }
float _ToFloat( ivec3 v ){ return float(v.x);   }
float _ToFloat( ivec4 v ){ return float(v.x);   }

float _ToFloat( uint x ) { return float(x);     }
float _ToFloat( uvec2 v ){ return float(v.x);   }
float _ToFloat( uvec3 v ){ return float(v.x);   }
float _ToFloat( uvec4 v ){ return float(v.x);   }

float _ToFloat( float x ){ return float(x);     }
float _ToFloat( vec2 v ) { return float(v.x);   }
float _ToFloat( vec3 v ) { return float(v.x);   }
float _ToFloat( vec4 v ) { return float(v.x);   }

float _ToFloat( bool x ) { return x   ? 1.0 : 0.0;}
float _ToFloat( bvec2 v ){ return v.x ? 1.0 : 0.0;}
float _ToFloat( bvec3 v ){ return v.x ? 1.0 : 0.0;}
float _ToFloat( bvec4 v ){ return v.x ? 1.0 : 0.0;}



uint _ToUint( int x )  { return uint(x);     }
uint _ToUint( uint x ) { return uint(x);     }
uint _ToUint( float x ){ return uint(x);     }
uint _ToUint( bool x ) { return x ? 1u : 0u; }

bool _ToBool( int x )  { return x != 0   ? true : false; }
bool _ToBool( uint x ) { return x != 0u  ? true : false; }
bool _ToBool( float x ){ return x != 0.0 ? true : false; }
bool _ToBool( bool x ) { return x; }

#define _ToVec2(x,y)     vec2(_ToFloat(x), _ToFloat(y))
#define _ToVec3(x,y,z)   vec3(_ToFloat(x), _ToFloat(y), _ToFloat(z))
#define _ToVec4(x,y,z,w) vec4(_ToFloat(x), _ToFloat(y), _ToFloat(z), _ToFloat(w))

#define _ToIvec2(x,y)     ivec2(_ToInt(x), _ToInt(y))
#define _ToIvec3(x,y,z)   ivec3(_ToInt(x), _ToInt(y), _ToInt(z))
#define _ToIvec4(x,y,z,w) ivec4(_ToInt(x), _ToInt(y), _ToInt(z), _ToInt(w))

#define _ToUvec2(x,y)     uvec2(_ToUint(x), _ToUint(y))
#define _ToUvec3(x,y,z)   uvec3(_ToUint(x), _ToUint(y), _ToUint(z))
#define _ToUvec4(x,y,z,w) uvec4(_ToUint(x), _ToUint(y), _ToUint(z), _ToUint(w))

#define _ToBvec2(x,y)     bvec2(_ToBool(x), _ToBool(y))
#define _ToBvec3(x,y,z)   bvec3(_ToBool(x), _ToBool(y), _ToBool(z))
#define _ToBvec4(x,y,z,w) bvec4(_ToBool(x), _ToBool(y), _ToBool(z), _ToBool(w))


int   _ToIvec( uint  u1 ){ return _ToInt(   u1 ); }
ivec2 _ToIvec( uvec2 u2 ){ return _ToIvec2( u2.x, u2.y ); }
ivec3 _ToIvec( uvec3 u3 ){ return _ToIvec3( u3.x, u3.y, u3.z ); }
ivec4 _ToIvec( uvec4 u4 ){ return _ToIvec4( u4.x, u4.y, u4.z, u4.w ); }

int   _ToIvec( int   i1 ){ return i1; }
ivec2 _ToIvec( ivec2 i2 ){ return i2; }
ivec3 _ToIvec( ivec3 i3 ){ return i3; }
ivec4 _ToIvec( ivec4 i4 ){ return i4; }

int   _ToIvec( float f1 ){ return _ToInt(   f1 ); }
ivec2 _ToIvec( vec2  f2 ){ return _ToIvec2( f2.x, f2.y ); }
ivec3 _ToIvec( vec3  f3 ){ return _ToIvec3( f3.x, f3.y, f3.z ); }
ivec4 _ToIvec( vec4  f4 ){ return _ToIvec4( f4.x, f4.y, f4.z, f4.w ); }


float _ToVec( uint  u1 ){ return _ToFloat(u1); }
vec2  _ToVec( uvec2 u2 ){ return _ToVec2( u2.x, u2.y ); }
vec3  _ToVec( uvec3 u3 ){ return _ToVec3( u3.x, u3.y, u3.z ); }
vec4  _ToVec( uvec4 u4 ){ return _ToVec4( u4.x, u4.y, u4.z, u4.w ); }
         
float _ToVec( int   i1 ){ return _ToFloat(i1); }
vec2  _ToVec( ivec2 i2 ){ return _ToVec2( i2.x, i2.y ); }
vec3  _ToVec( ivec3 i3 ){ return _ToVec3( i3.x, i3.y, i3.z ); }
vec4  _ToVec( ivec4 i4 ){ return _ToVec4( i4.x, i4.y, i4.z, i4.w ); }
         
float _ToVec( float f1 ){ return f1; }
vec2  _ToVec( vec2  f2 ){ return f2; }
vec3  _ToVec( vec3  f3 ){ return f3; }
vec4  _ToVec( vec4  f4 ){ return f4; }


uint   _ToUvec( uint  u1 ){ return u1; }
uvec2  _ToUvec( uvec2 u2 ){ return u2; }
uvec3  _ToUvec( uvec3 u3 ){ return u3; }
uvec4  _ToUvec( uvec4 u4 ){ return u4; }
         
uint   _ToUvec( int   i1 ){ return _ToUint(  i1 ); }
uvec2  _ToUvec( ivec2 i2 ){ return _ToUvec2( i2.x, i2.y ); }
uvec3  _ToUvec( ivec3 i3 ){ return _ToUvec3( i3.x, i3.y, i3.z ); }
uvec4  _ToUvec( ivec4 i4 ){ return _ToUvec4( i4.x, i4.y, i4.z, i4.w ); }
         
uint   _ToUvec( float f1 ){ return _ToUint(  f1 ); }
uvec2  _ToUvec( vec2  f2 ){ return _ToUvec2( f2.x, f2.y ); }
uvec3  _ToUvec( vec3  f3 ){ return _ToUvec3( f3.x, f3.y, f3.z ); }
uvec4  _ToUvec( vec4  f4 ){ return _ToUvec4( f4.x, f4.y, f4.z, f4.w ); }


// TEXTURE FUNCTION STUB MACROS
// https://www.opengl.org/wiki/Sampler_(GLSL)


// Texture size queries
// https://www.opengl.org/sdk/docs/man/html/textureSize.xhtml
// textureSize returns the dimensions of level lod (if present) of the texture bound to sampler. 
// The components in the return value are filled in, in order, with the width, height and depth 
// of the texture. For the array forms, the last component of the return value is the number of 
// layers in the texture array.

//#if !(defined(DESKTOP_GL) && __VERSION__ >= 430)
#   define textureQueryLevels(x) 0 // Only supported on 4.3+
//#endif

#define GetTex1DDimensions_1(Sampler, Width)\
{                                                       \
    _TypeConvertStore( Width, textureSize(Sampler, 0) );\
}

#define GetTex1DDimensions_3(Sampler, MipLevel, Width, NumberOfMipLevels)\
{                                                                        \
    _TypeConvertStore( Width, textureSize(Sampler, _ToInt(MipLevel)) );  \
    _TypeConvertStore( NumberOfMipLevels, textureQueryLevels(Sampler) ); \
}

#define GetTex1DArrDimensions_2(Sampler, Width, Elements)\
{                                           \
    ivec2 i2Size = textureSize(Sampler, 0); \
    _TypeConvertStore( Width,    i2Size.x );\
    _TypeConvertStore( Elements, i2Size.y );\
}

#define GetTex1DArrDimensions_4(Sampler, MipLevel, Width, Elements, NumberOfMipLevels)\
{                                                           \
    ivec2 i2Size = textureSize(Sampler, _ToInt(MipLevel));  \
    _TypeConvertStore( Width,    i2Size.x );                \
    _TypeConvertStore( Elements, i2Size.y );                \
    _TypeConvertStore( NumberOfMipLevels, textureQueryLevels(Sampler) );\
}

#define GetTex2DDimensions_2(Sampler, Width, Height)\
{                                                   \
    ivec2 i2Size = textureSize(Sampler, 0);         \
    _TypeConvertStore( Width,  i2Size.x );          \
    _TypeConvertStore( Height, i2Size.y );          \
}

#define GetTex2DDimensions_4(Sampler, MipLevel, Width, Height, NumberOfMipLevels)\
{                                                           \
    ivec2 i2Size = textureSize(Sampler, _ToInt(MipLevel) ); \
    _TypeConvertStore( Width,  i2Size.x );                              \
    _TypeConvertStore( Height, i2Size.y );                              \
    _TypeConvertStore( NumberOfMipLevels, textureQueryLevels(Sampler) );\
}

#define GetTex2DArrDimensions_3(Sampler, Width, Height, Elements)\
{                                           \
    ivec3 i3Size = textureSize(Sampler, 0); \
    _TypeConvertStore( Width,   i3Size.x ); \
    _TypeConvertStore( Height,  i3Size.y ); \
    _TypeConvertStore( Elements,i3Size.z ); \
}

#define GetTex2DArrDimensions_5(Sampler, MipLevel, Width, Height, Elements, NumberOfMipLevels)\
{                                                           \
    ivec3 i3Size = textureSize(Sampler, _ToInt(MipLevel));  \
    _TypeConvertStore( Width,    i3Size.x );                            \
    _TypeConvertStore( Height,   i3Size.y );                            \
    _TypeConvertStore( Elements, i3Size.z );                            \
    _TypeConvertStore( NumberOfMipLevels, textureQueryLevels(Sampler) );\
}

#define GetTex3DDimensions_3(Sampler, Width, Height, Depth)\
{                                           \
    ivec3 i3Size = textureSize(Sampler, 0); \
    _TypeConvertStore( Width,  i3Size.x );  \
    _TypeConvertStore( Height, i3Size.y );  \
    _TypeConvertStore( Depth,  i3Size.z );  \
}

#define GetTex3DDimensions_5(Sampler, MipLevel, Width, Height, Depth, NumberOfMipLevels)\
{                                                           \
    ivec3 i3Size = textureSize(Sampler, _ToInt(MipLevel));  \
    _TypeConvertStore( Width,  i3Size.x );                              \
    _TypeConvertStore( Height, i3Size.y );                              \
    _TypeConvertStore( Depth,  i3Size.z );                              \
    _TypeConvertStore( NumberOfMipLevels, textureQueryLevels(Sampler) );\
}

#define GetTex2DMSDimensions_3(Sampler, Width, Height, NumberOfSamples)\
{                                           \
    ivec2 i2Size = textureSize(Sampler);    \
    _TypeConvertStore( Width,  i2Size.x );  \
    _TypeConvertStore( Height, i2Size.y );  \
    _TypeConvertStore( NumberOfSamples, 0 );\
}

#define GetTex2DMSArrDimensions_4(Sampler, Width, Height, Elements, NumberOfSamples)\
{                                           \
    ivec3 i3Size = textureSize(Sampler);    \
    _TypeConvertStore( Width,    i3Size.x );\
    _TypeConvertStore( Height,   i3Size.y );\
    _TypeConvertStore( Elements, i3Size.z );\
    _TypeConvertStore( NumberOfSamples, 0 );\
}


// https://www.opengl.org/sdk/docs/man/html/imageSize.xhtml
// imageSize returns the dimensions of the image bound to image. The components in the 
// return value are filled in, in order, with the width, height and depth of the image. 
// For the array forms, the last component of the return value is the number of layers 
// in the texture array.

#define GetRWTex1DDimensions_1(Tex, Width)\
{                                               \
    _TypeConvertStore( Width, imageSize(Tex) ); \
}

#define GetRWTex1DArrDimensions_2(Tex, Width, Elements)\
{                                   \
    ivec2 i2Size = imageSize(Tex);  \
    _TypeConvertStore( Width,    i2Size.x ); \
    _TypeConvertStore( Elements, i2Size.y ); \
}

#define GetRWTex2DDimensions_2(Tex, Width, Height)\
{                                           \
    ivec2 i2Size = imageSize(Tex);          \
    _TypeConvertStore( Width,  i2Size.x );  \
    _TypeConvertStore( Height, i2Size.y );  \
}

#define GetRWTex2DArrDimensions_3(Tex, Width, Height, Elements)\
{                                           \
    ivec3 i3Size = imageSize(Tex);          \
    _TypeConvertStore( Width,    i3Size.x );\
    _TypeConvertStore( Height,   i3Size.y );\
    _TypeConvertStore( Elements, i3Size.z );\
}

#define GetRWTex3DDimensions_3(Tex, Width, Height, Depth)\
{                                           \
    ivec3 i3Size = imageSize(Tex);          \
    _TypeConvertStore( Width,  i3Size.x );  \
    _TypeConvertStore( Height, i3Size.y );  \
    _TypeConvertStore( Depth,  i3Size.z );  \
}



// Texture sampling operations


//                             IMPORTANT NOTE ABOUT OFFSET
// Offset parameter to all texture sampling functions must be a constant expression.
// If it is not, the shader will be successfully compiled, HOWEVER the value of Offset 
// will silently be zero. 
//
// A constant expression in GLSL is defined as follows:
// * A literal value.
// * A const-qualified variable with an explicit initializer (so not a function parameter).
// * The result of the length() function of an array, but only if the array has an explicit size.
// * The result of most operators, so long as all the operands are themselves constant expressions. 
//   The operators not on this list are any assignment operators (+= and so forth), and the comma operator.
// * The result of a constructor for a type, but only if all of the arguments to the constructor are 
//   themselves constant expressions.
// * The return value of any built-in function, but only if all of the arguments to the function are 
//   themselves constant expressions. Opaque Types are never constant expressions. Note that the 
//   functions dFdx, dFdy, and fwidth will return 0, when used in a context that requires a constant 
//   expression (such as a const variable initializer).
// 
// The list above does not include return value of a function, even when the value is compile-time expression.
// As a result, we cannot use type conversion functions for Offset parameter.

// In all texture sampling functions, the last component of Coords is used as Dsub and the array layer is specified 
// in the second to last component of Coords. (The second component of Coords is unused for 1D shadow lookups.)
// For cube array textures, Dsub is specified as a separate parameter
//                                                                                                                                     mip
#define SampleCmpLevel0Tex1D_3(Tex, Sampler, Coords, CompareValue)      textureLod(Tex, _ToVec3( Coords,           0.0, CompareValue), 0.0)
#define SampleCmpLevel0Tex1DArr_3(Tex, Sampler, Coords, CompareValue)   textureLod(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue), 0.0)
#define SampleCmpLevel0Tex2D_3(Tex, Sampler, Coords, CompareValue)      textureLod(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue), 0.0)
#define SampleCmpLevel0Tex2DArr_3(Tex, Sampler, Coords, CompareValue)   0.0 // No textureLod for sampler2DArrayShadow
#define SampleCmpLevel0TexCube_3(Tex, Sampler, Coords, CompareValue)    0.0 // No textureLod for samplerCubeShadow
#define SampleCmpLevel0TexCubeArr_3(Tex, Sampler, Coords, CompareValue) 0.0 // No textureLod for samplerCubeArrayShadow

//                                                                                                                                                 mip
#define SampleCmpLevel0Tex1D_4(Tex, Sampler, Coords, CompareValue, Offset)    textureLodOffset(Tex, _ToVec3( Coords,           0.0, CompareValue), 0.0, int(Offset))
#define SampleCmpLevel0Tex1DArr_4(Tex, Sampler, Coords, CompareValue, Offset) textureLodOffset(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue), 0.0, int(Offset))
#define SampleCmpLevel0Tex2D_4(Tex, Sampler, Coords, CompareValue, Offset)    textureLodOffset(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue), 0.0, ivec2((Offset).xy))
#define SampleCmpLevel0Tex2DArr_4(Tex, Sampler, Coords, CompareValue, Offset) 0.0 // No textureLodOffset for sampler2DArrayShadow


// https://www.opengl.org/sdk/docs/man/html/texture.xhtml - note: there are many mistakes on the page
#ifdef FRAGMENT_SHADER

#   define Sample_2(Tex, Sampler, Coords)         texture      (Tex, _ToVec(Coords))
#   define Sample_3(Tex, Sampler, Coords, Offset) textureOffset(Tex, _ToVec(Coords), Offset)
#   define SampleBias_3(Tex, Sampler, Coords, Bias)         texture      (Tex, _ToVec(Coords), _ToFloat(Bias))
#   define SampleBias_4(Tex, Sampler, Coords, Bias, Offset) textureOffset(Tex, _ToVec(Coords), Offset, _ToFloat(Bias))

#   define SampleCmpTex1D_3(Tex, Sampler, Coords, CompareValue)      texture(Tex, _ToVec3( Coords,           0.0, CompareValue))
#   define SampleCmpTex1DArr_3(Tex, Sampler, Coords, CompareValue)   texture(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue))
#   define SampleCmpTex2D_3(Tex, Sampler, Coords, CompareValue)      texture(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue))
#   define SampleCmpTex2DArr_3(Tex, Sampler, Coords, CompareValue)   texture(Tex, _ToVec4((Coords).x, (Coords).y, (Coords).z, CompareValue))
#   define SampleCmpTexCube_3(Tex, Sampler, Coords, CompareValue)    texture(Tex, _ToVec4((Coords).x, (Coords).y, (Coords).z, CompareValue))
#   define SampleCmpTexCubeArr_3(Tex, Sampler, Coords, CompareValue) texture(Tex, _ToVec4((Coords).x, (Coords).y, (Coords).z, (Coords).w), _ToFloat(CompareValue))

#   define SampleCmpTex1D_4(Tex, Sampler, Coords, CompareValue, Offset)    textureOffset(Tex, _ToVec3( Coords,           0.0, CompareValue), int(Offset))
#   define SampleCmpTex1DArr_4(Tex, Sampler, Coords, CompareValue, Offset) textureOffset(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue), int(Offset))
#   define SampleCmpTex2D_4(Tex, Sampler, Coords, CompareValue, Offset)    textureOffset(Tex, _ToVec3((Coords).x, (Coords).y, CompareValue), ivec2((Offset).xy))
#   define SampleCmpTex2DArr_4(Tex, Sampler, Coords, CompareValue, Offset) textureOffset(Tex, _ToVec4((Coords).x, (Coords).y, (Coords).z, CompareValue), ivec2((Offset).xy))

#else

// Derivatives are only available in fragment shader. GLSL compiler fails when it
// encounters texture() or textureOffset() instructions in other types of shaders. So
// to let the shader be compiled and to have something meaningful, replace such operations
// with textureLod() and textureLodOffset()

#   define Sample_2(Tex, Sampler, Coords)         textureLod      (Tex, _ToVec(Coords), 0.0)
#   define Sample_3(Tex, Sampler, Coords, Offset) textureLodOffset(Tex, _ToVec(Coords), 0.0, Offset)
#   define SampleBias_3(Tex, Sampler, Coords, Bias)         textureLod      (Tex, _ToVec(Coords), 0.0 + _ToFloat(Bias))
#   define SampleBias_4(Tex, Sampler, Coords, Bias, Offset) textureLodOffset(Tex, _ToVec(Coords), 0.0 + _ToFloat(Bias), Offset)

#   define SampleCmpTex1D_3        SampleCmpLevel0Tex1D_3
#   define SampleCmpTex1DArr_3     SampleCmpLevel0Tex1DArr_3
#   define SampleCmpTex2D_3        SampleCmpLevel0Tex2D_3
#   define SampleCmpTex2DArr_3     SampleCmpLevel0Tex2DArr_3
#   define SampleCmpTexCube_3      SampleCmpLevel0TexCube_3
#   define SampleCmpTexCubeArr_3   SampleCmpLevel0TexCubeArr_3
                                            
#   define SampleCmpTex1D_4        SampleCmpLevel0Tex1D_4
#   define SampleCmpTex1DArr_4     SampleCmpLevel0Tex1DArr_4
#   define SampleCmpTex2D_4        SampleCmpLevel0Tex2D_4
#   define SampleCmpTex2DArr_4     SampleCmpLevel0Tex2DArr_4

#endif

// https://www.opengl.org/sdk/docs/man/html/textureLod.xhtml
#define SampleLevel_3(Tex, Sampler, Coords, Level)         textureLod      (Tex, _ToVec(Coords), _ToFloat(Level))
#define SampleLevel_4(Tex, Sampler, Coords, Level, Offset) textureLodOffset(Tex, _ToVec(Coords), _ToFloat(Level), Offset)

// https://www.opengl.org/sdk/docs/man/html/textureGrad.xhtml
#define SampleGrad_4(Tex, Sampler, Coords, DDX, DDY)         textureGrad      (Tex, _ToVec(Coords), _ToVec(DDX), _ToVec(DDY))
#define SampleGrad_5(Tex, Sampler, Coords, DDX, DDY, Offset) textureGradOffset(Tex, _ToVec(Coords), _ToVec(DDX), _ToVec(DDY), Offset)


// texelFetch performs a lookup of a single texel from texture coordinate P in the texture 
// bound to sampler. The array layer is specified in the last component of P for array forms. 
// The lod parameter (if present) specifies the level-of-detail from which the texel will be fetched. 
// The sample specifies which sample within the texel will be returned when reading from a multi-sample texure.

#define LoadTex1D_1(Tex, Location)        texelFetch      (Tex, _ToInt((Location).x), _ToInt((Location).y))
#define LoadTex1D_2(Tex, Location, Offset)texelFetchOffset(Tex, _ToInt((Location).x), _ToInt((Location).y), int(Offset))
#define LoadTex1DArr_1(Tex, Location)        texelFetch      (Tex, _ToIvec( (Location).xy), _ToInt((Location).z) )
#define LoadTex1DArr_2(Tex, Location, Offset)texelFetchOffset(Tex, _ToIvec( (Location).xy), _ToInt((Location).z), int(Offset))
#define LoadTex2D_1(Tex, Location)        texelFetch      (Tex, _ToIvec( (Location).xy), _ToInt((Location).z))
#define LoadTex2D_2(Tex, Location, Offset)texelFetchOffset(Tex, _ToIvec( (Location).xy), _ToInt((Location).z), ivec2( (Offset).xy) )
#define LoadTex2DArr_1(Tex, Location)        texelFetch      (Tex, _ToIvec( (Location).xyz), _ToInt((Location).w) )
#define LoadTex2DArr_2(Tex, Location, Offset)texelFetchOffset(Tex, _ToIvec( (Location).xyz), _ToInt((Location).w), ivec2( (Offset).xy))
#define LoadTex3D_1(Tex, Location)        texelFetch      (Tex, _ToIvec( (Location).xyz), _ToInt((Location).w))
#define LoadTex3D_2(Tex, Location, Offset)texelFetchOffset(Tex, _ToIvec( (Location).xyz), _ToInt((Location).w), ivec3( (Offset).xyz))
#define LoadTex2DMS_2(Tex, Location, Sample)        texelFetch(Tex, _ToIvec( (Location).xy), _ToInt(Sample))
#define LoadTex2DMS_3(Tex, Location, Sample, Offset)texelFetch(Tex, _ToIvec2( (Location).x + (Offset).x, (Location).y + (Offset).y), int(Sample) ) // No texelFetchOffset for texture2DMS
#define LoadTex2DMSArr_2(Tex, Location, Sample)        texelFetch(Tex, _ToIvec( (Location).xyz), _ToInt(Sample))
#define LoadTex2DMSArr_3(Tex, Location, Sample, Offset)texelFetch(Tex, _ToIvec3( (Location).x + (Offset).x, (Location).y + (Offset).y, (Location).z), int(Sample)) // No texelFetchOffset for texture2DMSArray

//https://www.opengl.org/sdk/docs/man/html/imageLoad.xhtml
#define LoadRWTex1D_1(Tex, Location)    imageLoad(Tex, _ToInt(Location)        )
#define LoadRWTex1DArr_1(Tex, Location) imageLoad(Tex, _ToIvec((Location).xy)  )
#define LoadRWTex2D_1(Tex, Location)    imageLoad(Tex, _ToIvec((Location).xy)  )
#define LoadRWTex2DArr_1(Tex, Location) imageLoad(Tex, _ToIvec((Location).xyz) )
#define LoadRWTex3D_1(Tex, Location)    imageLoad(Tex, _ToIvec((Location).xyz) )


#define Gather_2(Tex, Sampler, Location)        textureGather      (Tex, _ToVec(Location))
#define Gather_3(Tex, Sampler, Location, Offset)textureGatherOffset(Tex, _ToVec(Location), Offset)

#define GatherCmp_3(Tex, Sampler, Location, CompareVal)        textureGather      (Tex, _ToVec(Location), _ToFloat(CompareVal))
#define GatherCmp_4(Tex, Sampler, Location, CompareVal, Offset)textureGatherOffset(Tex, _ToVec(Location), _ToFloat(CompareVal), Offset)

// Atomic operations
#define InterlockedAddSharedVar_2(dest, value)                      atomicAdd(dest, value)
#define InterlockedAddSharedVar_3(dest, value, orig_val) orig_val = atomicAdd(dest, value)
#define InterlockedAddImage_2(img, coords, value)                     imageAtomicAdd(img, _ToIvec(coords), value)
#define InterlockedAddImage_3(img, coords, value, orig_val)orig_val = imageAtomicAdd(img, _ToIvec(coords), value)

#define InterlockedAndSharedVar_2(dest, value)                      atomicAnd(dest, value)
#define InterlockedAndSharedVar_3(dest, value, orig_val) orig_val = atomicAnd(dest, value)
#define InterlockedAndImage_2(img, coords, value)                     imageAtomicAnd(img, _ToIvec(coords), value)
#define InterlockedAndImage_3(img, coords, value, orig_val)orig_val = imageAtomicAnd(img, _ToIvec(coords), value)

#define InterlockedMaxSharedVar_2(dest, value)                      atomicMax(dest, value)
#define InterlockedMaxSharedVar_3(dest, value, orig_val) orig_val = atomicMax(dest, value)
#define InterlockedMaxImage_2(img, coords, value)                     imageAtomicMax(img, _ToIvec(coords), value)
#define InterlockedMaxImage_3(img, coords, value, orig_val)orig_val = imageAtomicMax(img, _ToIvec(coords), value)

#define InterlockedMinSharedVar_2(dest, value)                      atomicMin(dest, value)
#define InterlockedMinSharedVar_3(dest, value, orig_val) orig_val = atomicMin(dest, value)
#define InterlockedMinImage_2(img, coords, value)                     imageAtomicMin(img, _ToIvec(coords), value)
#define InterlockedMinImage_3(img, coords, value, orig_val)orig_val = imageAtomicMin(img, _ToIvec(coords), value)

#define InterlockedOrSharedVar_2(dest, value)                      atomicOr(dest, value)
#define InterlockedOrSharedVar_3(dest, value, orig_val) orig_val = atomicOr(dest, value)
#define InterlockedOrImage_2(img, coords, value)                     imageAtomicOr(img, _ToIvec(coords), value)
#define InterlockedOrImage_3(img, coords, value, orig_val)orig_val = imageAtomicOr(img, _ToIvec(coords), value)

#define InterlockedXorSharedVar_2(dest, value)                      atomicXor(dest, value)
#define InterlockedXorSharedVar_3(dest, value, orig_val) orig_val = atomicXor(dest, value)
#define InterlockedXorImage_2(img, coords, value)                     imageAtomicXor(img, _ToIvec(coords), value)
#define InterlockedXorImage_3(img, coords, value, orig_val)orig_val = imageAtomicXor(img, _ToIvec(coords), value)

// There is actually no InterlockedExchange() with 2 arguments
#define InterlockedExchangeSharedVar_2(dest, value)                      atomicExchange(dest, value)
#define InterlockedExchangeSharedVar_3(dest, value, orig_val) orig_val = atomicExchange(dest, value)
#define InterlockedExchangeImage_2(img, coords, value)                     imageAtomicExchange(img, _ToIvec(coords), value)
#define InterlockedExchangeImage_3(img, coords, value, orig_val)orig_val = imageAtomicExchange(img, _ToIvec(coords), value)

//uint imageAtomicCompSwap(	image img,    IVec P,       nint compare,   nint data);
//void InterlockedCompareExchange(     in R dest, in T compare_value, in  T value, out T original_value);
#define InterlockedCompareExchangeSharedVar_4(dest, cmp_val, value, orig_val)  orig_val = atomicCompSwap(dest, cmp_val, value)
#define InterlockedCompareExchangeImage_4(img, coords, cmp_val, value, orig_val) orig_val = imageAtomicCompSwap(img, _ToIvec(coords), cmp_val, value)

#define InterlockedCompareStoreSharedVar_3(dest, cmp_val, value) atomicCompSwap(dest, cmp_val, value)
#define InterlockedCompareStoreImage_3(img, coords, cmp_val, value)imageAtomicCompSwap(img, _ToIvec(coords), cmp_val, value)


// Swizzling macros
#define _SWIZZLE0
#define _SWIZZLE1 .x
#define _SWIZZLE2 .xy
#define _SWIZZLE3 .xyz
#define _SWIZZLE4 .xyzw

// Helper functions

float2 NormalizedDeviceXYToTexUV( float2 f2ProjSpaceXY )
{
    return float2(0.5,0.5) + float2(0.5,0.5) * f2ProjSpaceXY.xy;
}

float NormalizedDeviceZToDepth(float fNDC_Z)
{
    return fNDC_Z * 0.5 + 0.5; // [-1, +1] -> [0, 1]
}

float DepthToNormalizedDeviceZ(float fDepth)
{
    return fDepth * 2.0 - 1.0; // [0, 1] -> [-1, +1]
}

#define F3NDC_XYZ_TO_UVD_SCALE float3(0.5, 0.5, 0.5)

#define NDC_MIN_Z -1.0 // Minimal z in the normalized device space

#define MATRIX_ELEMENT(mat, row, col) mat[col][row]

float4x4 MatrixFromRows(float4 row0, float4 row1, float4 row2, float4 row3)
{
    return transpose(float4x4(row0, row1, row2, row3));
}

float3x3 MatrixFromRows(float3 row0, float3 row1, float3 row2)
{
    return transpose(float3x3(row0, row1, row2));
}

float2x2 MatrixFromRows(float2 row0, float2 row1)
{
    return transpose(float2x2(row0, row1));
}

// ---------------------------------- Vertex shader ----------------------------------
#ifdef VERTEX_SHADER

#ifndef GL_ES
out gl_PerVertex
{
    vec4 gl_Position;
};
#endif

#define _GET_GL_VERTEX_ID(VertexId)_TypeConvertStore(VertexId, gl_VertexID)
#define _GET_GL_INSTANCE_ID(InstId)_TypeConvertStore(InstId, gl_InstanceID)
#define _SET_GL_POSITION(Pos)gl_Position=_ExpandVector(Pos)

#endif


// --------------------------------- Fragment shader ---------------------------------
#ifdef FRAGMENT_SHADER

#define _GET_GL_FRAG_COORD(FragCoord)_ResizeVector(FragCoord, gl_FragCoord)
#define _SET_GL_FRAG_DEPTH(Depth)_TypeConvertStore(gl_FragDepth, Depth)

#endif


// --------------------------------- Geometry shader ---------------------------------
#ifdef GEOMETRY_SHADER

// ARB_separate_shader_objects requires built-in block gl_PerVertex to be redeclared before accessing its members
// declaring gl_PointSize and gl_ClipDistance causes compilation error on Android
in gl_PerVertex
{
    vec4 gl_Position;
    //float gl_PointSize;
    //float gl_ClipDistance[];
} gl_in[];

out gl_PerVertex
{
    vec4 gl_Position;
    //float gl_PointSize;
    //float gl_ClipDistance[];
};

#define _GET_GL_POSITION(Pos)_ResizeVector(Pos, gl_in[i].gl_Position)
#define _GET_GL_PRIMITIVE_ID(PrimId)_TypeConvertStore(PrimId, gl_in[i].gl_PrimitiveIDIn)

#define _SET_GL_POSITION(Pos)gl_Position=_ExpandVector(Pos)
#define _SET_GL_LAYER(Layer)_TypeConvertStore(gl_Layer,Layer)

#endif


// --------------------------- Tessellation control shader ---------------------------
#ifdef TESS_CONTROL_SHADER

in gl_PerVertex
{
    vec4 gl_Position;
    //float gl_PointSize;
    //float gl_ClipDistance[];
} gl_in[gl_MaxPatchVertices];

out gl_PerVertex
{
    vec4 gl_Position;
    //float gl_PointSize;
    //float gl_ClipDistance[];
} gl_out[];

#define _GET_GL_INVOCATION_ID(InvocId)_TypeConvertStore(InvocId, gl_InvocationID)
#define _GET_GL_PRIMITIVE_ID(PrimId)_TypeConvertStore(PrimId, gl_PrimitiveID)
#define _GET_GL_POSITION(Pos)_ResizeVector(Pos, gl_in[i].gl_Position)

#define _SET_GL_POSITION(Pos)gl_out[gl_InvocationID].gl_Position=_ExpandVector(Pos)

void _SetGLTessLevelOuter(float OuterLevel[2])
{
    for(int i=0; i < 2; ++i)
        gl_TessLevelOuter[i] = OuterLevel[i];
}
void _SetGLTessLevelOuter(float OuterLevel[3])
{
    for(int i=0; i < 3; ++i)
        gl_TessLevelOuter[i] = OuterLevel[i];
}
void _SetGLTessLevelOuter(float OuterLevel[4])
{
    for(int i=0; i < 4; ++i)
        gl_TessLevelOuter[i] = OuterLevel[i];
}


void _SetGLTessLevelInner(float InnerLevel[2])
{
    gl_TessLevelInner[0] = InnerLevel[0];
    gl_TessLevelInner[1] = InnerLevel[1];
}
void _SetGLTessLevelInner(float InnerLevel)
{
    gl_TessLevelInner[0] = InnerLevel;
}

#endif


// --------------------------- Tessellation evaluation shader ---------------------------
#ifdef TESS_EVALUATION_SHADER

in gl_PerVertex
{
    vec4 gl_Position;
    //float gl_PointSize;
    //float gl_ClipDistance[];
} gl_in[gl_MaxPatchVertices];

out gl_PerVertex
{
    vec4 gl_Position;
    //float gl_PointSize;
    //float gl_ClipDistance[];
};

#define _GET_GL_POSITION(Pos)_ResizeVector(Pos, gl_in[i].gl_Position)

void _GetGLTessLevelOuter(out float OuterLevel[2])
{
    for(int i=0; i < 2; ++i)
        OuterLevel[i] = gl_TessLevelOuter[i];
}
void _GetGLTessLevelOuter(out float OuterLevel[3])
{
    for(int i=0; i < 3; ++i)
        OuterLevel[i] = gl_TessLevelOuter[i];
}
void _GetGLTessLevelOuter(out float OuterLevel[4])
{
    for(int i=0; i < 4; ++i)
        OuterLevel[i] = gl_TessLevelOuter[i];
}

void _GetGLTessLevelInner(out float InnerLevel[2])
{
    InnerLevel[0] = gl_TessLevelInner[0];
    InnerLevel[1] = gl_TessLevelInner[1];
}
void _GetGLTessLevelInner(out float InnerLevel)
{
    InnerLevel = gl_TessLevelInner[0];
}

#define _GET_GL_TESS_COORD(TessCoord)_ResizeVector(TessCoord, gl_TessCoord)
#define _GET_GL_PRIMITIVE_ID(PrimId)_TypeConvertStore(PrimId, gl_PrimitiveID)

#define _SET_GL_POSITION(Pos)gl_Position=_ExpandVector(Pos)

#endif


// ---------------------------------- Compute shader ----------------------------------
#ifdef COMPUTE_SHADER

#define _GET_GL_GLOBAL_INVOCATION_ID(Type, InvocId)InvocId=Type(gl_GlobalInvocationID)
#define _GET_GL_WORK_GROUP_ID(Type, GroupId)GroupId=Type(gl_WorkGroupID)
#define _GET_GL_LOCAL_INVOCATION_ID(Type, InvocId)InvocId=Type(gl_LocalInvocationID)
#define _GET_GL_LOCAL_INVOCATION_INDEX(Type, InvocInd)InvocInd=Type(gl_LocalInvocationIndex)

#endif

#endif // _GLSL_DEFINITIONS_
