/// <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); }
private string Compile(string shaderSource, string entryPoint, ShaderStage stage, bool isOpenGLES, bool isOpenGLES3, ShaderBytecodeResult shaderBytecodeResult, string sourceFilename = null) { if (isOpenGLES && !isOpenGLES3 && renderTargetCount > 1) { shaderBytecodeResult.Error("OpenGL ES 2 does not support multiple render targets."); } 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(null); } string shaderString = null; var generateUniformBlocks = isOpenGLES && isOpenGLES3; // null entry point for pixel shader means no pixel shader. In that case, we return a default function. if (entryPoint == null && stage == ShaderStage.Pixel && isOpenGLES) { shaderString = "out float fragmentdepth; void main(){ fragmentdepth = gl_FragCoord.z; }"; } else { // 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, isOpenGLES3); var glslShader = glslConvertor.Convert(shaderSource, entryPoint, pipelineStage, sourceFilename, shaderBytecodeResult); if (glslShader == null || shaderBytecodeResult.HasErrors) { return(null); } // Add std140 layout foreach (var constantBuffer in glslShader.Declarations.OfType <ConstantBuffer>()) { if (isOpenGLES3) // TODO: for OpenGL too? { var layoutQualifier = constantBuffer.Qualifiers.OfType <SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier>().FirstOrDefault(); if (layoutQualifier == null) { layoutQualifier = new SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier(); constantBuffer.Qualifiers |= layoutQualifier; } layoutQualifier.Layouts.Add(new LayoutKeyValue("std140")); } else { constantBuffer.Qualifiers |= new LayoutQualifier(new LayoutKeyValue("std140")); } } // Output the result var glslShaderWriter = new HlslToGlslWriter(); if (isOpenGLES) { glslShaderWriter.TrimFloatSuffix = true; glslShaderWriter.GenerateUniformBlocks = generateUniformBlocks; if (!isOpenGLES3) { 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); shaderString = glslShaderWriter.Text; } // Build shader source var glslShaderCode = new StringBuilder(); // Append some header depending on target if (isOpenGLES) { if (isOpenGLES3) { glslShaderCode .AppendLine("#version 300 es") // TODO: 310 version? .AppendLine(); } if (pipelineStage == PipelineStage.Pixel) { glslShaderCode .AppendLine("precision highp float;") .AppendLine(); } } else { glslShaderCode .AppendLine("#version 420") .AppendLine(); } if ((!isOpenGLES || isOpenGLES3) && pipelineStage == PipelineStage.Pixel && renderTargetCount > 0) { // TODO: identifiers starting with "gl_" should be reserved. Compilers usually accept them but it may should be prevented. glslShaderCode .AppendLine("#define gl_FragData _glesFragData") .AppendLine("out vec4 gl_FragData[" + renderTargetCount + "];") .AppendLine(); } glslShaderCode.Append(shaderString); var realShaderSource = glslShaderCode.ToString(); #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP // optimize shader try { var optShaderSource = RunOptimizer(shaderBytecodeResult, realShaderSource, isOpenGLES, isOpenGLES3, pipelineStage == PipelineStage.Vertex); if (!String.IsNullOrEmpty(optShaderSource)) { realShaderSource = optShaderSource; } } catch (Exception e) { shaderBytecodeResult.Warning("Could not run GLSL optimizer:\n{0}", e.Message); } #else shaderBytecodeResult.Warning("GLSL optimized has not been executed because it is currently not supported on this platform."); #endif return(realShaderSource); }
private string Compile(string shaderSource, string entryPoint, ShaderStage stage, bool isOpenGLES, bool isOpenGLES3, ShaderBytecodeResult shaderBytecodeResult, string sourceFilename = null) { if (isOpenGLES && !isOpenGLES3 && renderTargetCount > 1) shaderBytecodeResult.Error("OpenGL ES 2 does not support multiple render targets."); 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 null; string shaderString = null; var generateUniformBlocks = isOpenGLES && isOpenGLES3; // null entry point for pixel shader means no pixel shader. In that case, we return a default function. if (entryPoint == null && stage == ShaderStage.Pixel && isOpenGLES) { shaderString = "void main(){}"; } else { // 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, isOpenGLES3); var glslShader = glslConvertor.Convert(shaderSource, entryPoint, pipelineStage, sourceFilename, shaderBytecodeResult); if (glslShader == null || shaderBytecodeResult.HasErrors) return null; // Add std140 layout foreach (var constantBuffer in glslShader.Declarations.OfType<ConstantBuffer>()) { if (isOpenGLES3) // TODO: for OpenGL too? { var layoutQualifier = constantBuffer.Qualifiers.OfType<SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier>().FirstOrDefault(); if (layoutQualifier == null) { layoutQualifier = new SiliconStudio.Shaders.Ast.Glsl.LayoutQualifier(); constantBuffer.Qualifiers |= layoutQualifier; } layoutQualifier.Layouts.Add(new LayoutKeyValue("std140")); } else { constantBuffer.Qualifiers |= new LayoutQualifier(new LayoutKeyValue("std140")); } } // Output the result var glslShaderWriter = new HlslToGlslWriter(); if (isOpenGLES) { glslShaderWriter.TrimFloatSuffix = true; glslShaderWriter.GenerateUniformBlocks = generateUniformBlocks; if (!isOpenGLES3) { 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); shaderString = glslShaderWriter.Text; } // Build shader source var glslShaderCode = new StringBuilder(); // Append some header depending on target if (isOpenGLES) { if (isOpenGLES3) glslShaderCode .AppendLine("#version 300 es") // TODO: 310 version? .AppendLine(); if (generateUniformBlocks) // TODO: is it really needed? It produces only a warning. glslShaderCode .AppendLine("#extension GL_EXT_gpu_shader4 : enable") .AppendLine("#extension GL_ARB_gpu_shader5 : enable") .AppendLine(); if (pipelineStage == PipelineStage.Pixel) glslShaderCode .AppendLine("precision highp float;") .AppendLine(); } else { glslShaderCode .AppendLine("#version 420") .AppendLine(); } if ((!isOpenGLES || isOpenGLES3) && pipelineStage == PipelineStage.Pixel && renderTargetCount > 0) { // TODO: identifiers starting with "gl_" should be reserved. Compilers usually accept them but it may should be prevented. glslShaderCode .AppendLine("#define gl_FragData _glesFragData") .AppendLine("out vec4 gl_FragData[" + renderTargetCount + "];") .AppendLine(); } glslShaderCode.Append(shaderString); var realShaderSource = glslShaderCode.ToString(); #if SILICONSTUDIO_PLATFORM_WINDOWS_DESKTOP // optimize shader try { var optShaderSource = RunOptimizer(shaderBytecodeResult, realShaderSource, isOpenGLES, isOpenGLES3, pipelineStage == PipelineStage.Vertex); if (!String.IsNullOrEmpty(optShaderSource)) realShaderSource = optShaderSource; } catch (Exception e) { shaderBytecodeResult.Warning("Could not run GLSL optimizer:\n{0}", e.Message); } #else shaderBytecodeResult.Warning("GLSL optimized has not been executed because it is currently not supported on this platform."); #endif return realShaderSource; }