Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Hot Swappable Shaders for MonoGame
HotSwap shader sytem for MonoGame
HotSwap code only exists for debug builds
Edit paths to match your project
Construct in your Initialize method
Add shaders in LoadContent (or whenever)
Call CheckForChanges in Update() or periodically however you like
mgcb.exe usually located in C:\Program Files (x86)\MSBuild\MonoGame\v3.0\Tools
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Content;
using System.IO;
using System.Diagnostics;
namespace PRPG{
public class ShaderManager {
ContentManager TempContent;
DateTime LastUpdate;
const string mgcbPathExe = "c:/projects/prpgcs/Content/MGCB/mgcb.exe";
const string contentPath = "c:/projects/prpgcs/Content";
public Dictionary<string, Effect> Shaders;
ContentManager Content;
GraphicsDevice Device;
public ShaderManager(ContentManager content, GraphicsDevice device) {
Content = content;
Device = device;
Shaders = new Dictionary<string, Effect>();
TempContent = new ContentManager(Content.ServiceProvider, Content.RootDirectory);
LastUpdate = DateTime.Now;
public void CheckForChanges() {
var files = Directory.GetFiles(contentPath, "*.fx");
foreach (var file in files) {
var t = File.GetLastWriteTime(file);
if (t > LastUpdate) {
LastUpdate = t;
public void ShaderChanged(string path) {
string name = Path.GetFileNameWithoutExtension(path);
Process pProcess = new Process {
StartInfo =
FileName = mgcbPathExe,
Arguments = "/platform:DesktopGL /config: /profile:Reach /compress:False /importer:EffectImporter /processor:EffectProcessor /processorParam:DebugMode=Auto /build:"+name+".fx",
CreateNoWindow = true,
WorkingDirectory = contentPath,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
//Get program output
string stdError = null;
StringBuilder stdOutput = new StringBuilder();
pProcess.OutputDataReceived += (sender, args) => stdOutput.Append(args.Data);
try {
stdError = pProcess.StandardError.ReadToEnd();
string builtPath = contentPath+"/" + name + ".xnb";
string movePath = "C:/projects/prpgcs/PRPG/bin/DesktopGL/AnyCPU/Debug/Content/" + name + ".xnb";
File.Copy(builtPath, movePath, true);
ContentManager newTemp = new ContentManager(TempContent.ServiceProvider, TempContent.RootDirectory);
var newShaders = new Dictionary<string, Effect>();
foreach (var shaderName in Shaders.Keys) {
var effect = newTemp.Load<Effect>(shaderName);
newShaders.Add(shaderName.ToLower(), effect);
TempContent = newTemp;
Shaders = newShaders;
catch (Exception e) {
//todo log
finally {
public Effect GetShader(string name) {
return Shaders[name.ToLower()];
public void AddShader(string name) {
if (!Shaders.ContainsKey(name.ToLower())) {
var shader = Content.Load<Effect>(name);
Shaders.Add(name.ToLower(), shader);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment