Multiplatform Shader Compilation

So I’m trying to figure out how compiling shaders to different platforms work in monogame. I understand it uses a tool to compile hlsl to glsl, which means they can share the same shader file. However compiling to Windows 8 and the direct x target results in a new version of hlsl syntax, causing the compilation to fail. It seems that between Windows 8 and Direct X there’s a slightly different syntax required than with the rest of the platforms Monogame supports. Is there a solution anyone has so that the same shader file can be used for every platform?

We use a single shader for all platforms. We only need to compile it in two versions: one for DX platforms and one OpenGL platforms.

What syntax are you having troubles with?

Using a MonoGame Content project, anything that builds to OpenGL needs the Vertex and Pixel Shaders to be compiled at 2_0 version, whereas DirectX requires them to be at 4_0_level_9_1. When I switched the the DirectX compilation it also says I need to use SV_Position for any position declarations, which isn’t recognized in the OpenGL version. It’s possible I’m just doing/understanding this completely wrong, at which point I’d love to know how to do it properly!

Yes, you have to change the level to 2_0, but otherwise this works for us:
float4 main(float4 position : SV_Position, float4 color : COLOR0, float2 texCoord : TEXCOORD0) : COLOR0

So, that’ll build fine, but when I try to run it in the Monogame DirectX project, it tells me “The MGFX effect is the wrong profile for this platform!”. That’s if I build the shader for use on the Windows platform. If I switch it to build for the Windows8 platform, I get ‘Pixel shader must be SM 4.0 level 9.1 or higher!’. If I switch it to 4_0, then it builds and loads perfectly fine.

So compile one with 2_0 for OpenGL and one with 4_0 for DirectX.

Well my original question for this topic was if it was possible to get the different compiled versions from one file, without having to change the file for the different platforms. I’m now wondering if I can use ifdefs to determine what platform it is, and use the proper compiled version there. I’ll look into that, thanks!

The simple solution is to use macros. Here’s an example of one of my shader pass definitions:

technique GaussianBlur
{
    pass HorizontalBlur
    {
#if SM4
        PixelShader = compile ps_4_0_level_9_1 ps_GaussianBlurHorizontal();
#elif SM3
        PixelShader = compile ps_3_0 ps_GaussianBlurHorizontal();
#else
        PixelShader = compile ps_2_0 ps_GaussianBlurHorizontal();
#endif
    }

    pass VerticalBlur
    {
#if SM4
        PixelShader = compile ps_4_0_level_9_1 ps_GaussianBlurVertical();
#elif SM3
        PixelShader = compile ps_3_0 ps_GaussianBlurVertical();
#else
        PixelShader = compile ps_2_0 ps_GaussianBlurVertical();
#endif
    }
}