diff --git a/src/Fuse/FuseEffectInstance.cs b/src/Fuse/FuseEffectInstance.cs new file mode 100644 index 00000000..430347d2 --- /dev/null +++ b/src/Fuse/FuseEffectInstance.cs @@ -0,0 +1,98 @@ +using Stride.Core; +using Stride.Core.Diagnostics; +using Stride.Engine.Design; +using Stride.Games; +using Stride.Graphics; +using Stride.Rendering; +using Stride.Shaders; +using Stride.Shaders.Compiler; +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Fuse +{ + public class FuseEffectInstance : EffectInstance + { + private readonly ShaderSource shaderSource; + private readonly IDisposable subscriptions; + private EffectSystem effectSystem; + + public FuseEffectInstance(ShaderSource shaderSource, ParameterCollection parameters, IDisposable subscriptions) : base(null, parameters) + { + this.shaderSource = shaderSource; + this.subscriptions = subscriptions; + } + + protected override void Destroy() + { + subscriptions.Dispose(); + base.Destroy(); + } + + /// + /// Defines the effect parameters used when compiling this effect. + /// + public EffectCompilerParameters EffectCompilerParameters = EffectCompilerParameters.Default; + + public void Initialize(IServiceRegistry services) + { + this.effectSystem = services.GetSafeServiceAs(); + + var gameSettings = services.GetSafeServiceAs().Settings; + EffectCompilerParameters.ApplyCompilationMode(gameSettings.CompilationMode); + + var graphicsDevice = services.GetSafeServiceAs().GraphicsDevice; + EffectCompilerParameters.Platform = GraphicsDevice.Platform; + EffectCompilerParameters.Profile = graphicsDevice.Features.RequestedProfile; + } + + protected override void ChooseEffect(GraphicsDevice graphicsDevice) + { + var watch = Stopwatch.StartNew(); + + // Setup compilation parameters + var compilerParameters = new CompilerParameters + { + EffectParameters = EffectCompilerParameters, + }; + + foreach (var effectParameterKey in Parameters.ParameterKeyInfos) + { + if (effectParameterKey.Key.Type == ParameterKeyType.Permutation) + { + compilerParameters.SetObject(effectParameterKey.Key, Parameters.ObjectValues[effectParameterKey.BindingSlot]); + } + } + + var compiler = effectSystem.Compiler; + + // Workaround for Stride effect compiler cache bug + ShaderNodesUtil.MakeInMemoryShadersAvailableToTheSourceManager(compiler, shaderSource); + + // Compile the shader + var compilerResult = compiler.Compile(shaderSource, compilerParameters); + CheckResult(compilerResult); + + var bytecodeResult = compilerResult.Bytecode.WaitForResult(); + + CheckResult(bytecodeResult.CompilationLog); + + if (bytecodeResult.Bytecode is null) + throw new InvalidOperationException("EffectCompiler returned no shader and no compilation error."); + + effect = new Effect(graphicsDevice, bytecodeResult.Bytecode); + + Console.WriteLine($"Compile Time: {watch.ElapsedMilliseconds} ms for Shader {shaderSource}"); + } + + private static void CheckResult(LoggerResult compilerResult) + { + // Check errors + if (compilerResult.HasErrors) + { + throw new InvalidOperationException("Could not compile shader. See error messages." + compilerResult.ToText()); + } + } + } +} diff --git a/src/Fuse/Inputs.cs b/src/Fuse/Inputs.cs index 49a4e41c..5529e989 100644 --- a/src/Fuse/Inputs.cs +++ b/src/Fuse/Inputs.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text; using Fuse.compute; using Stride.Graphics; using Stride.Rendering; @@ -101,7 +102,9 @@ public override string ToString() public bool IsResource { get; } } - public abstract class AbstractInput : ShaderNode, IGpuInput where TParameterKeyType : ParameterKey where TParameterUpdaterType : ParameterUpdater + public abstract class AbstractInput : ShaderNode, IGpuInput + where TParameterKeyType : ParameterKey + where TParameterUpdaterType : ParameterUpdater { protected readonly TParameterUpdaterType Updater;// = new ValueParameterUpdater(); @@ -408,30 +411,4 @@ public override void OnUpdateName() SetFieldDeclaration(TypeHelpers.GetGpuType()); } } - /* - public class CompositionInput : AbstractInput, PermutationParameterUpdater> where T : IComputeNode - { - - public CompositionInput(SetVar value): base("input", false, value) - { - } - - protected override PermutationParameterUpdater CreateUpdater() - { - return new PermutationParameterUpdater(); - } - - protected override void OnPassContext(ShaderGeneratorContext nodeContext) - { - Updater.Track(nodeContext, ParameterKey); - } - - protected override void OnGenerateCode(ShaderGeneratorContext nodeContext) - { - ParameterKey = new PermutationParameterKey(ID); - SetFieldDeclaration(TypeHelpers.GetGpuTypeForType()); - } - - - }*/ } \ No newline at end of file diff --git a/src/Fuse/Properties/launchSettings.json b/src/Fuse/Properties/launchSettings.json index 0dbbaf3c..b8a1d596 100644 --- a/src/Fuse/Properties/launchSettings.json +++ b/src/Fuse/Properties/launchSettings.json @@ -2,12 +2,12 @@ "profiles": { "Normal startup": { "commandName": "Executable", - "executablePath": "C:\\Program Files\\vvvv\\vvvv_gamma_5.3-0251-gc882d50ce8\\vvvv.exe", + "executablePath": "C:\\Program Files\\vvvv\\vvvv_gamma_5.3-0346-g0a53f6c531\\vvvv.exe", "commandLineArgs": "--package-repositories $(PackageRepositories)" }, "Work on Fuse patches": { "commandName": "Executable", - "executablePath": "C:\\Program Files\\vvvv\\vvvv_gamma_5.3-0251-gc882d50ce8\\vvvv.exe", + "executablePath": "C:\\Program Files\\vvvv\\vvvv_gamma_5.3-0346-g0a53f6c531\\vvvv.exe", "commandLineArgs": "--package-repositories $(PackageRepositories) --editable-packages VL.Fuse" } } diff --git a/src/Fuse/ShaderFX/AbstractToShaderFX.cs b/src/Fuse/ShaderFX/AbstractToShaderFX.cs index a84bcdb1..561d05fe 100644 --- a/src/Fuse/ShaderFX/AbstractToShaderFX.cs +++ b/src/Fuse/ShaderFX/AbstractToShaderFX.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using JetBrains.Profiler.Api; +using Stride.Core.Yaml.Tokens; using Stride.Rendering.Materials; using Stride.Rendering.Materials.ComputeColors; using Stride.Shaders; @@ -58,7 +59,7 @@ public abstract class AbstractToShaderFX : IComputeValue private readonly HashSet_constantArrays = new(); private readonly HashSet_streams = new(); private readonly HashSet_mixins = new(); - private readonly HashSet_compositions = new(); + private readonly HashSet_compositions = new(); private readonly Dictionary _functionMap = new(); private readonly string _sourceTemplate; @@ -148,7 +149,7 @@ private Dictionary BuildTemplateMap() _mixins.ForEach(mixin => mixinBuilder.Append(", " + mixin)); var compositionBuilder = new StringBuilder(); - _compositions.ForEach(composition => compositionBuilder.AppendLine(composition)); + _compositions.ForEach(composition => compositionBuilder.AppendLine(composition.Declaration)); var functionBuilder = new StringBuilder(); _functionMap?.ForEach(kv => functionBuilder.AppendLine(kv.Value)); @@ -212,11 +213,11 @@ private void HandleShader(bool theIsComputeShader, AbstractShaderNode theShaderI _stopwatch.Restart(); theShaderInput.MixinList().ForEach(value => _mixins.Add(value)); if(ShaderNodesUtil.TimeShaderGeneration)Console.WriteLine($" Mixins: {_stopwatch.ElapsedMilliseconds} ms"); - /* + _stopwatch.Restart(); theShaderInput.CompositionList().ForEach(value => _compositions.Add(value)); if(ShaderNodesUtil.TimeShaderGeneration)Console.WriteLine($" Compositions: {_stopwatch.ElapsedMilliseconds} ms"); - */ + _stopwatch.Restart(); theShaderInput.FunctionMap().ForEach(HandleFunction); if(ShaderNodesUtil.TimeShaderGeneration)Console.WriteLine($" Functions: {_stopwatch.ElapsedMilliseconds} ms"); @@ -291,18 +292,32 @@ public ShaderSource GenerateShaderSource(ShaderGeneratorContext theContext, Mate } if(ShaderNodesUtil.TimeShaderGeneration)Console.WriteLine($"-> Evaluate: {_stopwatch.ElapsedMilliseconds} ms"); - _stopwatch.Restart(); - ShaderNodesUtil.AddShaderSource( ShaderName, ShaderCode, "shaders\\" + ShaderName + ".sdsl"); - if(ShaderNodesUtil.TimeShaderGeneration)Console.WriteLine($"-> AddShaderSource: {_stopwatch.ElapsedMilliseconds} ms"); - // _parameters = theContext.Parameters; - - // _input.InputList().ForEach(input => input.AddParameters(_parameters)); - var result = new ShaderClassSource(ShaderName); - _stopwatch.Stop(); + var shaderSource = new ShaderMixinSource() + { + Name = ShaderName, + Mixins = + { + new ShaderClassString(ShaderName, ShaderCode) + } + }; + + foreach (var composition in _compositions) + { + var compositionSource = composition.ComputeNode.GenerateShaderSource(theContext, baseKeys); + shaderSource.AddComposition(composition.CompositionName, compositionSource); + } + + // _parameters = theContext.Parameters; + + // _input.InputList().ForEach(input => input.AddParameters(_parameters)); + //var result = new ShaderClassSource(ShaderName); + _stopwatch.Stop(); if(ShaderNodesUtil.TimeShaderGeneration)Console.WriteLine($"-> Execution Time: {watch.ElapsedMilliseconds} ms for Shader {ShaderName}"); //MeasureProfiler.SaveData(ShaderName); - return result; + //return result; + + return shaderSource; } } } \ No newline at end of file diff --git a/src/Fuse/ShaderFX/CompositionInput.cs b/src/Fuse/ShaderFX/CompositionInput.cs index 4bcfbf9e..1ce63d54 100644 --- a/src/Fuse/ShaderFX/CompositionInput.cs +++ b/src/Fuse/ShaderFX/CompositionInput.cs @@ -1,47 +1,48 @@ using System.Collections.Generic; -using NuGet; using Stride.Rendering.Materials; using VL.Core; using VL.Stride.Shaders.ShaderFX; namespace Fuse.ShaderFX { + public interface IComposition + { + string Declaration { get; } + string CompositionName { get; } + IComputeNode ComputeNode { get; } + } - public class CompositionInput : ShaderNode + public class CompositionInput : ShaderNode, IComposition + where T : unmanaged { - protected const string DeclarationTemplate = @" - compose Compute${compositionType} ${compositionName};"; - - public CompositionInput(NodeContext nodeContext, string theId, IComputeValue theComposition) : base(nodeContext, theId, null) + IComputeValue _value; + + public CompositionInput(NodeContext nodeContext, SetVar value /* Should just be IComputeValue */) + : base(nodeContext, "compositionInput") { - - AddProperty(Compositions, this); + _value = value?.Value ?? new VL.Stride.Shaders.ShaderFX.ComputeNode(); + + SetProperty(Compositions, this); } - - protected override Dictionary CreateTemplateMap() - { - var result = new Dictionary - { - {"compositionType", TypeHelpers.GetSignature()}, - {"compositionName", ID} - }; - foreach (var template in base.CreateTemplateMap()) + public string Declaration => ShaderNodesUtil.Evaluate("compose ${compositionType} ${compositionId};", + new Dictionary() { - result.Add(template.Key, template.Value); - } + { "compositionType", TypeHelpers.GetCompositionType() }, + { "compositionId", CompositionName } + }); + + public IComputeNode ComputeNode => _value; + + public string CompositionName => $"composition{ID}"; - return result; - } - protected override string SourceTemplate() { - return "${resultType} ${resultName} = ${compositionName}.Compute();"; - } - - public override void OnPassContext(ShaderGeneratorContext theContext) - { - ShaderNodesUtil.Evaluate(DeclarationTemplate,CreateTemplateMap()); + return ShaderNodesUtil.Evaluate("${resultType} ${resultName} = ${compositionId}.Compute();", + new Dictionary + { + {"compositionId", CompositionName}, + }); } } } \ No newline at end of file diff --git a/src/Fuse/ShaderNode.cs b/src/Fuse/ShaderNode.cs index 9ca6c612..d560dffa 100644 --- a/src/Fuse/ShaderNode.cs +++ b/src/Fuse/ShaderNode.cs @@ -605,9 +605,9 @@ public List InputList() return PropertyForTree(Inputs); } - public List CompositionList() + public List CompositionList() { - return PropertyForTree(Compositions); + return PropertyForTree(Compositions); } public List DeclarationList() diff --git a/src/Fuse/ShaderNodesUtil.cs b/src/Fuse/ShaderNodesUtil.cs index 4d263501..f38cea46 100644 --- a/src/Fuse/ShaderNodesUtil.cs +++ b/src/Fuse/ShaderNodesUtil.cs @@ -10,12 +10,16 @@ using Fuse.ShaderFX; using Stride.Core; using Stride.Core.Mathematics; +using Stride.Graphics; using Stride.Rendering; using Stride.Rendering.Materials; +using Stride.Shaders; using Stride.Shaders.Compiler; using Stride.Shaders.Parser; +using Stride.Shaders.Parser.Mixins; using VL.Core; using VL.Stride; +using VL.Stride.Effects; using VL.Stride.Rendering; using VL.Stride.Rendering.ComputeEffect; using VL.Stride.Shaders.ShaderFX; @@ -223,19 +227,8 @@ public static void AddShaderSource(string type, string sourceCode, string source var game = ServiceRegistry.Current.GetGameProvider().GetHandle().Resource; if (game == null) return; - var effectSystem = game.EffectSystem; - var compiler = effectSystem.Compiler as EffectCompiler; - if (compiler is null && effectSystem.Compiler is EffectCompilerCache effectCompilerCache) - compiler = typeof(EffectCompilerChain) - .GetProperty("Compiler", BindingFlags.Instance | BindingFlags.NonPublic) - ?.GetValue(effectCompilerCache) as EffectCompiler; - - if (compiler == null) return; - - var getParserMethod = - typeof(EffectCompiler).GetMethod("GetMixinParser", BindingFlags.Instance | BindingFlags.NonPublic); - if (getParserMethod == null) return; - if (!(getParserMethod.Invoke(compiler, null) is ShaderMixinParser parser)) return; + var parser = game.EffectSystem.Compiler.GetShaderMixinParser(); + if (parser is null) return; var sourceManager = parser.SourceManager; sourceManager.AddShaderSource(type, sourceCode, sourcePath); @@ -246,6 +239,49 @@ public static void AddShaderSource(string type, string sourceCode, string source } } + public static ShaderMixinParser GetShaderMixinParser(this IEffectCompiler compiler) + { + var effectCompiler = compiler as EffectCompiler; + if (effectCompiler is null && compiler is EffectCompilerCache effectCompilerCache) + effectCompiler = typeof(EffectCompilerChain) + .GetProperty("Compiler", BindingFlags.Instance | BindingFlags.NonPublic) + ?.GetValue(effectCompilerCache) as EffectCompiler; + + if (effectCompiler is null) + return null; + + var getParserMethod = typeof(EffectCompiler).GetMethod("GetMixinParser", BindingFlags.Instance | BindingFlags.NonPublic); + if (getParserMethod is null) + return null; + + return (ShaderMixinParser)getParserMethod.Invoke(effectCompiler, null); + } + + // Workaround for compiler cache: it assumes that all shaders are known to the source manager + public static void MakeInMemoryShadersAvailableToTheSourceManager(IEffectCompiler compiler, ShaderSource shaderSource) + { + var parser = compiler.GetShaderMixinParser(); + if (parser is null) + return; + + var sourceManager = parser.SourceManager; + if (sourceManager is null) + return; + + Scan(sourceManager, shaderSource); + + static void Scan(ShaderSourceManager sourceManager, ShaderSource shaderSource) + { + if (shaderSource is ShaderClassString shaderClassString) + sourceManager.AddShaderSource(shaderClassString.ClassName, shaderClassString.ShaderSourceCode, null); + else if (shaderSource is ShaderMixinSource shaderMixinSource) + { + foreach (var s in shaderMixinSource.Mixins) + Scan(sourceManager, s); + } + } + } + // ReSharper disable once UnusedMember.Global // accessed from vl public static VLComputeEffectShader RegisterComputeShader( ToComputeFx theComputeFx) @@ -273,7 +309,7 @@ protected override void Destroy() } - public static DynamicEffectInstance RegisterDrawShader(ToDrawFX theDrawShader) + public static FuseEffectInstance RegisterDrawShader(ToDrawFX theDrawShader) { var watch = new Stopwatch(); @@ -281,16 +317,16 @@ public static DynamicEffectInstance RegisterDrawShader(ToDrawFX theDrawShader) var game = ServiceRegistry.Current.GetGameProvider().GetHandle().Resource; if (game == null) return null; - var effectImageShader = new DynamicDrawEffectInstance("ShaderFXGraphEffect"); + var parameters = new ParameterCollection(); + var subscriptions = new CompositeDisposable(); var method = typeof(ShaderGraph).GetMethod("NewShaderGeneratorContext", BindingFlags.Static | BindingFlags.NonPublic); - var context = method?.Invoke(null, new object[]{game.GraphicsDevice, effectImageShader.Parameters, effectImageShader.Subscriptions}); + var context = (ShaderGeneratorContext)method?.Invoke(null, new object[]{game.GraphicsDevice, parameters, subscriptions}); //var context = ShaderGraph.NewShaderGeneratorContext(game.GraphicsDevice, effectImageShader.Parameters, effectImageShader.Subscriptions); var key = new MaterialComputeColorKeys(MaterialKeys.DiffuseMap, MaterialKeys.DiffuseValue, Stride.Core.Mathematics.Color.White); - theDrawShader.GenerateShaderSource((ShaderGeneratorContext) context,key); - effectImageShader.EffectName = theDrawShader.ShaderName; - Console.WriteLine($"Register Time: {watch.ElapsedMilliseconds} ms for Shader {theDrawShader.ShaderName}"); - return effectImageShader; + var shaderSource = theDrawShader.GenerateShaderSource(context,key); + + return new FuseEffectInstance(shaderSource, parameters, subscriptions); } // ReSharper disable once UnusedMember.Global diff --git a/vl/Fuse.Core.vl b/vl/Fuse.Core.vl index 83ac5218..5ccba1ec 100644 --- a/vl/Fuse.Core.vl +++ b/vl/Fuse.Core.vl @@ -1,6 +1,6 @@  - - + + @@ -54214,6 +54214,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 11 @@ -87710,14 +87781,14 @@ - - + + - + - + @@ -87860,10 +87931,10 @@ - - - + + + @@ -88244,6 +88315,394 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -88255,7 +88714,6 @@ - @@ -88345,6 +88803,7 @@ +