/// <summary> /// Create this ShaderObject. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for creating this object. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="ctx"/> is null. /// </exception> public override void Create(GraphicsContext ctx) { // Create default compilation _CompilationParams = new ShaderCompilerContext(ctx.ShadingVersion); // Base implementation base.Create(ctx); }
/// <summary> /// /// </summary> /// <param name="cctx"></param> /// <returns></returns> internal string GetHashInfo(ShaderCompilerContext cctx) { StringBuilder hashMessage = new StringBuilder(); // Take into account the shader program name hashMessage.Append(Id); // Objects hash if (Objects != null) { foreach (Object programObject in Objects) { hashMessage.Append(programObject); } } // Compilation parameters (common to all shaders?) hashMessage.Append(cctx.ShaderVersion); if (cctx.Includes != null) { foreach (string includePath in cctx.Includes) { hashMessage.Append(includePath); } } hashMessage.Append(cctx.FeedbackVaryingsFormat); return(hashMessage.ToString()); }
/// <summary> /// Create a ProgramTag from a <see cref="Program"/>. /// </summary> /// <param name="program"> /// The <see cref="Program"/> that specifies how to build the shader. /// </param> internal ProgramTag(Program program, ShaderCompilerContext cctx) : this(program) { if (cctx == null) { throw new ArgumentNullException("cctx"); } CompilerContext.Merge(cctx); }
/// <summary> /// Append default source header. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for the compilation process. /// </param> /// <param name="sourceLines"> /// A <see cref="List{String}"/> which represent the current shader object /// source lines. /// </param> /// <param name="version"> /// A <see cref="Int32"/> representing the shader language version to use in generated shader. /// </param> protected void AppendHeader(GraphicsContext ctx, ShaderCompilerContext cctx, List <string> sourceLines, int version) { if (ctx == null) { throw new ArgumentNullException("ctx"); } if (cctx == null) { throw new ArgumentNullException("ctx"); } if (sourceLines == null) { throw new ArgumentNullException("sLines"); } // Prepend required shader version if (version >= 150) { // Starting from GLSL 1.50, profiles are implemented if ((ctx.Flags & GraphicsContextFlags.ForwardCompatible) != 0) { sourceLines.Add(String.Format("#version {0} core\n", version)); } else { sourceLines.Add(String.Format("#version {0} compatibility\n", version)); } } else { sourceLines.Add(String.Format("#version {0}\n", version)); } // #extension List <ShaderExtension> shaderExtensions = new List <ShaderExtension>(Extensions); // Includes generic extension from compiler foreach (ShaderExtension contextShaderExtension in cctx.Extensions) { if (!shaderExtensions.Exists(delegate(ShaderExtension item) { return(item.Name == contextShaderExtension.Name); })) { shaderExtensions.Add(contextShaderExtension); } } // #pragma #if DEBUG // Debug directives //sourceLines.Add("#pragma optimization(off)\n"); //sourceLines.Add("#pragma debug(on)\n"); #else //sourceLines.Add("#pragma optimization(on)\n"); //sourceLines.Add("#pragma debug(off)\n"); #endif }
/// <summary> /// Create a <see cref="ProgramTag"/> used for creating (lazily) a <see cref="ShaderProgram"/>. /// </summary> /// <param name="programId"> /// A <see cref="String"/> that specifies the program identifier. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specifies *additional* parameters to be applied/merged to the default /// compiler parameters. /// </param> /// <returns> /// It returns a <see cref="ProgramTag"/> for creating the program identified with <paramref name="programId"/>. /// </returns> public ProgramTag CreateProgramTag(string programId, ShaderCompilerContext cctx) { Program libraryProgram = GetProgram(programId); if (libraryProgram == null) { throw new ArgumentException("no program with such identifier", "programId"); } return(new ProgramTag(libraryProgram, cctx)); }
/// <summary> /// Create this ShaderObject. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for creating this object. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify compiler parameters. /// </param> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="ctx"/> is null. /// </exception> /// <exception cref="ArgumentNullException"> /// Exception thrown if <paramref name="cctx"/> is null. /// </exception> public virtual void Create(GraphicsContext ctx, ShaderCompilerContext cctx) { if (cctx == null) { throw new ArgumentNullException("cctx"); } // Cache compilation parameters (used by CreateObject) _CompilationParams = cctx; // Base implementation base.Create(ctx); }
/// <summary> /// Create a program from this Program. /// </summary> /// <param name="cctx"></param> /// <returns></returns> public ShaderProgram Create(ShaderCompilerContext cctx) { if (string.IsNullOrEmpty(Id)) { throw new InvalidOperationException("invalid program identifier"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } ShaderProgram shaderProgram = new ShaderProgram(Id); // Attach required objects if (Objects != null) { foreach (Object shaderProgramObject in Objects) { Shader shaderObject = new Shader(shaderProgramObject.Stage); // Load source shaderObject.LoadSource(shaderProgramObject.Resource); // Attach object shaderProgram.Attach(shaderObject); } } // Register attributes semantic if (Attributes != null) { foreach (Attribute attribute in Attributes) { shaderProgram.SetAttributeSemantic(attribute.Name, attribute.Semantic); if (attribute.Location >= 0) { shaderProgram.SetAttributeLocation(attribute.Name, attribute.Location); } } } // Register uniforms semantic if (Uniforms != null) { foreach (Uniform uniform in Uniforms) { shaderProgram.SetUniformSemantic(uniform.Name, uniform.Semantic); } } shaderProgram.Create(cctx); return(shaderProgram); }
/// <summary> /// Generate ShaderObject source. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for the compilation process. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify the information required for compiling this ShaderObject. /// </param> /// <returns> /// It returns a <see cref="List{T}"/> which represent this ShaderObject source. This source text is ready to be compiled. /// </returns> private List <string> GenerateSource(GraphicsContext ctx, ShaderCompilerContext cctx) { if (ctx == null) { throw new ArgumentNullException("ctx"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } if (_SourceStrings == null) { throw new InvalidOperationException("no source loaded"); } List <string> shaderSource = new List <string>(256); string[] shaderSourceStrings = _SourceStrings.ToArray(); // Append imposed header - Every source shall compile with this header AppendHeader(ctx, cctx, shaderSource, cctx.ShaderVersion.VersionId); // Append required #define statments if (cctx.Defines != null) { foreach (string def in cctx.Defines) { shaderSource.Add(String.Format("#define {0} 1\n", def)); Log(" Symbol: {0}", def); } } // Append specific source for composing shader essence AppendSourceStrings(shaderSource, shaderSourceStrings); // Remove comment lines shaderSource = CleanSource(shaderSource); // Preprocessing // Manage #include preprocessor directives in the case GL_ARB_shading_language_include is not supported // When #include are replaced, conditionals are processed too if (ctx.Extensions.ShadingLanguageInclude_ARB == false) { shaderSource = ShaderPreprocessor.Process(shaderSource, cctx, ctx.IncludeLibrary, ShaderPreprocessor.Stage.All); } shaderSource = CleanSource(shaderSource); return(shaderSource); }
/// <summary> /// /// </summary> /// <param name="cctx"></param> /// <returns></returns> internal void GetHashInfo(StringBuilder hashMessage, ShaderCompilerContext cctx) { // Shaders can be compiled at different stages hashMessage.Append(Stage); // All symbols known for this shader are included foreach (string define in cctx.Defines) { if (Symbols.Contains(define) == false) { continue; } hashMessage.Append(define); } }
internal ShaderCompilerContext GetCompilerContext() { ShaderCompilerContext shaderCompilerParams = new ShaderCompilerContext(); foreach (Object shaderProgramObject in Objects) { // Take into account required preprocessor symbols foreach (string preprocessorSymbol in shaderProgramObject.Symbols) { shaderCompilerParams.Defines.Add(preprocessorSymbol); } } return(shaderCompilerParams); }
internal ShaderCompilerContext GetCompilerContext() { ShaderCompilerContext shaderCompilerParams = new ShaderCompilerContext(); // Preprocessor symbols foreach (Object shaderProgramObject in Objects) { foreach (string preprocessorSymbol in shaderProgramObject.Symbols) { shaderCompilerParams.Defines.Add(preprocessorSymbol); } } // Shader extensions shaderCompilerParams.Extensions.AddRange(Extensions); return(shaderCompilerParams); }
/// <summary> /// Process shader source lines to resolve #include directives. /// </summary> /// <param name="shaderSource"> /// A <see cref="IEnumerable{String}"/> that specify the shader source lines. Null items in the enumeration /// will be ignored. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify the compiler parameteres. /// </param> /// <param name="includeLibrary"> /// A <see cref="ShaderIncludeLibrary"/> determining the shader include file system. /// </param> /// <returns> /// It returns the processed source lines <paramref name="shaderSource"/>, but without any #include directive. Each #include /// directive will be replaced by the corresponding text depending on <paramref name="cctx"/>. /// </returns> /// <remarks> /// <para> /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// Exception throw if <paramref name="includeLibrary"/>, <paramref name="cctx"/> or <paramref name="shaderSource"/> is null. /// </exception> private static List <string> ProcessIncludes(List <string> shaderSource, ShaderCompilerContext cctx, ShaderIncludeLibrary includeLibrary) { if (includeLibrary == null) { throw new ArgumentNullException("includeLibrary"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } if (shaderSource == null) { throw new ArgumentNullException("sSource"); } IncludeProcessorContext ictx = new IncludeProcessorContext(); return(ProcessIncludes(shaderSource, cctx, includeLibrary, ictx)); }
/// <summary> /// Process shader source lines to resolve #include directives. /// </summary> /// <param name="includeLibrary"> /// A <see cref="ShaderIncludeLibrary"/> determining the shader include file system. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify the compiler parameteres. /// </param> /// <param name="shaderSource"> /// A <see cref="IEnumerable{String}"/> that specify the shader source lines. Null items in the enumeration /// will be ignored. /// </param> /// <returns> /// It returns the processed source lines <paramref name="shaderSource"/>, but without any #include directive. Each #include /// directive will be replaced by the corresponding text depending on <paramref name="cctx"/>. /// </returns> /// <remarks> /// <para> /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// Exception throw if <paramref name="includeLibrary"/>, <paramref name="cctx"/> or <paramref name="shaderSource"/> is null. /// </exception> public static List <string> Process(ShaderIncludeLibrary includeLibrary, ShaderCompilerContext cctx, List <string> shaderSource) { if (includeLibrary == null) { throw new ArgumentNullException("includeLibrary"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } if (shaderSource == null) { throw new ArgumentNullException("sSource"); } IncludeProcessorContext ictx = new IncludeProcessorContext(); return(Process(includeLibrary, cctx, ictx, shaderSource)); }
/// <summary> /// Process a source using the preprocessor. /// </summary> /// <param name="shaderSource"></param> /// <returns></returns> public static List <string> Process(List <string> shaderSource, ShaderCompilerContext cctx, ShaderIncludeLibrary includeLibrary, Stage stages) { if (shaderSource == null) { throw new ArgumentNullException("shaderSource"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } List <string> processedSource = shaderSource; if ((stages & Stage.Includes) != 0) { processedSource = ProcessIncludes(processedSource, cctx, includeLibrary); } if ((stages & Stage.Conditionals) != 0) { processedSource = ProcessConditionals(processedSource, cctx); } return(processedSource); }
/// <summary> /// Generate ShaderObject source. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for the compilation process. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify the information required for compiling this ShaderObject. /// </param> /// <returns> /// It returns a <see cref="List{T}"/> which represent this ShaderObject source. This source text is ready to be compiled. /// </returns> private List <string> GenerateSource(GraphicsContext ctx, ShaderCompilerContext cctx) { if (ctx == null) { throw new ArgumentNullException("ctx"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } if (_SourceStrings == null) { throw new InvalidOperationException("no source loaded"); } List <string> shaderSource = new List <string>(256); string[] shaderSourceStrings = _SourceStrings.ToArray(); // Append imposed header - Every source shall compile with this header AppendHeader(ctx, cctx, shaderSource, cctx.ShaderVersion.VersionId); // Shader extension behavior if (ctx.Extensions.ShadingLanguageInclude_ARB) { shaderSource.Add("#extension GL_ARB_shading_language_include : require\n"); } if (ctx.Extensions.UniformBufferObject_ARB) { shaderSource.Add("#extension GL_ARB_uniform_buffer_object : enable\n"); } else { shaderSource.Add("#define DISABLE_GL_ARB_uniform_buffer_object\n"); } if (ObjectStage == ShaderStage.Geometry && ctx.Extensions.GeometryShader4_ARB) { shaderSource.Add("#extension GL_ARB_geometry_shader4 : enable\n"); } // Append required #define statments if (cctx.Defines != null) { foreach (string def in cctx.Defines) { shaderSource.Add(String.Format("#define {0}\n", def)); Log(" Symbol: {0}", def); } } // Append specific source for composing shader essence AppendSourceStrings(shaderSource, shaderSourceStrings); // Manage #include preprocessor directives in the case GL_ARB_shading_language_include is not supported if (ctx.Extensions.ShadingLanguageInclude_ARB == false) { shaderSource = ShaderIncludePreprocessor.Process(ctx.IncludeLibrary, cctx, shaderSource); } // Remove comment lines shaderSource = CleanSource(shaderSource); return(shaderSource); }
/// <summary> /// Generate Shader source. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for the compilation process. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify the information required for compiling this Shader. /// </param> /// <returns> /// It returns a <see cref="List{T}"/> which represent this Shader source. This source text is ready to be compiled. /// </returns> private List <string> GenerateSource(GraphicsContext ctx, ShaderCompilerContext cctx) { if (ctx == null) { throw new ArgumentNullException("ctx"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } if (_SourceStrings == null) { throw new InvalidOperationException("no source loaded"); } List <string> sourceLines = new List <string>(); // Initial status: original AppendSourceStrings(sourceLines, _SourceStrings.ToArray()); // Append imposed header - Every source shall compile with this header int headerLine = 0; if (ctx.ShadingVersion.Api == KhronosVersion.ApiGlsl) { // Automatically prepend required shader version, if necessary if (sourceLines.Count > 0 && !Regex.IsMatch(sourceLines[0], @"\s*#version \d+.*")) { int version = cctx.ShaderVersion.VersionId; if (version >= 150) { // Starting from GLSL 1.50, profiles are implemented if ((ctx.Flags & GraphicsContextFlags.ForwardCompatible) != 0) { sourceLines.Insert(headerLine++, $"#version {version} core\n"); } else { sourceLines.Insert(headerLine++, $"#version {version} compatibility\n"); } } else { sourceLines.Insert(headerLine++, $"#version {version}\n"); } } else { headerLine++; // #version already specified by implementor } // #extension if (ctx.Extensions.ShadingLanguageInclude_ARB) { sourceLines.Insert(headerLine++, "#extension GL_ARB_shading_language_include : require\n"); } if (ctx.Extensions.UniformBufferObject_ARB) { sourceLines.Insert(headerLine++, "#extension GL_ARB_uniform_buffer_object : enable\n"); } else { sourceLines.Insert(headerLine++, "#define DISABLE_GL_ARB_uniform_buffer_object\n"); } if (ObjectStage == ShaderType.GeometryShader && ctx.Extensions.GeometryShader4_ARB) { sourceLines.Insert(headerLine++, "#extension GL_ARB_geometry_shader4 : enable\n"); } if (ctx.Extensions.ShaderDrawParameters_ARB) { sourceLines.Insert(headerLine++, "#extension GL_ARB_shader_draw_parameters : enable\n"); } foreach (ShaderExtension shaderExtension in cctx.Extensions) { // Do not include any #extension directive on extensions not supported by driver if (ctx.Extensions.HasExtensions(shaderExtension.Name) == false) { continue; } sourceLines.Insert(headerLine++, $"#extension {shaderExtension.Name} : {shaderExtension.Behavior.ToString().ToLowerInvariant()}\n"); } // #pragma #if DEBUG // Debug directives sourceLines.Insert(headerLine++, "#pragma optimization(off)\n"); sourceLines.Insert(headerLine++, "#pragma debug(on)\n"); #else sourceLines.Insert(headerLine++, "#pragma optimization(on)\n"); sourceLines.Insert(headerLine++, "#pragma debug(off)\n"); #endif } else { switch (ObjectStage) { case ShaderType.FragmentShader: sourceLines.Insert(headerLine++, "precision mediump float;\n"); break; } } // Append required #define statments if (cctx.Defines != null) { foreach (string def in cctx.Defines) { sourceLines.Insert(headerLine++, $"#define {def} 1\n"); Log(" Symbol: {0}", def); } } // Remove comment lines sourceLines = CleanSource(sourceLines); // Preprocessing // Manage #include preprocessor directives in the case GL_ARB_shading_language_include is not supported // When #include are replaced, conditionals are processed too if (!ctx.Extensions.ShadingLanguageInclude_ARB) { sourceLines = ShaderPreprocessor.Process(sourceLines, cctx, ctx.IncludeLibrary, ShaderPreprocessor.Stage.All); sourceLines = CleanSource(sourceLines); } return(sourceLines); }
/// <summary> /// Actually create this ShaderObject resource. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for allocating resources. /// </param> protected override void CreateObject(GraphicsContext ctx) { if (ctx == null) { throw new ArgumentNullException("ctx"); } if (_CompilationParams == null) { throw new InvalidOperationException("no compiler parameters"); } // Using a deep copy of the shader compiler context, since it will be modified by this ShaderProgram // instance and the attached ShaderObject instances ShaderCompilerContext cctx = new ShaderCompilerContext(_CompilationParams); Log("=== Compilation of shader object '{0}'.", _SourcePath); List <string> source = GenerateSource(ctx, cctx); // Source generation! // Set shader source Gl.ShaderSource(ObjectName, source.ToArray()); #if !MONODROID if (ctx.Extensions.ShadingLanguageInclude_ARB) { string[] includePaths = new string[cctx.Includes.Count]; cctx.Includes.CopyTo(includePaths, 0); // Compile shader object (specifying include paths) Gl.CompileShaderIncludeARB(ObjectName, includePaths, null); } else { // Compile shader object (includes are already preprocessed) Gl.CompileShader(ObjectName); } #else // Compile shader object (includes are already preprocessed) Gl.CompileShader(ObjectName); #endif // Check for compilation errors int compilationStatus; Gl.GetShader(ObjectName, ShaderParameterName.CompileStatus, out compilationStatus); if (compilationStatus != Gl.TRUE) { StringBuilder sb = GetInfoLog(); // Stop compilation process Log("Shader object \"{0}\" compilation failed:\n{1}", _SourcePath ?? "<Hardcoded>", sb.ToString()); // Log the source code referred to the shader log Log("Source code for shader '{0}' that has generated the compiler error.", _SourcePath); Log("--------------------------------------------------------------------------------"); uint sourcelineNo = 0; foreach (string sourceline in source) { Log("{0,4} | {1}", ++sourcelineNo, sourceline.Length > 0 ? sourceline.Remove(sourceline.Length - 1, 1) : String.Empty); } Log("--------------------------------------------------------------------------------"); throw new ShaderException("shader object is not valid. Compiler output for {0}: {1}\n", _SourcePath, sb.ToString()); } else { StringBuilder sb = GetInfoLog(); if (sb.Length > 0) { Log("Shader object \"{0}\" compilation warning: {1}", _SourcePath ?? "<Hardcoded>", sb.ToString()); } } _Compiled = true; }
/// <summary> /// Process a source using the preprocessor. /// </summary> /// <param name="shaderSource"></param> /// <returns></returns> private static List <string> ProcessConditionals(List <string> shaderSource, ShaderCompilerContext cctx) { if (shaderSource == null) { throw new ArgumentNullException("shaderSource"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } ShaderPreprocessorParser expressionEvaluator = new ShaderPreprocessorParser(); List <string> processedSource = new List <string>(); // Define system preprocessor symbols // TODO: support __FILE__ and __LINE__ expressionEvaluator.Define("__VERSION__", cctx.ShaderVersion.VersionId.ToString()); if (cctx.ShaderVersion.Api == KhronosVersion.ApiEssl) { expressionEvaluator.Define("GL_ES", "1"); } // Define user preprocessor symbols foreach (string symbol in cctx.Defines) { expressionEvaluator.Define(symbol, "1"); } foreach (string sourceLine in shaderSource) { Match match; if ((match = _RegexPreprocessorExpression.Match(sourceLine)).Success) { string op = match.Groups["Op"].Value; string expr = match.Groups["Expr"].Success ? match.Groups["Expr"].Value : null; // TODO: support empty # directive switch (op) { case "if": if (expr == null) { throw new InvalidOperationException("invalid #if directive"); } expressionEvaluator.If(expr); break; case "ifdef": if (expr == null) { throw new InvalidOperationException("invalid #ifdef directive"); } expressionEvaluator.IfDef(expr); break; case "ifndef": if (expr == null) { throw new InvalidOperationException("invalid #ifndef directive"); } expressionEvaluator.IfNDef(expr); break; case "elif": if (expr == null) { throw new InvalidOperationException("invalid #elif directive"); } expressionEvaluator.ElIf(expr); break; case "else": if (expr != null) { throw new InvalidOperationException("invalid #else directive"); } expressionEvaluator.Else(); break; case "endif": if (expr != null) { throw new InvalidOperationException("invalid #endif directive"); } expressionEvaluator.Endif(); break; case "define": if (expr == null) { throw new InvalidOperationException("invalid #define directive"); } if (expressionEvaluator.IsActive) { expressionEvaluator.Define(expr); processedSource.Add(sourceLine); } break; case "undef": if (expr == null) { throw new InvalidOperationException("invalid #undef directive"); } if (expressionEvaluator.IsActive) { expressionEvaluator.Undef(expr); processedSource.Add(sourceLine); } break; case "line": // TODO: support __LINE__ case "version": // TODO: support __VERSION__ warning (mismatch with cctx.ShaderVersion) case "error": case "pragma": case "extension": // Note: above directives are output as they are, only if the preprocessor is active if (expressionEvaluator.IsActive) { processedSource.Add(sourceLine); } break; default: throw new NotSupportedException("preprocessor token " + op + " not supported"); } } else { if (expressionEvaluator.IsActive) { processedSource.Add(sourceLine); } } } return(processedSource); }
/// <summary> /// Append default source header. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for the compilation process. /// </param> /// <param name="sourceLines"> /// A <see cref="List{String}"/> which represent the current shader object /// source lines. /// </param> /// <param name="version"> /// A <see cref="Int32"/> representing the shader language version to use in generated shader. /// </param> protected void AppendHeader(GraphicsContext ctx, ShaderCompilerContext cctx, List <string> sourceLines, int version) { if (ctx == null) { throw new ArgumentNullException("ctx"); } if (cctx == null) { throw new ArgumentNullException("ctx"); } if (sourceLines == null) { throw new ArgumentNullException("sourceLines"); } if (ctx.ShadingVersion.Api == KhronosVersion.ApiGlsl) { // Prepend required shader version if (version >= 150) { // Starting from GLSL 1.50, profiles are implemented if ((ctx.Flags & GraphicsContextFlags.ForwardCompatible) != 0) { sourceLines.Add(String.Format("#version {0} core\n", version)); } else { sourceLines.Add(String.Format("#version {0} compatibility\n", version)); } } else { sourceLines.Add(String.Format("#version {0}\n", version)); } // #extension if (ctx.Extensions.ShadingLanguageInclude_ARB) { sourceLines.Add("#extension GL_ARB_shading_language_include : require\n"); } if (ctx.Extensions.UniformBufferObject_ARB) { sourceLines.Add("#extension GL_ARB_uniform_buffer_object : enable\n"); } else { sourceLines.Add("#define DISABLE_GL_ARB_uniform_buffer_object\n"); } if (ObjectStage == ShaderType.GeometryShader && ctx.Extensions.GeometryShader4_ARB) { sourceLines.Add("#extension GL_ARB_geometry_shader4 : enable\n"); } if (ctx.Extensions.ShaderDrawParameters_ARB) { sourceLines.Add("#extension GL_ARB_shader_draw_parameters : enable\n"); } foreach (ShaderExtension shaderExtension in cctx.Extensions) { // Do not include any #extension directive on extensions not supported by driver if (ctx.Extensions.HasExtensions(shaderExtension.Name) == false) { continue; } sourceLines.Add(String.Format( "#extension {0} : {1}\n", shaderExtension.Name, shaderExtension.Behavior.ToString().ToLowerInvariant()) ); } // #pragma #if DEBUG // Debug directives sourceLines.Add("#pragma optimization(off)\n"); sourceLines.Add("#pragma debug(on)\n"); #else sourceLines.Add("#pragma optimization(on)\n"); sourceLines.Add("#pragma debug(off)\n"); #endif } else { switch (ObjectStage) { case ShaderType.FragmentShader: sourceLines.Add("precision mediump float;\n"); break; } } }
private static List <string> Process(ShaderIncludeLibrary includeLibrary, ShaderCompilerContext cctx, IncludeProcessorContext ictx, IEnumerable <string> shaderSource) { if (includeLibrary == null) { throw new ArgumentNullException("includeLibrary"); } if (cctx == null) { throw new ArgumentNullException("cctx"); } if (shaderSource == null) { throw new ArgumentNullException("sSource"); } List <string> processedSource = new List <string>(); // Shader includes not supported. Process them manually before submitting shader source text lines. foreach (string line in shaderSource) { // Ignore null items if (line == null) { continue; } if ((_RegexInclude.Match(line)).Success) { ShaderInclude shaderInclude = null; string includePath = ExtractIncludePath(line); string canonicalPath = String.Empty; if (includePath.StartsWith("/") == false) { // If <path> does not start with a forward slash, it is a path relative // to one of the ordered list of initial search points. if ((ictx.CurrentPath != String.Empty) && (_RegexIncludeAngular.Match(line).Success == false)) { // If it is quoted with double quotes in a previously included string, then the first // search point will be the tree location where the previously included // string had been found. If not found there, the search continues at // the beginning of the list of search points, as just described (see comment later). canonicalPath = NormalizeIncludePath(Path.Combine(ictx.CurrentPath, includePath)); if (includeLibrary.IsPathDefined(canonicalPath)) { shaderInclude = includeLibrary.GetInclude(canonicalPath); } } // If this path is quoted with angled brackets, the tree is searched relative to the // first search point in the ordered list, and then relative to each // subsequent search point, in order, until a matching path is found in // the tree. This is also the behavior if it is quoted with double // quotes in an initial (non-included) shader string. if (shaderInclude == null) { foreach (string includeSearchPath in cctx.Includes) { canonicalPath = NormalizeIncludePath(Path.Combine(includeSearchPath, includePath)); if (includeLibrary.IsPathDefined(canonicalPath)) { shaderInclude = includeLibrary.GetInclude(canonicalPath); break; } } } } else { // If <path> starts with a forward slash, whether it is quoted with // double quotes or with angled brackets, the list of search points is // ignored and <path> is looked up in the tree as described in Appendix // A. canonicalPath = includePath; if (includeLibrary.IsPathDefined(canonicalPath) == false) { throw new InvalidOperationException(String.Format("absolute include path \"{0}\" not existing", canonicalPath)); } shaderInclude = includeLibrary.GetInclude(canonicalPath); } if (shaderInclude == null) { throw new InvalidOperationException(String.Format("include path '{0}' not found", includePath)); } // Recurse on included source (it may contain other includes) IncludeProcessorContext ictxRecurse = new IncludeProcessorContext(); System.Diagnostics.Debug.Assert(String.IsNullOrEmpty(canonicalPath) == false); ictxRecurse.CurrentPath = canonicalPath; processedSource.AddRange(Process(includeLibrary, cctx, ictxRecurse, shaderInclude.Source)); } else { processedSource.Add(line); } } return(processedSource); }