Runtime Shader Modification and Chaining

So I’m trying to get a runtime version of a node based shader editor shipping with my game. The idea is I’d have a bunch of pre-built shaders that represent simple effects/operations, and the user could chain them together to get a final composite effect. My first thought was just to create a tree of the effects, and then apply them all in order to the model as its drawn. However it seems like it’s not possible to chain the effects of multiple shaders together on a single model. My second thought was to either compile the composite shader at runtime, or modify an existing one with all the different steps, but that doesn’t seem possible either. Does anyone have an idea of how I could go about doing this? I seem to be stuck on this.
Thanks so much!

@tgjones has something similar from XNA. Might be useful.

Excellent! That approach seems to be exactly what I need to allow modifications of the shader. I’m going to look more into that and get that integrated in.

The only remaining problem then is to get this working at runtime. Even the solution above uses the content pipeline to compile the shaders, seeing as it relies on editing the uncompiled shader. Does anybody know if runtime compilation of shaders is supported in MonoGame? I’ve looked around a lot and the answer seems to be no, but maybe I missed something.

How complicated is the compiled shader format? Would it be possible to search and do matches for the fragments in the compiled version, and add or remove steps to modify the behavior? Any other thoughts would be greatly appreciated!

If you link the content pipeline into your game it is. Beyond that it does not support it.

This is the recommended way to do this in both MonoGame and XNA itself.

Depends on the platform you are targeting. If you are targeting a GL platform it is currently just GLSL code. If you are targeting a DirectX platform it is a HLSL assembly file. If you are targeting a PSVita or a PS4 or some other console it is whatever binary format is optimal for that platform.

Would linking the content pipeline work on all platforms like android and ios? Or is it only supported on windows?

No it wouldn’t. The pipeline is designed to run on desktop systems… Windows, Mac, and Linux only.

There are two general problems with shader compilation at runtime:

  1. Some platforms don’t support compiling shaders at runtime on the target device.
  2. Shader compilers are generally really slow for non-trivial shaders.

These issues may never be fixed entirely which is why MonoGame and XNA (and even Unity and Unreal) choose for offline compilation as the default path. This decision is made with performance and portability to all the target platforms in mind.

All that said we could start thinking about improvements to the effect compiler (2mgfx) in particular to allow it to run on the target device in cases where that is possible to do. It might not be fast if it works at all… but it could work in some cases.

Still offline compiling of effects will always be our default path in MonoGame.

Is there any way of getting shader compilation working from a windows UWP project during runtime?

I have tried porting 2mgfx to UWP using sharpdx 3.1 as the backend but have failed so far as it seems to be a much larger task than I first assumed. My current round about solution is to use a web server that invokes the MGCB and then returns the results, but I am yet to write this and I don’t know how to handle shader includes with this system.

We have just ported the last of our large projects to Monogame, but not having live shader editing anymore is killing me…

What are you stuck on? Maybe I can point you in the right direction.

You should be able to run MGCB.exe from the command line from any Windows app to rebuild a shader. That is the path I would look to take.

I solved getting live shader reloading in my UWP app by running a small web service that compiles a shader on request from the UWP application.

It works better than I thought it would :slight_smile:

I might clean it up sometime if there is any desire for it be others.

Any guidelines/docs on how to link the content pipeline into the game, and then call the shader compilation steps?

Using this as a I guide I’ve gotten as far as a compiledeffectcontent, but how do I get to an effect?

if you have your shader, save it as a *.fx file and load it with MGCB.exe

Here is the way i do it, but you will easier understand it if you google how to start applications from inside the app (with Process) and read the MGCB documentation

string mgcbPathExe = Application.StartupPath + "/Content/MGCB/mgcb.exe";

                    //Create pProcess
                    Process pProcess = new Process
                        StartInfo =
                            FileName = mgcbPathExe,
                            Arguments = "/@:\"Content/mgcb/" + pipeLineFile + "\""
                            +" /build:\"" + fileName /*completeFilePath*/+ "\"",
                            CreateNoWindow = true,
                            WorkingDirectory = Application.StartupPath,
                            UseShellExecute = false,
                            RedirectStandardError = true,
                            RedirectStandardOutput = true

                    //Get program output
                    string stdError = null;
                    StringBuilder stdOutput = new StringBuilder();
                    pProcess.OutputDataReceived += (sender, args) => stdOutput.Append( args.Data );

                        stdError = pProcess.StandardError.ReadToEnd();

Yeah it looks like the command line approach will be less pain overall, since this is for debug only it will work fine. I was making progress but having to include more and more DLLs, conditionally.

So I have the shader successfully building, I see the .xnb file getting output, I copy it to the execution directory, but when I call Content.Load, I don’t see any change. Does Content.Load do some caching behind the scenes? looks at source code yep! it does. How can I get the ContentManager to unload the effect? Calling dispose doesn’t seem to do it. I think I am going to have to do something silly like alias the shader names, or reload ALL the content. :frowning:

You can modify the content manager to allow unloading or, and that’s what I would do, use a separate content manager for that effect and unload that before reloading the new effect.
Still better than name-aliasing imo.

Thank you, yes just as you were typing this I figured out I can use a separate ContentManager for hot swappable shaders, and just clear it out and reload all of them each time. Working now. I am very excited!

Nice to hear. Good luck.