GLSL to Cg Conversion Cheat-Sheet

Since GLSL is still more common than Cg for generic code snippets on the Web, and some existing shaders still aren’t converted, here are some resources to help with conversion to RetroArch’s preferred Cg format.

Thankfully, with pixel shaders, the actual code is pretty much just math, so most of it can be reused with little-to-no modification. The surrounding framework, though, can be very different across languages. This is a generic, annotated Cg shader framework:

/* COMPATIBILITY 
   - HLSL compilers
   - Cg   compilers
*/

// This is a good place for any #defines you might have

// VERTEX SHADER //

void main_vertex
(
    float4 position    : POSITION,
    float2 texCoord : TEXCOORD0,

    uniform float4x4 modelViewProj,

    out float4 oPosition : POSITION,
    out float2 otexCoord : TEXCOORD
)
{
    oPosition = mul(modelViewProj, position);
    otexCoord = texCoord;

// Paste your vertex code--the part that is inside the GLSL shader's "void main(){ ... }"--here:
}

struct input      // These are things that can attach to 'IN' to achieve similar function to ruby* stuff in existing GLSL shaders. NOTE: the ruby* prefix is deprecated but still works in GLSL for legacy compatibility purposes.
{
  float2 video_size;     // <- rubyInputSize
  float2 texCoord_size;
  float2 output_size;     // <- rubyOutputSize
  float frame_count;     // <- rubyFrameCount
  float frame_direction;     // <- rubyFrameDirection
  float frame_rotation;
  float2 texture_size;     // <- rubyTextureSize
  sampler2D texture : TEXUNIT0;     // <- texture2D(rubyTexture, gl_texCoord) can generally be replaced with tex2D(IN.texture, texCoord)
};

struct output 
{
  float4 col    : COLOR;
};

// FRAGMENT SHADER //

output main_fragment(in float2 texCoord : TEXCOORD0,
uniform input IN,
uniform sampler2D texture : TEXUNIT0
)
{
// Paste your fragment code--the part that is inside the GLSL shader's "void main(){ ... }"--here:

   output OUT;
   OUT.col = foo * bar; // <- Similar to gl_FragColor
   return OUT;
}

Generally, you can do a find-and-replace for vec2/3/4 and change them to float2/3/4 and they’ll work just fine. The main exception to this that I’ve found is for matrices, which go from something like this:

vec2 foo = mat2(1.0, 2.0, 3.0, 4.0);

to:

float2x2 foo = float2x2(1.0, 3.0, 2.0, 4.0);

Related to that, you will sometimes see such a matrix multiplied by texture coordinates, like this:

vec2 foo = mat2(1.0, 2.0, 3.0, 4.0) * texCoord;

which isn’t allowed. Instead, you need to separate this into two lines, like this:

float2x2 foo_matrix = float2x2(1.0, 3.0, 2.0, 4.0);
float2 foo = foo_matrix * texCoord;

You’ll also notice that the matrix ordering is row-major in Cg but column-major in GLSL, so keep that in mind^^

Another thing that comes up sometimes is the derivative functions ddx/ddy/fwidth, which are available in Cg but not in the profile we use. Instead, you should replace these with IN structs. For example, ddx/ddy becomes:

IN.video_size / (IN.output_size * IN.texture_size)

Referencing LUTs and Previous Shader Passes

Adding lookup textures (LUTs) requires the use of a Cg Preset (cgp) file. In XML GLSL shaders, the LUT is referenced at the beginning of the file, before the first vertex shader, like this:

<texture id="lut_name" file="lut_name.png" filter="linear"/>

In Cg, you would add something like this to the bottom of the cgp file:

textures = LUT_NAME1;LUT_NAME2
LUT_NAME1 = relative/path/to/file1.png
LUT_NAME1_linear = true
LUT_NAME2 = relative/path/to/file2.png
LUT_NAME2_linear = true

Then, in the shader files that reference the LUT(s), you would identify it/them in the main_fragment() and assign them a TEXUNIT, like this:

float4 main_fragment(
float2 texCoord : TEXCOORD0,
uniform input IN,
uniform sampler2D LUT_NAME1 : TEXUNIT1) : COLOR

Similarly, referencing previous passes in multipass shaders requires the same sort of TEXUNIT assignment, but also requires you to add structs before the main_fragment, like this:

struct prev_0
{
     sampler2D texture : TEXUNIT2;
};

struct prev_1
{
     sampler2D texture : TEXUNIT3;
};

...

float4 main_fragment(
float2 texCoord : TEXCOORD0,
uniform input IN,
uniform sampler2D LUT_NAME1 : TEXUNIT1,
uniform prev_0 PREV,
uniform prev_1 PREV1) : COLOR

In both cases, you can then reference the input structs for LUTs and/or previous passes, like this, for example:

foo = tex2D(LUT_NAME1.texture , texCoord) * tex2D(PREV1.texture , texCoord);

Miscellaneous Notes

The ‘saturate(X)’ function should be replaced with clamp(X, 0.0, 1.0).

While both GLSL and Cg have the mul() function, the order of the vertex and matrix are reversed, so a GLSL mul(foo, bar) would change to mul(bar, foo) in Cg.

If anyone has anything to add, let me know :slight_smile:

Cg and GLSL have different matrix ordering. GLSL is column-major, while Cg is row-major.


mat2(0.0, 1.0, 2.0, 3.0)

column-major
  |
  v
// 0 2
// 1 3

is equivalent to


float2x2(0.0, 2.0, 1.0, 3.0)

Row-major
---> 0 2
     1 3

in Cg.

Also, note that the ruby prefix in GLSL is deprecated.

Ah, I didn’t know that :open_mouth:

I’ll have to go revisit some of my conversions!

EDIT: I edited the first post to reflect.