public void TestSimpleChild() { var properties = new ShaderMixinParameters(); var mixin = GenerateMixin("DefaultSimpleChild", properties); mixin.CheckMixin("A", "B", "C", "C1", "C2"); }
public void TestSimple() { var properties = new ShaderMixinParameters(); ShaderMixinParameters usedProperties; var mixin = GenerateMixin("DefaultSimple", properties, out usedProperties); mixin.CheckMixin("A", "B", "C"); }
public void TestSimpleCompose() { var properties = new ShaderMixinParameters(); var mixin = GenerateMixin("DefaultSimpleCompose", properties); mixin.CheckMixin("A", "B", "C"); mixin.CheckComposition("x", "X"); }
/// <summary> /// Converts the hlsl code into glsl and stores the result as plain text /// </summary> /// <param name="shaderSource">the hlsl shader</param> /// <param name="entryPoint">the entrypoint function name</param> /// <param name="stage">the shader pipeline stage</param> /// <param name="compilerParameters"></param> /// <param name="reflection">the reflection gathered from the hlsl analysis</param> /// <param name="sourceFilename">the name of the source file</param> /// <returns></returns> public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null) { var isOpenGLES = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey) == GraphicsPlatform.OpenGLES; var isOpenGLES3 = compilerParameters.Get(CompilerParameters.GraphicsProfileKey) >= GraphicsProfile.Level_10_0; var shaderBytecodeResult = new ShaderBytecodeResult(); byte[] rawData; var shader = Compile(shaderSource, entryPoint, stage, isOpenGLES, isOpenGLES3, shaderBytecodeResult, sourceFilename); if (shader == null) return shaderBytecodeResult; if (isOpenGLES) { // store both ES 2 and ES 3 on OpenGL ES platforms var shaderBytecodes = new ShaderLevelBytecode(); if (isOpenGLES3) { shaderBytecodes.DataES3 = shader; shaderBytecodes.DataES2 = null; } else { shaderBytecodes.DataES2 = shader; shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, true, true, shaderBytecodeResult, sourceFilename); } using (var stream = new MemoryStream()) { BinarySerialization.Write(stream, shaderBytecodes); #if !SILICONSTUDIO_RUNTIME_CORECLR rawData = stream.GetBuffer(); #else // FIXME: Manu: The call to "ToArray()" might be slower than "GetBuffer()" rawData = stream.ToArray(); #endif } } else { // store string on OpenGL platforms rawData = Encoding.ASCII.GetBytes(shader); } var bytecodeId = ObjectId.FromBytes(rawData); var bytecode = new ShaderBytecode(bytecodeId, rawData); bytecode.Stage = stage; shaderBytecodeResult.Bytecode = bytecode; return shaderBytecodeResult; }
public void Init() { // Create and mount database file system var objDatabase = new ObjectDatabase("/data/db", "index", "/local/db"); var databaseFileProvider = new DatabaseFileProvider(objDatabase); AssetManager.GetFileProvider = () => databaseFileProvider; Compiler = new EffectCompiler(); Compiler.SourceDirectories.Add("shaders"); MixinParameters = new ShaderMixinParameters(); MixinParameters.Add(CompilerParameters.GraphicsPlatformKey, GraphicsPlatform.Direct3D11); MixinParameters.Add(CompilerParameters.GraphicsProfileKey, GraphicsProfile.Level_11_0); ResultLogger = new LoggerResult(); }
/// <summary> /// Generates the mixin. /// </summary> /// <param name="mixinName">Name of the mixin.</param> /// <param name="properties">The properties that the mixin will use.</param> /// <param name="usedProperties">The properties effectively used by the mixin.</param> /// <returns>ShaderMixinSource.</returns> private static ShaderMixinSource GenerateMixin(string mixinName, ParameterCollection properties, out ShaderMixinParameters usedProperties) { var allUsedProperties = new List<ShaderMixinParameters>(); var mixin = ShaderMixinManager.Generate(mixinName, properties); usedProperties = mixin.UsedParameters; // Verify that output used properties are a subset of input properties Assert.That(usedProperties.IsSubsetOf(properties), Is.True); foreach(var usedProps in allUsedProperties) Assert.That(usedProps.IsSubsetOf(properties), Is.True); return mixin; }
public void TestSimpleParams() { var properties = new ShaderMixinParameters(); var mixin = GenerateMixin("DefaultSimpleParams", properties); mixin.CheckMixin("A", "B", "D"); mixin.CheckComposition("y", "Y"); mixin.CheckMacro("Test", "ok"); // Set a key to modify the mixin properties.Set(Test7.TestParameters.param1, true); mixin = GenerateMixin("DefaultSimpleParams", properties); mixin.CheckMixin("A", "B", "C"); mixin.CheckComposition("x", "X"); mixin.CheckMacro("param2", 1); }
public void Compile() { Console.WriteLine(@"Inside Thread"); var parameters = new ShaderMixinParameters(); parameters.Set(CompilerParameters.GraphicsPlatformKey, GraphicsPlatform.Direct3D11); parameters.Set(CompilerParameters.GraphicsProfileKey, GraphicsProfile.Level_11_0); var mixinTree = new ShaderMixinSource() { Name = "TestParallelMix", UsedParameters = parameters }; var result = effectCompiler.Compile(mixinTree, new CompilerParameters()).WaitForResult(); Assert.IsFalse(result.CompilationLog.HasErrors); Assert.IsNotNull(result); Console.WriteLine(@"Thread end"); }
public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null) { var isDebug = compilerParameters.Get(CompilerParameters.DebugKey); var profile = compilerParameters.Get(CompilerParameters.GraphicsProfileKey); var shaderModel = ShaderStageToString(stage) + "_" + ShaderProfileFromGraphicsProfile(profile); var shaderFlags = ShaderFlags.None; if (isDebug) { shaderFlags = ShaderFlags.OptimizationLevel0 | ShaderFlags.Debug; } SharpDX.Configuration.ThrowOnShaderCompileError = false; // Compile using D3DCompiler var compilationResult = SharpDX.D3DCompiler.ShaderBytecode.Compile(shaderSource, entryPoint, shaderModel, shaderFlags, EffectFlags.None, null, null, sourceFilename); var byteCodeResult = new ShaderBytecodeResult(); if (compilationResult.HasErrors) { // Log compilation errors byteCodeResult.Error(compilationResult.Message); } else { // As effect bytecode binary can changed when having debug infos (with d3dcompiler_47), we are calculating a bytecodeId on the stripped version var rawData = compilationResult.Bytecode.Strip(StripFlags.CompilerStripDebugInformation | StripFlags.CompilerStripReflectionData); var bytecodeId = ObjectId.FromBytes(rawData); byteCodeResult.Bytecode = new ShaderBytecode(bytecodeId, compilationResult.Bytecode.Data) { Stage = stage }; // If compilation succeed, then we can update reflection. UpdateReflection(byteCodeResult.Bytecode, reflection, byteCodeResult); if (!string.IsNullOrEmpty(compilationResult.Message)) { byteCodeResult.Warning(compilationResult.Message); } } return byteCodeResult; }
public void TestShaderCompilation() { var generics = new string[3]; generics[0] = "Texturing.Texture1"; generics[1] = "TEXCOORD0"; generics[2] = "float4(2.0,1,1,1)"; var compilerParameters = new ShaderMixinParameters(); compilerParameters.Set(EffectSourceCodeKeys.Enable, true); compilerParameters.Set(CompilerParameters.GraphicsProfileKey, GraphicsProfile.Level_11_0); var mixinSource = new ShaderMixinSource { Name = "TestShaderCompilationGenericClass", UsedParameters = compilerParameters }; mixinSource.Mixins.Add(new ShaderClassSource("GenericClass2", generics)); var log = new CompilerResults(); var compiler = new EffectCompiler(); compiler.SourceDirectories.Add("shaders"); var effectByteCode = compiler.Compile(mixinSource, new CompilerParameters()); }
/// <inheritdoc/> public override void Load() { base.Load(); useMultipleRenderTargets = GraphicsDevice.Features.Profile >= GraphicsProfile.Level_10_0; for (var maxBlendCount = 2; maxBlendCount < 5; ++maxBlendCount) { var compilerParameter = new CompilerParameters(); var compilerParameterChild = new ShaderMixinParameters[maxBlendCount]; for (var i = 0; i < maxBlendCount; ++i) { var param = new ShaderMixinParameters(); param.Add(CubemapBlendRenderer.CubemapKey, GetTextureCubeKey(i)); compilerParameterChild[i] = param; } compilerParameter.Set(CubemapBlendRenderer.Cubemaps, compilerParameterChild); compilerParameter.Set(CubemapBlendRenderer.CubemapCount, maxBlendCount); compilerParameter.Set(CubemapBlendRenderer.UseMultipleRenderTargets, useMultipleRenderTargets); cubemapBlendEffects.Add(maxBlendCount, EffectSystem.LoadEffect("CubemapBlendEffect", compilerParameter)); } }
public void TestShaderParametersSerialization() { // Test serialization var shaderParameters = new ShaderMixinParameters("Test"); shaderParameters.Set(PropertyInt, 5); var subShaderParameters = new ShaderMixinParameters("Sub"); subShaderParameters.Set(PropertyInt, 6); shaderParameters.Set(PropertySub, subShaderParameters); var subShaderParametersArray = new ShaderMixinParameters[1]; var subShaderParametersArray1 = new ShaderMixinParameters("InArray1"); subShaderParametersArray[0] = subShaderParametersArray1; subShaderParametersArray1.Set(PropertyInt, 7); shaderParameters.Set(PropertySubs, subShaderParametersArray); var memoryStream = new MemoryStream(); var writer = new BinarySerializationWriter(memoryStream); writer.Write(shaderParameters); writer.Flush(); memoryStream.Position = 0; var reader = new BinarySerializationReader(memoryStream); var shaderParametersReloaded = reader.Read <ShaderMixinParameters>(); // They should be strictly equal Assert.That(shaderParametersReloaded.IsSubsetOf(shaderParameters), Is.True); Assert.That(shaderParameters.IsSubsetOf(shaderParametersReloaded), Is.True); // Test subset // Check that by removing one key from the original parameters, the reloaded version is // no longer a subset subShaderParametersArray1.Remove(PropertyInt); Assert.That(shaderParametersReloaded.IsSubsetOf(shaderParameters), Is.False); }
public void TestShaderCompilation() { var generics = new string[3]; generics[0] = "Texturing.Texture1"; generics[1] = "TEXCOORD0"; generics[2] = "float4(2.0,1,1,1)"; var mixinSource = new ShaderMixinSource(); mixinSource.Mixins.Add(new ShaderClassSource("GenericClass2", generics)); var compilerParameters = new ShaderMixinParameters(); compilerParameters.Set(EffectSourceCodeKeys.Enable, true); compilerParameters.Set(CompilerParameters.GraphicsProfileKey, GraphicsProfile.Level_11_0); var log = new CompilerResults(); var compiler = new EffectCompiler(); compiler.SourceDirectories.Add("shaders"); var effectByteCode = compiler.Compile(mixinSource, "TestShaderCompilationGenericClass", compilerParameters, null, null, log); }
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); }
public void TestComplexParams() { var properties = new ShaderMixinParameters(); // Populate the the properties used by the mixin var subParam1 = new Test1.SubParameters(); var subParameters = new Test1.SubParameters[4]; for (int i = 0; i < subParameters.Length; i++) { subParameters[i] = new Test1.SubParameters(); } properties.Set(Test1.TestParameters.subParam1, subParam1); properties.Set(Test1.TestParameters.subParameters, subParameters); // Generate the mixin with default properties var mixin = GenerateMixin("DefaultComplexParams", properties); mixin.CheckMixin("A", "B", "C", "D"); // Modify properties in order to modify mixin for (int i = 0; i < subParameters.Length; i++) { subParameters[i].Set(Test1.SubParameters.param1, (i & 1) == 0); } subParam1.Set(Test1.SubParameters.param2, 2); mixin = GenerateMixin("DefaultComplexParams", properties); mixin.CheckMixin("A", "B", "C", "C1", "C3"); }
public override EffectBytecode Compile(ShaderMixinSource mixin, string fullEffectName, ShaderMixinParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, LoggerResult log) { var database = AssetManager.FileProvider; if (database == null) { throw new NotSupportedException("Using the cache requires to AssetManager.FileProvider to be valid."); } // remove the old shaders if (recentlyModifiedShaders != null && recentlyModifiedShaders.Count != 0) { RemoveObsoleteStoredResults(recentlyModifiedShaders); } var ids = ShaderMixinObjectId.Compute(mixin, compilerParameters); EffectBytecode bytecode = null; lock (storedResults) { if (storedResults.TryGetValue(ids.FullParametersId, out bytecode)) { return(bytecode); } // Final url of the compiled bytecode var compiledUrl = string.Format("{0}/{1}", CompiledShadersKey, ids.CompileParametersId); // ------------------------------------------------------------------------------------------------------------ // 1) Try to load latest bytecode // ------------------------------------------------------------------------------------------------------------ ObjectId bytecodeId; if (database.AssetIndexMap.TryGetValue(compiledUrl, out bytecodeId)) { using (var stream = database.ObjectDatabase.OpenStream(bytecodeId)) { var localBytecode = BinarySerialization.Read <EffectBytecode>(stream); // If latest bytecode is in sync if (!Platform.IsWindowsDesktop || CheckBytecodeInSyncAgainstSources(localBytecode, database)) { bytecode = localBytecode; // if bytecode contains a modified shource, do not use it. if (modifiedShaders != null && modifiedShaders.Count != 0 && IsBytecodeObsolete(bytecode, modifiedShaders)) { bytecode = null; } } } } // On non Windows platform, we are expecting to have the bytecode stored directly if (!Platform.IsWindowsDesktop && bytecode == null) { Log.Error("Unable to find compiled shaders [{0}] for mixin [{1}] with parameters [{2}]", compiledUrl, mixin, compilerParameters.ToStringDetailed()); throw new InvalidOperationException("Unable to find compiled shaders [{0}]".ToFormat(compiledUrl)); } // ------------------------------------------------------------------------------------------------------------ // 2) Try to load from intermediate results // ------------------------------------------------------------------------------------------------------------ if (bytecode == null) { // Check if this id has already a ShaderBytecodeStore var isObjectInDatabase = database.ObjectDatabase.Exists(ids.CompileParametersId); // Try to load from an existing ShaderBytecode if ((modifiedShaders == null || modifiedShaders.Count == 0) && isObjectInDatabase) { var stream = database.ObjectDatabase.OpenStream(ids.CompileParametersId, VirtualFileMode.Open, VirtualFileAccess.Read, VirtualFileShare.Read); using (var resultsStore = new ShaderBytecodeStore(stream)) { // Load new values resultsStore.LoadNewValues(); var storedValues = resultsStore.GetValues(); foreach (KeyValuePair <HashSourceCollection, EffectBytecode> hashResults in storedValues) { if (CheckBytecodeInSyncAgainstSources(hashResults.Value, database)) { bytecode = hashResults.Value; break; } } } } // -------------------------------------------------------------------------------------------------------- // 3) Bytecode was not found in the cache on disk, we need to compile it // -------------------------------------------------------------------------------------------------------- if (bytecode == null) { // Open the database for writing var stream = database.ObjectDatabase.OpenStream(ids.CompileParametersId, VirtualFileMode.OpenOrCreate, VirtualFileAccess.ReadWrite, VirtualFileShare.ReadWrite); using (var resultsStore = new ShaderBytecodeStore(stream)) { var localLogger = new LoggerResult(); // Compile the mixin bytecode = base.Compile(mixin, fullEffectName, compilerParameters, modifiedShaders, recentlyModifiedShaders, localLogger); log.Info("New effect compiled [{0}]\r\n{1}", ids.CompileParametersId, compilerParameters.ToStringDetailed()); localLogger.CopyTo(log); // If there are any errors, return immediately if (localLogger.HasErrors) { return(null); } // Else store the bytecode for this set of HashSources resultsStore[bytecode.HashSources] = bytecode; } } // Save latest bytecode into the storage using (var stream = database.OpenStream(compiledUrl, VirtualFileMode.Create, VirtualFileAccess.Write, VirtualFileShare.Write)) { BinarySerialization.Write(stream, bytecode); } } else { // clone the bytecode since it is not the first time we load it. bytecode = bytecode.Clone(); } // Store the bytecode in the memory cache storedResults[ids.FullParametersId] = bytecode; } return(bytecode); }
public EffectCompileRequest(string effectName, ShaderMixinParameters usedParameters) { EffectName = effectName; UsedParameters = usedParameters; }
private EffectBytecodeCompilerResult CompileBytecode(ShaderMixinSource mixinTree, CompilerParameters compilerParameters, ObjectId mixinObjectId, DatabaseFileProvider database, string compiledUrl, ShaderMixinParameters usedParameters) { // Open the database for writing var log = new LoggerResult(); // Note: this compiler is expected to not be async and directly write stuff in localLogger var compiledShader = base.Compile(mixinTree, compilerParameters).WaitForResult(); compiledShader.CompilationLog.CopyTo(log); // If there are any errors, return immediately if (log.HasErrors) { lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return(new EffectBytecodeCompilerResult(null, log)); } // Compute the bytecodeId var newBytecodeId = compiledShader.Bytecode.ComputeId(); // Check if we really need to store the bytecode lock (bytecodes) { // Using custom serialization to the database to store an object with a custom id // TODO: Check if we really need to write the bytecode everytime even if id is not changed var memoryStream = new MemoryStream(); compiledShader.Bytecode.WriteTo(memoryStream); memoryStream.Position = 0; database.ObjectDatabase.Write(memoryStream, newBytecodeId, true); database.AssetIndexMap[compiledUrl] = newBytecodeId; // Save bytecode Id to the database cache as well memoryStream.SetLength(0); memoryStream.Write((byte[])newBytecodeId, 0, ObjectId.HashSize); memoryStream.Position = 0; database.ObjectDatabase.Write(memoryStream, mixinObjectId, true); if (!bytecodes.ContainsKey(newBytecodeId)) { log.Info("New effect compiled #{0} [{1}] (db: {2})\r\n{3}", effectCompileCount, mixinObjectId, newBytecodeId, usedParameters.ToStringDetailed()); Interlocked.Increment(ref effectCompileCount); // Replace or add new bytecode bytecodes[newBytecodeId] = compiledShader.Bytecode; } } lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return(compiledShader); }
public override EffectBytecode Compile(ShaderMixinSource mixin, string fullEffectName, ShaderMixinParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, LoggerResult log) { throw new NotSupportedException("Shader Compilation is not allowed at run time on this platform."); }
public override EffectBytecode Compile(ShaderMixinSource mixin, string fullEffectName, ShaderMixinParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, LoggerResult log) { return(compiler.Compile(mixin, fullEffectName, compilerParameters, modifiedShaders, recentlyModifiedShaders, log)); }
/// <summary> /// Converts the hlsl code into glsl and stores the result as plain text /// </summary> /// <param name="shaderSource">the hlsl shader</param> /// <param name="entryPoint">the entrypoint function name</param> /// <param name="stage">the shader pipeline stage</param> /// <param name="compilerParameters"></param> /// <param name="reflection">the reflection gathered from the hlsl analysis</param> /// <param name="sourceFilename">the name of the source file</param> /// <returns></returns> public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null) { var isOpenGLES = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey) == GraphicsPlatform.OpenGLES; var isOpenGLES3 = compilerParameters.Get(CompilerParameters.GraphicsProfileKey) >= GraphicsProfile.Level_10_0; var shaderBytecodeResult = new ShaderBytecodeResult(); byte[] rawData; var shader = Compile(shaderSource, entryPoint, stage, isOpenGLES, isOpenGLES3, shaderBytecodeResult, sourceFilename); if (shader == null) { return(shaderBytecodeResult); } if (isOpenGLES) { // store both ES 2 and ES 3 on OpenGL ES platforms var shaderBytecodes = new ShaderLevelBytecode(); if (isOpenGLES3) { shaderBytecodes.DataES3 = shader; shaderBytecodes.DataES2 = null; } else { shaderBytecodes.DataES2 = shader; shaderBytecodes.DataES3 = Compile(shaderSource, entryPoint, stage, true, true, shaderBytecodeResult, sourceFilename); } using (var stream = new MemoryStream()) { BinarySerialization.Write(stream, shaderBytecodes); rawData = stream.GetBuffer(); } } else { // store string on OpenGL platforms rawData = Encoding.ASCII.GetBytes(shader); } var bytecodeId = ObjectId.FromBytes(rawData); var bytecode = new ShaderBytecode(bytecodeId, rawData); bytecode.Stage = stage; shaderBytecodeResult.Bytecode = bytecode; return(shaderBytecodeResult); }
public void TestShaderParametersSerialization() { // Test serialization var shaderParameters = new ShaderMixinParameters("Test"); shaderParameters.Set(PropertyInt, 5); var subShaderParameters = new ShaderMixinParameters("Sub"); subShaderParameters.Set(PropertyInt, 6); shaderParameters.Set(PropertySub, subShaderParameters); var subShaderParametersArray = new ShaderMixinParameters[1]; var subShaderParametersArray1 = new ShaderMixinParameters("InArray1"); subShaderParametersArray[0] = subShaderParametersArray1; subShaderParametersArray1.Set(PropertyInt, 7); shaderParameters.Set(PropertySubs, subShaderParametersArray); var memoryStream = new MemoryStream(); var writer = new BinarySerializationWriter(memoryStream); writer.Write(shaderParameters); writer.Flush(); memoryStream.Position = 0; var reader = new BinarySerializationReader(memoryStream); var shaderParametersReloaded = reader.Read<ShaderMixinParameters>(); // They should be strictly equal Assert.That(shaderParametersReloaded.IsSubsetOf(shaderParameters), Is.True); Assert.That(shaderParameters.IsSubsetOf(shaderParametersReloaded), Is.True); // Test subset // Check that by removing one key from the original parameters, the reloaded version is // no longer a subset subShaderParametersArray1.Remove(PropertyInt); Assert.That(shaderParametersReloaded.IsSubsetOf(shaderParameters), Is.False); }
/// <summary> /// Compiles the ShaderMixinSource into a platform bytecode. /// </summary> /// <param name="mixin">The ShaderMixinSource.</param> /// <param name="fullEffectName">The name of the effect.</param> /// <param name="compilerParameters">The parameters used for compilation.</param> /// <param name="modifiedShaders">The list of modified shaders.</param> /// <param name="recentlyModifiedShaders">The list of recently modified shaders.</param> /// <param name="log">The logger.</param> /// <returns>The platform-dependent bytecode.</returns> public abstract EffectBytecode Compile(ShaderMixinSource mixin, string fullEffectName, ShaderMixinParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, LoggerResult log);
public CompilerResults Compile(ShaderSource shaderSource, CompilerParameters compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders) { ShaderMixinSourceTree mixinTree; var shaderMixinGeneratorSource = shaderSource as ShaderMixinGeneratorSource; var mainUsedParameters = new ShaderMixinParameters(); var usedParameters = new List <ShaderMixinParameters>(); string effectName = null; if (shaderMixinGeneratorSource != null) { effectName = shaderMixinGeneratorSource.Name; // getting the effect from the used parameters only makes sense when the source files are the same // TODO: improve this by updating earlyCompilerCache - cache can still be relevant if (modifiedShaders == null || modifiedShaders.Count == 0) { // perform an early test only based on the parameters var foundCompilerResults = GetShaderFromParameters(effectName, compilerParameters); if (foundCompilerResults != null) { var earlyCompilerResults = new CompilerResults(); earlyCompilerResults.Module = string.Format("EffectCompile [{0}]", effectName); earlyCompilerResults.MainBytecode = foundCompilerResults.MainBytecode; earlyCompilerResults.MainUsedParameters = foundCompilerResults.MainUsedParameters; foreach (var foundBytecode in foundCompilerResults.Bytecodes) { earlyCompilerResults.Bytecodes.Add(foundBytecode.Key, foundBytecode.Value); } foreach (var foundUsedParameters in foundCompilerResults.UsedParameters) { earlyCompilerResults.UsedParameters.Add(foundUsedParameters.Key, foundUsedParameters.Value); } return(earlyCompilerResults); } } mixinTree = ShaderMixinManager.Generate(effectName, compilerParameters, out mainUsedParameters, out usedParameters); } else { effectName = "Effect"; var shaderMixinSource = shaderSource as ShaderMixinSource; var shaderClassSource = shaderSource as ShaderClassSource; if (shaderClassSource != null) { shaderMixinSource = new ShaderMixinSource(); shaderMixinSource.Mixins.Add(shaderClassSource); } if (shaderMixinSource != null) { mixinTree = new ShaderMixinSourceTree() { Mixin = shaderMixinSource }; } else { throw new ArgumentException("Unsupported ShaderSource type [{0}]. Supporting only ShaderMixinSource/pdxfx, ShaderClassSource", "shaderSource"); } } // Copy global parameters to used Parameters by default, as it is used by the compiler mainUsedParameters.Set(CompilerParameters.GraphicsPlatformKey, compilerParameters.Platform); mainUsedParameters.Set(CompilerParameters.GraphicsProfileKey, compilerParameters.Profile); mainUsedParameters.Set(CompilerParameters.DebugKey, compilerParameters.Debug); foreach (var parameters in usedParameters) { parameters.Set(CompilerParameters.GraphicsPlatformKey, compilerParameters.Platform); parameters.Set(CompilerParameters.GraphicsProfileKey, compilerParameters.Profile); parameters.Set(CompilerParameters.DebugKey, compilerParameters.Debug); } // Compile the whole mixin tree var compilerResults = new CompilerResults(); compilerResults.Module = string.Format("EffectCompile [{0}]", effectName); var wasCompiled = Compile(string.Empty, effectName, mixinTree, mainUsedParameters, usedParameters, modifiedShaders, recentlyModifiedShaders, compilerResults); if (wasCompiled && shaderMixinGeneratorSource != null) { lock (earlyCompilerCache) { List <CompilerResults> effectCompilerResults; if (!earlyCompilerCache.TryGetValue(effectName, out effectCompilerResults)) { effectCompilerResults = new List <CompilerResults>(); earlyCompilerCache.Add(effectName, effectCompilerResults); } // Register bytecode used parameters so that they are checked when another effect is instanced effectCompilerResults.Add(compilerResults); } } return(compilerResults); }
/// <summary> /// Compile the effect and its children. /// </summary> /// <param name="effectName">The name of the effect (without the base effect name).</param> /// <param name="fullEffectName">The full name of the effect (with the base effect name).</param> /// <param name="mixinTree">The ShaderMixinSourceTree.</param> /// <param name="mainCompilerParameters">The parameters used to create the main effect.</param> /// <param name="compilerParameters">The parameters used to create the child effects</param> /// <param name="modifiedShaders">The list of modified shaders since the beginning of the runtime.</param> /// <param name="recentlyModifiedShaders">The list of modified shaders that have not been replaced yet.</param> /// <param name="compilerResults">The result of the compilation.</param> /// <returns>true if the compilation succeded, false otherwise.</returns> protected virtual bool Compile(string effectName, string fullEffectName, ShaderMixinSourceTree mixinTree, ShaderMixinParameters mainCompilerParameters, List <ShaderMixinParameters> compilerParameters, HashSet <string> modifiedShaders, HashSet <string> recentlyModifiedShaders, CompilerResults compilerResults) { if (mixinTree.Mixin == null) { return(false); } var cp = compilerParameters.FirstOrDefault(x => x.Name == fullEffectName); var bytecode = Compile(mixinTree.Mixin, fullEffectName, cp ?? mainCompilerParameters, modifiedShaders, recentlyModifiedShaders, compilerResults); var wasCompiled = false; if (bytecode != null) { if (effectName == string.Empty) { compilerResults.MainBytecode = bytecode; compilerResults.MainUsedParameters = mainCompilerParameters; } compilerResults.Bytecodes.Add(effectName, bytecode); compilerResults.UsedParameters.Add(effectName, cp); wasCompiled = true; } foreach (var childTree in mixinTree.Children) { var childEffectName = effectName == string.Empty ? childTree.Key : effectName + "." + childTree.Key; var fullChildEffectName = fullEffectName == string.Empty ? childTree.Key : fullEffectName + "." + childTree.Key; wasCompiled |= Compile(childEffectName, fullChildEffectName, childTree.Value, mainCompilerParameters, compilerParameters, modifiedShaders, recentlyModifiedShaders, compilerResults); } return(wasCompiled); }
private EffectBytecodeCompilerResult CompileBytecode(ShaderMixinSource mixinTree, CompilerParameters compilerParameters, ObjectId mixinObjectId, DatabaseFileProvider database, string compiledUrl, ShaderMixinParameters usedParameters) { // Open the database for writing var log = new LoggerResult(); // Note: this compiler is expected to not be async and directly write stuff in localLogger var compiledShader = base.Compile(mixinTree, compilerParameters).WaitForResult(); compiledShader.CompilationLog.CopyTo(log); // If there are any errors, return immediately if (log.HasErrors) { lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return new EffectBytecodeCompilerResult(null, log); } // Compute the bytecodeId var newBytecodeId = compiledShader.Bytecode.ComputeId(); // Check if we really need to store the bytecode lock (bytecodes) { // Using custom serialization to the database to store an object with a custom id // TODO: Check if we really need to write the bytecode everytime even if id is not changed var memoryStream = new MemoryStream(); compiledShader.Bytecode.WriteTo(memoryStream); memoryStream.Position = 0; database.ObjectDatabase.Write(memoryStream, newBytecodeId, true); database.AssetIndexMap[compiledUrl] = newBytecodeId; // Save bytecode Id to the database cache as well memoryStream.SetLength(0); memoryStream.Write((byte[])newBytecodeId, 0, ObjectId.HashSize); memoryStream.Position = 0; database.ObjectDatabase.Write(memoryStream, mixinObjectId, true); if (!bytecodes.ContainsKey(newBytecodeId)) { log.Info("New effect compiled #{0} [{1}] (db: {2})\r\n{3}", effectCompileCount, mixinObjectId, newBytecodeId, usedParameters.ToStringDetailed()); Interlocked.Increment(ref effectCompileCount); // Replace or add new bytecode bytecodes[newBytecodeId] = compiledShader.Bytecode; } } lock (compilingShaders) { compilingShaders.Remove(mixinObjectId); } return compiledShader; }
/// <summary> /// Converts the hlsl code into glsl and stores the result as plain text /// </summary> /// <param name="shaderSource">the hlsl shader</param> /// <param name="entryPoint">the entrypoint function name</param> /// <param name="stage">the shader pipeline stage</param> /// <param name="compilerParameters"></param> /// <param name="reflection">the reflection gathered from the hlsl analysis</param> /// <param name="sourceFilename">the name of the source file</param> /// <returns></returns> public ShaderBytecodeResult Compile(string shaderSource, string entryPoint, ShaderStage stage, ShaderMixinParameters compilerParameters, EffectReflection reflection, string sourceFilename = null) { var isOpenGLES = compilerParameters.Get(CompilerParameters.GraphicsPlatformKey) == GraphicsPlatform.OpenGLES; var shaderBytecodeResult = new ShaderBytecodeResult(); PipelineStage pipelineStage = PipelineStage.None; switch (stage) { case ShaderStage.Vertex: pipelineStage = PipelineStage.Vertex; break; case ShaderStage.Pixel: pipelineStage = PipelineStage.Pixel; break; case ShaderStage.Geometry: shaderBytecodeResult.Error("Geometry stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported"); break; case ShaderStage.Hull: shaderBytecodeResult.Error("Hull stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported"); break; case ShaderStage.Domain: shaderBytecodeResult.Error("Domain stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported"); break; case ShaderStage.Compute: shaderBytecodeResult.Error("Compute stage can't be converted to OpenGL. Only Vertex and Pixel shaders are supported"); break; default: shaderBytecodeResult.Error("Unknown shader profile."); break; } if (shaderBytecodeResult.HasErrors) { return(shaderBytecodeResult); } // Convert from HLSL to GLSL // Note that for now we parse from shader as a string, but we could simply clone effectPass.Shader to avoid multiple parsing. var glslConvertor = new ShaderConverter(isOpenGLES); var glslShader = glslConvertor.Convert(shaderSource, entryPoint, pipelineStage, sourceFilename, shaderBytecodeResult); // Add std140 layout foreach (var constantBuffer in glslShader.Declarations.OfType <ConstantBuffer>()) { constantBuffer.Qualifiers |= new LayoutQualifier(new LayoutKeyValue("std140")); } // Output the result var glslShaderWriter = new HlslToGlslWriter(); if (isOpenGLES) { glslShaderWriter.TrimFloatSuffix = true; glslShaderWriter.GenerateUniformBlocks = false; foreach (var variable in glslShader.Declarations.OfType <Variable>()) { if (variable.Qualifiers.Contains(ParameterQualifier.In)) { variable.Qualifiers.Values.Remove(ParameterQualifier.In); // "in" becomes "attribute" in VS, "varying" in other stages variable.Qualifiers.Values.Add( pipelineStage == PipelineStage.Vertex ? global::SiliconStudio.Shaders.Ast.Glsl.ParameterQualifier.Attribute : global::SiliconStudio.Shaders.Ast.Glsl.ParameterQualifier.Varying); } if (variable.Qualifiers.Contains(ParameterQualifier.Out)) { variable.Qualifiers.Values.Remove(ParameterQualifier.Out); variable.Qualifiers.Values.Add(global::SiliconStudio.Shaders.Ast.Glsl.ParameterQualifier.Varying); } } } // Write shader glslShaderWriter.Visit(glslShader); // Build shader source var glslShaderCode = new StringBuilder(); // Append some header depending on target if (!isOpenGLES) { glslShaderCode .AppendLine("#version 420") .AppendLine(); if (pipelineStage == PipelineStage.Pixel) { glslShaderCode .AppendLine("out vec4 gl_FragData[1];") .AppendLine(); } } if (isOpenGLES) { if (pipelineStage == PipelineStage.Pixel) { glslShaderCode .AppendLine("precision highp float;") .AppendLine(); } } glslShaderCode.Append(glslShaderWriter.Text); var realShaderSource = glslShaderCode.ToString(); // optimize shader var optShaderSource = RunOptimizer(realShaderSource, isOpenGLES, false, pipelineStage == PipelineStage.Vertex); if (!String.IsNullOrEmpty(optShaderSource)) { realShaderSource = optShaderSource; } var rawData = Encoding.ASCII.GetBytes(realShaderSource); var bytecodeId = ObjectId.FromBytes(rawData); var bytecode = new ShaderBytecode(bytecodeId, rawData); bytecode.Stage = stage; shaderBytecodeResult.Bytecode = bytecode; return(shaderBytecodeResult); }
public void TestSimpleChildParams() { var properties = new ShaderMixinParameters(); properties.Set(Test4.TestParameters.TestCount, 0); ShaderMixinParameters usedProperties; var mixin = GenerateMixin("DefaultSimpleChildParams", properties, out usedProperties); mixin.CheckMixin("A", "B", "C"); var childMixin = GenerateMixin("DefaultSimpleChildParams.ChildParamsMixin", properties, out usedProperties); childMixin.CheckMixin("A", "B", "C1"); Assert.IsTrue(childMixin.UsedParameters.ContainsKey(Test4.TestParameters.TestCount)); Assert.AreEqual(0, childMixin.UsedParameters.Get(Test4.TestParameters.TestCount)); }
public void TestMixinAndComposeKeys() { var properties = new ShaderMixinParameters(); var subCompute1Key = TestABC.TestParameters.UseComputeColor2.ComposeWith("SubCompute1"); var subCompute2Key = TestABC.TestParameters.UseComputeColor2.ComposeWith("SubCompute2"); var subComputesKey = TestABC.TestParameters.UseComputeColorRedirect.ComposeWith("SubComputes[0]"); properties.Set(subCompute1Key, true); properties.Set(subComputesKey, true); var mixin = GenerateMixin("test_mixin_compose_keys", properties); mixin.CheckMixin("A"); Assert.AreEqual(3, mixin.Compositions.Count); Assert.IsTrue(mixin.Compositions.ContainsKey("SubCompute1")); Assert.IsTrue(mixin.Compositions.ContainsKey("SubCompute2")); Assert.IsTrue(mixin.Compositions.ContainsKey("SubComputes")); Assert.AreEqual("mixin ComputeColor2", mixin.Compositions["SubCompute1"].ToString()); Assert.AreEqual("mixin ComputeColor", mixin.Compositions["SubCompute2"].ToString()); Assert.AreEqual("[mixin ComputeColorRedirect [{ColorRedirect = mixin ComputeColor2}]]", mixin.Compositions["SubComputes"].ToString()); }
/// <summary> /// Generates the mixin. /// </summary> /// <param name="mixinName">Name of the mixin.</param> /// <param name="properties">The properties that the mixin will use.</param> /// <param name="usedProperties">The properties effectively used by the mixin.</param> /// <returns>ShaderMixinSource.</returns> private static ShaderMixinSourceTree GenerateMixin(string mixinName, ParameterCollection properties, out ShaderMixinParameters usedProperties) { var allUsedProperties = new List <ShaderMixinParameters>(); var mixin = ShaderMixinManager.Generate(mixinName, properties); usedProperties = mixin.UsedParameters; // Verify that output used properties are a subset of input properties Assert.That(usedProperties.IsSubsetOf(properties), Is.True); foreach (var usedProps in allUsedProperties) { Assert.That(usedProps.IsSubsetOf(properties), Is.True); } return(mixin); }