Exemplo n.º 1
0
        /*
         * [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);
        }
Exemplo n.º 2
0
        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"));
        }
Exemplo n.º 3
0
        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);
        }