Search Unity

Material's shader recompiled event

Discussion in 'Shaders' started by svobodajak, Jun 17, 2020.

  1. svobodajak

    svobodajak

    Joined:
    Jun 27, 2017
    Posts:
    11
    Hi, I'd like to ask whether there is some way of knowing that a particular Material instance's shader was recompiled. Or if any shader was just recompiled would be okay. Something like https://docs.unity3d.com/ScriptReference/Callbacks.DidReloadScripts.html but for shaders.

    I was searching for an answer, but couldn't find any, the closest I got was: https://answers.unity.com/questions/173209/notification-of-shader-recompilation-.html, but no one gave a definitive answer.

    My use-case is similar to the question I linked to. I have some shader properties that I set through material.SetBuffer that change very infrequently, so I don't want to set them every frame.
     
  2. Xilliah

    Xilliah

    Joined:
    Feb 24, 2021
    Posts:
    4
    You can use FileSystemWatcher, but unfortunately it seems to be borked under Linux. Here's what I use:

    ShaderChangedDispatcherPostprocessor.cs
    Code (CSharp):
    1.  
    2. using UnityEditor;
    3.  
    4. namespace Packages.VonElise.Editor
    5. {
    6.     public class ShaderChangedDispatcherPostprocessor : AssetPostprocessor
    7.     {
    8.         private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets,
    9.             string[] movedFromAssetPaths)
    10.         {
    11.             ShaderChangedDispatcher.OnPostprocessImportedAssets(importedAssets);
    12.         }
    13.     }
    14. }
    15.  

    ShaderChangedDispatcher.cs

    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections.Generic;
    4. using System.IO;
    5. using System.Linq;
    6. using UnityEngine;
    7. using Object = UnityEngine.Object;
    8. // ReSharper disable EventNeverSubscribedTo.Global
    9.  
    10. namespace Packages.VonElise
    11. {
    12.     public static class ShaderChangedDispatcher
    13.     {
    14.         public static event Action<Shader> OnShaderChanged;
    15.         public static event Action<ComputeShader> OnComputeShaderChanged;
    16.      
    17. #if UNITY_EDITOR
    18.         public static void OnPostprocessImportedAssets(string[] importedAssets)
    19.         {
    20.             CallOnAssetChanged(importedAssets, ".shader", OnShaderChanged);
    21.             CallOnAssetChanged(importedAssets, ".compute", OnComputeShaderChanged);
    22.         }
    23.      
    24.         private static void CallOnAssetChanged<T>(IEnumerable<string> importedAssets, string extension, Action<T> callEvent) where T : Object
    25.         {
    26.             if (callEvent == null) return;
    27.          
    28.             var assetPaths = importedAssets.Where(path => Path.GetExtension(path) == extension);
    29.             var assets = assetPaths.Select(UnityEditor.AssetDatabase.LoadAssetAtPath<T>);
    30.          
    31.             foreach (var asset in assets)
    32.                 callEvent(asset);
    33.         }
    34. #endif
    35.     }
    36. }
    37.  
    In your script:
    Code (CSharp):
    1.  
    2.             ShaderChangedDispatcher.OnComputeShaderChanged += shader =>
    3.             {
    4.                 if (shader == computeShader) DeInit();
    5.             };
    6.  
    In this case DeInit() releases the buffers, and in the next update it will automatically recreate them. At least you'll want to set the buffers again.

    Furthermore you'll want to implement ISerializationCallbackReceiver and use OnBeforeSerialize() to DeInit() your buffers and so on. That way your shader will keep working when you save in the editor.

    You'll also want to DeInit() from OnDisable() and not from OnDestroy(). That way you'll also properly handle the case where the assembly is reloaded after you change your script. There are also specific assembly-reload callbacks somewhere in case you want to get fancy.
     
    Last edited: Jun 18, 2023