/* * [Fact] * public void TestErrors() * { * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // check that a cyclic definition throws an error * var shaderClassSourceCyclic = new HashSet<ShaderClassSource> * { * new ShaderClassSource("CyclicTest") * }; * var mcmCyclic = new ShaderCompilationContext(shaderClassSourceCyclic, shaderLoader.LoadClassSource); * mcmCyclic.Run(); * * Assert.Equal(1, mcmCyclic.ErrorWarningLog.Messages.Count); * Assert.Equal(StrideMessageCode.ErrorCyclicDependency.Code, mcmCyclic.ErrorWarningLog.Messages[0].Code); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // check that a missing mixin throws an error * var shaderClassSourceMissing = new HashSet<ShaderClassSource> * { * new ShaderClassSource("Child") * }; * var mcmMissing = new ShaderCompilationContext(shaderClassSourceMissing, shaderLoader.LoadClassSource); * mcmMissing.Run(); * * Assert.Equal(1, mcmMissing.ErrorWarningLog.Messages.Count); * Assert.Equal(StrideMessageCode.ErrorDependencyNotInModule.Code, mcmMissing.ErrorWarningLog.Messages[0].Code); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // should throw an error: missing override keyword * var shaderClassSourceOverride = new HashSet<ShaderClassSource> * { * new ShaderClassSource("Parent"), * new ShaderClassSource("ChildError") * }; * var mcmOverride = new ShaderCompilationContext(shaderClassSourceOverride, shaderLoader.LoadClassSource); * mcmOverride.Run(); * * Assert.Equal(1, mcmOverride.ErrorWarningLog.Messages.Count); * Assert.Equal(StrideMessageCode.ErrorMissingOverride.Code, mcmOverride.ErrorWarningLog.Messages[0].Code); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // should create an error: name ambiguous * var shaderClassSourceAmbiguous = new HashSet<ShaderClassSource> * { * new ShaderClassSource("BasicMixin"), * new ShaderClassSource("BasicMixin2"), * new ShaderClassSource("MixinNameClash") * }; * var mcmAmbiguous = new ShaderCompilationContext(shaderClassSourceAmbiguous, shaderLoader.LoadClassSource); * mcmAmbiguous.Run(); * * Assert.Equal(1, mcmAmbiguous.ErrorWarningLog.Messages.Count); * Assert.Equal(StrideMessageCode.ErrorVariableNameAmbiguity.Code, mcmAmbiguous.ErrorWarningLog.Messages[0].Code); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // test what happens if an interface is declared * var shaderClassSourceInterface = new HashSet<ShaderClassSource> * { * new ShaderClassSource("InterfaceTest") * }; * var mcmInterface = new ShaderCompilationContext(shaderClassSourceInterface, shaderLoader.LoadClassSource); * mcmInterface.Run(); * * Assert.True(mcmInterface.ErrorWarningLog.HasErrors); * Assert.Equal(1, mcmInterface.ErrorWarningLog.Messages.Count); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // test what happens when a mixin is a parameter or a return value inside a function * var shaderClassSourceParamReturn = new HashSet<ShaderClassSource> * { * new ShaderClassSource("ExternMixin"), * new ShaderClassSource("MixinFunctionParamaterTest") * }; * var mcmParamReturn = new ShaderCompilationContext(shaderClassSourceParamReturn, shaderLoader.LoadClassSource); * mcmParamReturn.Run(); * * Assert.Equal(3, mcmParamReturn.ErrorWarningLog.Messages.Count); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * // test that it is impossible to put a shaderclass as a generic value * var shaderClassSourceGenerics = new HashSet<ShaderClassSource> * { * new ShaderClassSource("StructuredBufferTest"), * new ShaderClassSource("StaticMixin") * }; * var mcmGenerics = new ShaderCompilationContext(shaderClassSourceGenerics, shaderLoader.LoadClassSource); * mcmGenerics.Run(); * * Assert.True(mcmGenerics.ErrorWarningLog.HasErrors); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * var shaderClassSourceFunc = new HashSet<ShaderClassSource> * { * new ShaderClassSource("TestErrors"), * new ShaderClassSource("ExternMixin") * }; * var mcmFunc = new ShaderCompilationContext(shaderClassSourceFunc, shaderLoader.LoadClassSource); * mcmFunc.Run(); * * // TODO: separate tests or check messages/error code * Assert.Equal(27, mcmFunc.ErrorWarningLog.Messages.Count); * * /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// * * var shaderClassSourceStream = new HashSet<ShaderClassSource> * { * new ShaderClassSource("StreamError") * }; * var mcmStream = new ShaderCompilationContext(shaderClassSourceStream, shaderLoader.LoadClassSource); * mcmStream.Run(); * * Assert.Equal(1, mcmStream.ErrorWarningLog.Messages.Count); * Assert.Equal(StrideMessageCode.ErrorInOutStream.Code, mcmStream.ErrorWarningLog.Messages[0].Code); * } * * [Fact] * public void TestShaderLibrary() * { * var shaderClassSourceList = new HashSet<string> * { * "BaseTestParent", * "BaseTestParent", * "StaticMixin", * "MacroTest", * "MacroTestChild" * }; * * var lib = new StrideShaderLibrary(shaderClassSourceList); * lib.LoadClass = shaderLoader.LoadClassSource; * * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(0, lib.MixinInfos.Count); * var context = lib.GetContextFromMacros(new ShaderMacro[] { new ShaderMacro("MAC0", "0") }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(4, lib.MixinInfos.Count); * Assert.Equal(4, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); * * context = lib.GetContextFromMacros(new ShaderMacro[] { new ShaderMacro("MAC1", "1") }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(8, lib.MixinInfos.Count); * Assert.Equal(4, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); * * context = lib.GetContextFromMacros(new ShaderMacro[] { new ShaderMacro("MAC0", "1") }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(12, lib.MixinInfos.Count); * Assert.Equal(4, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); * * context = lib.GetContextFromMacros(new ShaderMacro[] { new ShaderMacro("MAC0", "0") }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(12, lib.MixinInfos.Count); * Assert.Equal(4, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); * * context = lib.GetContextFromMacros(new ShaderMacro[] { }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(16, lib.MixinInfos.Count); * Assert.Equal(4, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); * * context = lib.GetContextFromMacros(new ShaderMacro[] { new ShaderMacro("MACRO_TEST", "int") }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(20, lib.MixinInfos.Count); * Assert.Equal(5, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); * * context = lib.GetContextFromMacros(new ShaderMacro[] { new ShaderMacro("MACRO_TEST", "float") }); * Assert.Equal(4, context.Count); * Assert.Equal(4, lib.AvailableShaders.Count); * Assert.Equal(24, lib.MixinInfos.Count); * Assert.Equal(6, lib.MixinInfos.Select(x => x.Mixin).Distinct().Count()); // TODO: should be 4! * * } * * [Fact] * public void TestSingle() * { * VirtualFileSystem.MountFileSystem("/assets/shaders", "../../../../../shaders"); * * var className = "ComputeColorFixed"; * Console.WriteLine(@"Loading effect " + className); * * var effectCompiler = EffectCompiler.New(EffectCompilerTarget.Direct3D11, GraphicsProfile.Level_11_1) as EffectCompilerHlsl; * * if (effectCompiler != null) * { * var mixin = new ShaderMixinSource(); * mixin.Mixins.Add(new ShaderClassSource(className, "float4(0.0,1.0, 0.5, 2.0)")); * effectCompiler.CompileEffectShaderPass("test.hlsl", mixin, null); * } * } * * private void TestSingleClass(EffectCompilerHlsl effectCompiler, string className) * { * var mixin = new ShaderMixinSource(); * mixin.Mixins.Add(new ShaderClassSource(className)); * try * { * if (className == "LightMultiDirectionalShadingPerPixel") * mixin.Mixins[0].GenericParameters = new object[] {4}; * effectCompiler.CompileEffectShaderPass("test.hlsl", mixin, null); * } * catch (Exception exp) * { * Console.WriteLine(@"---EXCEPTION---"); * Console.WriteLine(exp.Message); * } * } * * [Fact] * public void TestWarnings() * { * //VirtualFileSystem.MountFileSystem("/assets/shaders", "../../../../../shaders"); * VirtualFileSystem.MountFileSystem("/assets/shaders", "C:\\Users\\aurelien.serandour\\Desktop\\Shaders"); * foreach (var file in VirtualFileSystem.ListFiles("/assets/shaders", "*.sdsl", VirtualSearchOption.TopDirectoryOnly).Result) * { * var fileParts = file.Split('.', '/'); * var className = fileParts[fileParts.Length - 2]; * Console.WriteLine(); * Console.WriteLine(@"Loading effect " + className); * var effectCompiler = EffectCompiler.New(EffectCompilerTarget.Direct3D11, GraphicsProfile.Level_11_1) as EffectCompilerHlsl; * * if (effectCompiler != null) * TestSingleClass(effectCompiler, className); * } * * var effectCompiler0 = EffectCompiler.New(EffectCompilerTarget.Direct3D11, GraphicsProfile.Level_11_1) as EffectCompilerHlsl; * TestSingleClass(effectCompiler0, "PostEffectFXAA"); * } * * * [Fact] * public void TestMayaWarnings() * { * VirtualFileSystem.MountFileSystem("/assets/shaders", "../../../../../shaders"); * //VirtualFileSystem.MountFileSystem("/assets/shaders", "C:\\Users\\aurelien.serandour\\Desktop\\Shaders\\Maya"); * foreach (var file in VirtualFileSystem.ListFiles("/assets/shaders", "*.sdsl", VirtualSearchOption.TopDirectoryOnly).Result) * { * var fileParts = file.Split('.', '/'); * var className = fileParts[fileParts.Length - 2]; * Console.WriteLine(); * Console.WriteLine(@"Loading effect " + className); * var effectCompiler = EffectCompiler.New(EffectCompilerTarget.Direct3D11, GraphicsProfile.Level_11_1) as EffectCompilerHlsl; * * if (effectCompiler != null) * TestSingleClass(effectCompiler, className); * } * }*/ public ModuleMixinInfo GetAnalyzedMixin(string mixinName) { var source = new ShaderMixinSource(); source.Mixins.Add(new ShaderClassSource(mixinName)); shaderMixinParser.Parse(source, new Stride.Shaders.ShaderMacro[0]); var moduleMixin = shaderMixinParser.GetMixin(mixinName); Assert.IsNotNull(moduleMixin); Assert.False(moduleMixin.Log.HasErrors); Assert.IsNotNull(moduleMixin.Mixin); return(moduleMixin); }
public void TestMacros() { // test that macros are correctly used var baseMixin = new ShaderMixinSource(); baseMixin.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_DIRECT3D", 1); baseMixin.Macros.Add(new ShaderMacro("MACRO_TEST", "int")); baseMixin.Mixins.Add(new ShaderClassSource("TestMacros")); var macros0 = new ShaderMixinSource(); macros0.Mixins.Add(new ShaderClassSource("MacroTest")); baseMixin.Compositions.Add("macros0", macros0); var macros1 = new ShaderMixinSource(); macros1.Mixins.Add(new ShaderClassSource("MacroTest")); macros1.Macros.Add(new ShaderMacro("MACRO_TEST", "float")); baseMixin.Compositions.Add("macros1", macros1); var macros2 = new ShaderMixinSource(); macros2.Mixins.Add(new ShaderClassSource("MacroTest")); macros2.Macros.Add(new ShaderMacro("MACRO_TEST", "float4")); baseMixin.Compositions.Add("macros2", macros2); var parsingResult = shaderMixinParser.Parse(baseMixin, baseMixin.Macros.ToArray()); Assert.IsFalse(parsingResult.HasErrors); var cBufferVar = parsingResult.Shader.Declarations.OfType <ConstantBuffer>().First(x => x.Name == "Globals").Members.OfType <Variable>().ToList(); Assert.AreEqual(1, cBufferVar.Count(x => x.Type.Name.Text == "int")); Assert.AreEqual(1, cBufferVar.Count(x => x.Type.Name.Text == "float")); Assert.AreEqual(1, cBufferVar.Count(x => x.Type.Name.Text == "float4")); // test clash when reloading var baseMixin2 = new ShaderMixinSource(); baseMixin2.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_DIRECT3D", 1); baseMixin2.Macros.Add(new ShaderMacro("MACRO_TEST", "int")); baseMixin2.Mixins.Add(new ShaderClassSource("TestMacros")); var macros3 = new ShaderMixinSource(); macros3.Mixins.Add(new ShaderClassSource("MacroTest")); baseMixin2.Compositions.Add("macros0", macros3); var macros4 = new ShaderMixinSource(); macros4.Mixins.Add(new ShaderClassSource("MacroTest")); macros4.Macros.Add(new ShaderMacro("MACRO_TEST", "uint4")); baseMixin2.Compositions.Add("macros1", macros4); var macros5 = new ShaderMixinSource(); macros5.Mixins.Add(new ShaderClassSource("MacroTest")); macros5.Macros.Add(new ShaderMacro("MACRO_TEST", "float4")); baseMixin2.Compositions.Add("macros2", macros5); var parsingResult2 = shaderMixinParser.Parse(baseMixin2, baseMixin2.Macros.ToArray()); Assert.IsFalse(parsingResult.HasErrors); var cBufferVar2 = parsingResult2.Shader.Declarations.OfType <ConstantBuffer>().First(x => x.Name == "Globals").Members.OfType <Variable>().ToList(); Assert.AreEqual(1, cBufferVar2.Count(x => x.Type.Name.Text == "int")); Assert.AreEqual(1, cBufferVar2.Count(x => x.Type.Name.Text == "uint4")); Assert.AreEqual(1, cBufferVar2.Count(x => x.Type.Name.Text == "float4")); }
public override EffectBytecode Compile(ShaderMixinSource shaderMixinSource, string fullEffectName, ShaderMixinParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, LoggerResult log) { // Load D3D compiler dll // Note: No lock, it's probably fine if it gets called from multiple threads at the same time. if (Platform.IsWindowsDesktop && !d3dcompilerLoaded) { NativeLibrary.PreloadLibrary("d3dcompiler_47.dll"); d3dcompilerLoaded = true; } // Make a copy of shaderMixinSource. Use deep clone since shaderMixinSource can be altered during compilation (e.g. macros) var shaderMixinSourceCopy = new ShaderMixinSource(); shaderMixinSourceCopy.DeepCloneFrom(shaderMixinSource); shaderMixinSource = shaderMixinSourceCopy; // Generate platform-specific macros var platform = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey); switch (platform) { case GraphicsPlatform.Direct3D11: shaderMixinSource.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_DIRECT3D", 1); shaderMixinSource.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_DIRECT3D11", 1); break; case GraphicsPlatform.OpenGL: shaderMixinSource.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGL", 1); shaderMixinSource.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLCORE", 1); break; case GraphicsPlatform.OpenGLES: shaderMixinSource.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGL", 1); shaderMixinSource.AddMacro("SILICONSTUDIO_PARADOX_GRAPHICS_API_OPENGLES", 1); break; default: throw new NotSupportedException(); } // Generate the AST from the mixin description if (shaderMixinParser == null) { shaderMixinParser = new ShaderMixinParser(); shaderMixinParser.SourceManager.LookupDirectoryList = SourceDirectories; // TODO: temp shaderMixinParser.SourceManager.UrlToFilePath = UrlToFilePath; // TODO: temp } if (recentlyModifiedShaders != null && recentlyModifiedShaders.Count > 0) { shaderMixinParser.DeleteObsoleteCache(GetShaderNames(recentlyModifiedShaders)); recentlyModifiedShaders.Clear(); } var parsingResult = shaderMixinParser.Parse(shaderMixinSource, shaderMixinSource.Macros.ToArray(), modifiedShaders); // Copy log from parser results to output CopyLogs(parsingResult, log); // Return directly if there are any errors if (parsingResult.HasErrors) { return(null); } // Convert the AST to HLSL var writer = new SiliconStudio.Shaders.Writer.Hlsl.HlslWriter { EnablePreprocessorLine = false }; writer.Visit(parsingResult.Shader); var shaderSourceText = writer.Text; // ------------------------------------------------------- // Save shader log // TODO: TEMP code to allow debugging generated shaders on Windows Desktop #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP var shaderId = ObjectId.FromBytes(Encoding.UTF8.GetBytes(shaderSourceText)); var logDir = "log"; if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } var shaderSourceFilename = Path.Combine(logDir, "shader_" + shaderId); lock (WriterLock) // protect write in case the same shader is created twice { if (!File.Exists(shaderSourceFilename)) { var builder = new StringBuilder(); builder.AppendLine("/***** Used Parameters *****"); builder.Append(" * EffectName: "); builder.AppendLine(fullEffectName ?? ""); WriteParameters(builder, compilerParameters, 0, false); builder.AppendLine(" ***************************/"); builder.Append(shaderSourceText); File.WriteAllText(shaderSourceFilename, builder.ToString()); } } #endif // ------------------------------------------------------- var bytecode = new EffectBytecode { Reflection = parsingResult.Reflection, HashSources = parsingResult.HashSources }; // Select the correct backend compiler IShaderCompiler compiler; switch (platform) { case GraphicsPlatform.Direct3D11: compiler = new Direct3D.ShaderCompiler(); break; case GraphicsPlatform.OpenGL: case GraphicsPlatform.OpenGLES: compiler = new OpenGL.ShaderCompiler(); break; default: throw new NotSupportedException(); } var shaderStageBytecodes = new List <ShaderBytecode>(); foreach (var stageBinding in parsingResult.EntryPoints) { // Compile var result = compiler.Compile(shaderSourceText, stageBinding.Value, stageBinding.Key, compilerParameters, bytecode.Reflection, shaderSourceFilename); result.CopyTo(log); if (result.HasErrors) { continue; } // ------------------------------------------------------- // Append bytecode id to shader log #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP lock (WriterLock) // protect write in case the same shader is created twice { if (File.Exists(shaderSourceFilename)) { // Append at the end of the shader the bytecodes Id File.AppendAllText(shaderSourceFilename, "\n// {0} {1}".ToFormat(stageBinding.Key, result.Bytecode.Id)); } } #endif // ------------------------------------------------------- shaderStageBytecodes.Add(result.Bytecode); // When this is a compute shader, there is no need to scan other stages if (stageBinding.Key == ShaderStage.Compute) { break; } } // Get the current time of compilation bytecode.Time = DateTime.Now; // In case of Direct3D, we can safely remove reflection data as it is entirely resolved at compile time. if (platform == GraphicsPlatform.Direct3D11) { CleanupReflection(bytecode.Reflection); } bytecode.Stages = shaderStageBytecodes.ToArray(); return(bytecode); }