/// <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); }
/// <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 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 || compilationResult.Bytecode == null) { // 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 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 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); }
/// <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); }