/// <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(); // Base implementation base.Create(ctx); }
/// <summary> /// Determine an unique identifier that specify the linked shader program. /// </summary> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> determining the compiler parameteres. /// </param> /// <param name="libraryId"> /// A <see cref="String"/> that identifies the shader object in library. /// </param> /// <returns> /// It returns a string that identify the a shader program classified with <paramref name="libraryId"/> by /// specifying <paramref name="cctx"/> as compiled parameters. /// </returns> internal static string ComputeLibraryHash(ShaderCompilerContext cctx, string libraryId) { StringBuilder hashMessage = new StringBuilder(); // Take into account the shader program name hashMessage.Append(libraryId); // Take into account the shader version hashMessage.Append(cctx.ShaderVersion); // Do NOT take into account the shader program compilation symbols: they are considered // in attached shader objects. // Take into account the shader program include paths foreach (string includePath in cctx.Includes) { hashMessage.AppendFormat("{0}", includePath); } // Hash all information byte[] hashBytes; using (System.Security.Cryptography.HashAlgorithm hash = System.Security.Cryptography.HashAlgorithm.Create("SHA256")) { hashBytes = hash.ComputeHash(Encoding.ASCII.GetBytes(hashMessage.ToString())); } // ConvertItemType has to string return(Convert.ToBase64String(hashBytes)); }
/// <summary> /// Create this ShaderProgram. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for creating this object. /// </param> public override void Create(GraphicsContext ctx) { // Create default compilation, but only if necessary if (ReferenceEquals(CompilationParams, null)) { _CompilationParams = new ShaderCompilerContext(ctx.ShadingVersion); } // Base implementation base.Create(ctx); }
/// <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> /// Construct a ShaderProgram. /// </summary> /// <param name="programName"> /// A <see cref="String"/> that specify the shader program name. /// </param> /// <param name="compilationParams"> /// A <see cref="ShaderCompilerContext"/> /// </param> /// <exception cref="ArgumentException"> /// This exception is thrown if the parameter <paramref name="programName"/> is not a valid name. /// </exception> public ShaderProgram(string programName, ShaderCompilerContext compilationParams) : base(programName) { try { // GraphicsResource allows empty string, enforce check if (String.IsNullOrEmpty(programName)) { throw new ArgumentException("invalid", "programName"); } // Default compilation parameter _CompilationParams = compilationParams ?? new ShaderCompilerContext(); } catch { // Avoid finalizer assertion failure (don't call dispose since it's virtual) GC.SuppressFinalize(this); throw; } }
/// <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> /// Determine an unique identifier that specify the compiled shader object. /// </summary> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> determining the compiler parameteres. /// </param> /// <param name="libraryId"> /// A <see cref="String"/> that identifies the shader object in library. /// </param> /// <param name="sObjectStage"> /// A <see cref="ShaderObject.ShaderStage"/> that specify the shader object stage. /// </param> /// <returns> /// It returns a string that identify the a shader object classified with <paramref name="libraryId"/>, by /// specifying <paramref name="cctx"/> as compiled parameters, for the shader stage <paramref name="sObjectStage"/>. /// </returns> internal static string ComputeCompilerHash(ShaderCompilerContext cctx, string libraryId, ShaderStage sObjectStage) { StringBuilder hashMessage = new StringBuilder(); if (cctx == null) { throw new ArgumentNullException("cctx"); } if (libraryId == null) { throw new ArgumentNullException("libraryId"); } // Take into account the shader object library identifier hashMessage.Append(libraryId); // Take into account the shader object library stage hashMessage.Append(sObjectStage.ToString()); // Take into account the shader version hashMessage.Append(cctx.ShaderVersion); // Take into account the shader program compilation symbols foreach (String define in cctx.Defines) { hashMessage.AppendFormat("{0}", define); } // Take into account the shader program include paths foreach (string includePath in cctx.Includes) { hashMessage.AppendFormat("{0}", includePath); } // Hash all information byte[] hashBytes; using (HashAlgorithm hashAlgorithm = HashAlgorithm.Create("SHA256")) { hashBytes = hashAlgorithm.ComputeHash(Encoding.ASCII.GetBytes(hashMessage.ToString())); } // ConvertItemType has to string return(Convert.ToBase64String(hashBytes)); }
/// <summary> /// Create a program from this Program. /// </summary> /// <returns></returns> public ShaderProgram Create() { if (String.IsNullOrEmpty(Id)) { throw new InvalidOperationException("invalid program identifier"); } ShaderProgram shaderProgram = new ShaderProgram(Id); ShaderCompilerContext shaderCompilerParams = new ShaderCompilerContext(); // Attach required objects foreach (Object shaderProgramObject in Objects) { ShaderObject shaderObject = new ShaderObject(shaderProgramObject.Stage); // Load source shaderObject.LoadSource(shaderProgramObject.Path); // Attach object shaderProgram.AttachShader(shaderObject); // Take into account required preprocessor symbols foreach (string preprocessorSymbol in shaderProgramObject.Symbols) { shaderCompilerParams.Defines.Add(preprocessorSymbol); } } // Set compiler parameters shaderProgram.CompilationParams = shaderCompilerParams; // Register attributes semantic foreach (Attribute attribute in Attributes) { shaderProgram.SetAttributeSemantic(attribute.Name, attribute.Semantic); } return(shaderProgram); }
/// <summary> /// Link this ShaderProgram. /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for linking this ShaderProgram. /// </param> /// <param name="cctx"> /// A <see cref="ShaderCompilerContext"/> that specify additional compiler parameters. /// </param> /// <remarks> /// <para> /// Generate shader program source code, compile and link it. After a successfull /// link, obtain every information about active uniform and input variables. /// </para> /// <para> /// This routine generate the source code of each attached ShaderObject instance and /// compile it. This step is performed only if really required (tendentially every /// shader object is already compiled). /// </para> /// <para> /// After having compiled every attached shader object, it's performed the linkage between /// shader objects. After this process the ShaderProgram instance can be bound to issue /// rendering commands. /// </para> /// </remarks> /// <exception cref="InvalidOperationException"> /// Exception thrown in the case this ShaderProgram is already linked. /// </exception> /// <exception cref="ShaderException"> /// Exception throw in the case this ShaderProgram is not linkable. /// </exception> private void Link(GraphicsContext ctx, ShaderCompilerContext cctx) { CheckCurrentContext(ctx); if (cctx == null) { throw new ArgumentNullException("cctx"); } // Using a deep copy of the shader compiler context, since it will be modified by this ShaderProgram // instance and the attached ShaderObject instances cctx = new ShaderCompilerContext(cctx); #region Compile and Attach Shader Objects // Be sure to take every attached shader uint[] shadersObject = null; int shadersCount; Gl.GetProgram(ObjectName, Gl.ATTACHED_SHADERS, out shadersCount); if (shadersCount > 0) { shadersObject = new uint[shadersCount]; Gl.GetAttachedShaders(ObjectName, out shadersCount, shadersObject); Debug.Assert(shadersCount == shadersObject.Length); } foreach (ShaderObject shaderObject in _ProgramObjects) { // Create shader object, if necessary if (shaderObject.Exists(ctx) == false) { shaderObject.Create(ctx, cctx); } // Do not re-attach the same shader object if ((shadersObject != null) && Array.Exists(shadersObject, delegate(uint item) { return(item == shaderObject.ObjectName); })) { continue; } // Attach shader object Gl.AttachShader(ObjectName, shaderObject.ObjectName); } #endregion #region Transform Feedback Definition IntPtr[] feedbackVaryingsPtrs = null; if ((_FeedbackVaryings != null) && (_FeedbackVaryings.Count > 0)) { sLog.Debug("Feedback varyings ({0}):", cctx.FeedbackVaryingsFormat); sLog.Indent(); foreach (string feedbackVarying in _FeedbackVaryings) { sLog.Debug("- {0}", feedbackVarying); } sLog.Unindent(); if (ctx.Caps.GlExtensions.TransformFeedback2_ARB || ctx.Caps.GlExtensions.TransformFeedback_EXT) { string[] feedbackVaryings = _FeedbackVaryings.ToArray(); // Bug in NVIDIA drivers? Not exactly, but the NVIDIA driver hold the 'feedbackVaryings' pointer until // glLinkProgram is executed, causing linker errors like 'duplicate varying names are not allowed' or garbaging // part of the returned strings via glGetTransformFeedbackVarying feedbackVaryingsPtrs = feedbackVaryings.AllocHGlobal(); // Specify feedback varyings Gl.TransformFeedbackVaryings(ObjectName, feedbackVaryingsPtrs, (int)cctx.FeedbackVaryingsFormat); } else if (ctx.Caps.GlExtensions.TransformFeedback2_NV) { // Nothing to do ATM } else { throw new InvalidOperationException("transform feedback not supported"); } } #endregion #region Bind Fragment Locations if (ctx.Caps.GlExtensions.GpuShader4_EXT) { // Setup fragment locations, where defined foreach (KeyValuePair <string, int> pair in _FragLocations) { if (pair.Value >= 0) { Gl.BindFragDataLocation(ObjectName, (uint)pair.Value, pair.Key); } } } #endregion #region Link Shader Program Objects int lStatus; sLog.Debug("Link shader program {0}", Identifier ?? "<Unnamed>"); // Link shader program Gl.LinkProgram(ObjectName); // Check for linking errors Gl.GetProgram(ObjectName, Gl.LINK_STATUS, out lStatus); // Release feedback varyings unmanaged memory if (feedbackVaryingsPtrs != null) { feedbackVaryingsPtrs.FreeHGlobal(); } if (lStatus != Gl.TRUE) { const int MaxInfoLength = 4096; StringBuilder logInfo = new StringBuilder(MaxInfoLength); int logLength; // Obtain compilation log Gl.GetProgramInfoLog(ObjectName, MaxInfoLength, out logLength, logInfo); // Stop link process StringBuilder sb = new StringBuilder(logInfo.Capacity); string[] compilerLogLines = logInfo.ToString().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string logLine in compilerLogLines) { sb.AppendLine(" $ " + logLine); } sLog.Error("Shader program \"{0}\" linkage failed: {1}", Identifier ?? "<Unnamed>", sb.ToString()); throw new ShaderException("shader program is not valid. Linker output for {0}: {1}\n", Identifier ?? "<Unnamed>", sb.ToString()); } // Set linked flag _Linked = true; #endregion #region Collect Active Program Uniforms int uniformBufferSize, attributeBufferSize; int uniformCount; // Get active uniforms count Gl.GetProgram(ObjectName, Gl.ACTIVE_UNIFORMS, out uniformCount); // Get uniforms maximum length for name Gl.GetProgram(ObjectName, Gl.ACTIVE_UNIFORM_MAX_LENGTH, out uniformBufferSize); // Clear uniform mapping _UniformMap.Clear(); _DefaultBlockUniformSlots = 0; // Collect uniform information for (uint i = 0; i < (uint)uniformCount; i++) { int uniformNameLength, uniformSize, uniformType; // Mono optimize StringBuilder capacity after P/Invoke... ensure enought room StringBuilder uNameBuilder = new StringBuilder(uniformBufferSize + 2); uNameBuilder.EnsureCapacity(uniformBufferSize); // Obtain active uniform informations Gl.GetActiveUniform(ObjectName, i, uniformBufferSize, out uniformNameLength, out uniformSize, out uniformType, uNameBuilder); string uniformName = uNameBuilder.ToString(); // Obtain active uniform location int uLocation = Gl.GetUniformLocation(ObjectName, uniformName); UniformBinding uniformBinding = new UniformBinding(uniformName, i, uLocation, (ShaderUniformType)uniformType); // Map active uniform _UniformMap[uniformName] = uniformBinding; // Keep track of used slot _DefaultBlockUniformSlots += GetUniformSlotCount(uniformBinding.UniformType); } // Log uniform location mapping List <string> uniformNames = new List <string>(_UniformMap.Keys); // Make uniform list invariant respect the used driver (ease log comparation) uniformNames.Sort(); sLog.Debug("Shader program active uniforms:"); foreach (string uniformName in uniformNames) { sLog.Debug("\tUniform {0} (Type: {1}, Location: {2})", uniformName, _UniformMap[uniformName].UniformType, _UniformMap[uniformName].Location); } sLog.Debug("Shader program active uniform slots: {0}", _DefaultBlockUniformSlots); #endregion #region Collect Active Program Inputs // Get active inputs count int activeInputs; Gl.GetProgram(ObjectName, Gl.ACTIVE_ATTRIBUTES, out activeInputs); // Get inputs maximum length for name Gl.GetProgram(ObjectName, Gl.ACTIVE_ATTRIBUTE_MAX_LENGTH, out attributeBufferSize); // Clear input mapping _AttributesMap.Clear(); // Collect input location mapping for (uint i = 0; i < (uint)activeInputs; i++) { StringBuilder nameBuffer = new StringBuilder(attributeBufferSize); int nameLength, size, type; // Mono optimize StringBuilder capacity after P/Invoke... ensure enought room for the current loop nameBuffer.EnsureCapacity(attributeBufferSize); // Obtain active input informations Gl.GetActiveAttrib(ObjectName, i, attributeBufferSize, out nameLength, out size, out type, nameBuffer); // Obtain active input location string name = nameBuffer.ToString(); int location = Gl.GetAttribLocation(ObjectName, name); // Map active input _AttributesMap[name] = new AttributeBinding((uint)location, (ShaderAttributeType)type); } // Log attribute mapping List <string> attributeNames = new List <string>(_AttributesMap.Keys); // Make attribute list invariant respect the used driver (ease log comparation) attributeNames.Sort(); sLog.Debug("Shader program active attributes:"); foreach (string attributeName in attributeNames) { sLog.Debug("\tAttribute {0} (Type: {1}, Location: {2})", attributeName, _AttributesMap[attributeName].Type, _AttributesMap[attributeName].Location); } #endregion #region Collect Fragment Locations if (ctx.Caps.GlExtensions.GpuShader4_EXT) { // Get fragment locations, just in the case automatically assigned foreach (string fragOutputName in new List <string>(_FragLocations.Keys)) { _FragLocations[fragOutputName] = Gl.GetFragDataLocation(ObjectName, fragOutputName); } } #endregion #region Collect Feedback Varyings if ((_FeedbackVaryings != null) && (_FeedbackVaryings.Count > 0)) { if (ctx.Caps.GlExtensions.TransformFeedback2_ARB || ctx.Caps.GlExtensions.TransformFeedback_EXT) { // Map active feedback int feebackVaryings, feebackVaryingsMaxLength; Gl.GetProgram(ObjectName, Gl.TRANSFORM_FEEDBACK_VARYINGS, out feebackVaryings); Gl.GetProgram(ObjectName, Gl.TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, out feebackVaryingsMaxLength); for (uint i = 0; i < feebackVaryings; i++) { StringBuilder sb = new StringBuilder(feebackVaryingsMaxLength); int length = 0, size = 0, type = 0; Gl.GetTransformFeedbackVarying(ObjectName, (uint)i, feebackVaryingsMaxLength, out length, out size, out type, sb); _FeedbacksMap.Add(sb.ToString(), new FeedbackBinding((ShaderAttributeType)type, (uint)size)); } } else if (ctx.Caps.GlExtensions.TransformFeedback2_NV) { // Activate varyings foreach (string feedbackVaryingName in _FeedbackVaryings) { Gl.ActiveVaryingNV(ObjectName, feedbackVaryingName); } // Map active feedback int feebackVaryings, feebackVaryingsMaxLength; Gl.GetProgram(ObjectName, Gl.ACTIVE_VARYINGS_NV, out feebackVaryings); Gl.GetProgram(ObjectName, Gl.ACTIVE_VARYING_MAX_LENGTH_NV, out feebackVaryingsMaxLength); for (uint i = 0; i < feebackVaryings; i++) { StringBuilder sb = new StringBuilder(feebackVaryingsMaxLength * 2); int length = 0, size = 0, type = 0; Gl.GetActiveVaryingNV(ObjectName, (uint)i, feebackVaryingsMaxLength * 2, out length, out size, out type, sb); _FeedbacksMap.Add(sb.ToString(), new FeedbackBinding((ShaderAttributeType)type, (uint)size)); } // Specify feedback varyings List <int> feedbackLocations = new List <int>(); foreach (string feedbackVaryingName in _FeedbackVaryings) { int location = Gl.GetVaryingLocationNV(ObjectName, feedbackVaryingName); if (location >= 0) { feedbackLocations.Add(location); } } Gl.TransformFeedbackVaryingsNV(ObjectName, feedbackLocations.ToArray(), (int)cctx.FeedbackVaryingsFormat); // Map active feedback } Debug.Assert(_FeedbacksMap.Count > 0); // Log feedback mapping sLog.Debug("Shader program active feedbacks:"); foreach (string feedbackName in _FeedbacksMap.Keys) { sLog.Debug("\tFeedback {0} (Type: {1})", feedbackName, _FeedbacksMap[feedbackName].Type); } } #endregion }
/// <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); sLog.Debug("Compilation of shader object '{0}'.", _SourcePath); List <string> source = GenerateSource(ctx, cctx); // Source generation! // Set shader source Gl.ShaderSource(ObjectName, source.ToArray()); if (ctx.Caps.GlExtensions.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); } // Check for compilation errors int cStatus; Gl.GetObjectParameterARB(ObjectName, Gl.COMPILE_STATUS, out cStatus); if (cStatus != Gl.TRUE) { StringBuilder sb = GetInfoLog(); // Stop compilation process sLog.Error("Shader object \"{0}\" compilation failed:\n{1}", _SourcePath ?? "<Hardcoded>", sb.ToString()); // Log the source code referred to the shader log sLog.Error("Source code for shader '{0}' that has generated the compiler error.", _SourcePath); sLog.Error("--------------------------------------------------------------------------------"); uint sourcelineNo = 0; foreach (string sourceline in source) { sLog.Error("{0,4} | {1}", ++sourcelineNo, sourceline.Length > 0 ? sourceline.Remove(sourceline.Length - 1, 1) : String.Empty); } sLog.Error("--------------------------------------------------------------------------------"); 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) { sLog.Warn("Shader object \"{0}\" compilation warning: {1}", _SourcePath ?? "<Hardcoded>", sb.ToString()); } } _Compiled = true; }
/// <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="sVersion"> /// 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 sVersion) { 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 (sVersion >= 150) { // Starting from GLSL 1.50, profiles are implemented if ((ctx.Flags & GraphicsContextFlags.ForwardCompatible) != 0) { sourceLines.Add(String.Format("#version {0} core\n", sVersion)); } else { sourceLines.Add(String.Format("#version {0} compatibility\n", sVersion)); } } else { sourceLines.Add(String.Format("#version {0}\n", sVersion)); } // #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); } } #if false // #extension GL_ARB_shading_language_include (required by framework) shaderExtensions.RemoveAll(delegate(ShaderExtension item) { return(item.Name == "GL_ARB_shading_language_include"); }); if (ctx.Caps.GlExtensions.ShadingLanguageInclude_ARB) { RenderCapabilities.ShadingExtSupport shaderExtension = ctx.Caps.GetShadingExtensionSupport("GL_ARB_shading_language_include"); // ARB_shading_language_include may become a core feature? Who knows... if ((shaderExtension == null) || (!shaderExtension.IsCoreSupported((GraphicsContext.GLSLVersion)sVersion))) { sLines.Add(String.Format("#extension GL_ARB_shading_language_include : require\n")); } } // Remaining extension foreach (ShaderExtension extension in shaderExtensions) { RenderCapabilities.ShadingExtSupport shaderExtension = ctx.Caps.GetShadingExtensionSupport(extension.Name); if (shaderExtension == null) { continue; } switch (extension.Behavior) { case ShaderExtensionBehavior.ForceEnable: if (shaderExtension.Supported) { sLines.Add(String.Format("#extension {0} : enable\n")); } break; default: // Default behavior: enable extension only if the current version is not supported by the actual version used // for compiling the shader object if (shaderExtension.Supported && !shaderExtension.IsCoreSupported((GraphicsContext.GLSLVersion)sVersion)) { sLines.Add(String.Format("#extension {0} : {1}\n", shaderExtension.ExtensionString, extension.Behavior.ToString().ToLower())); } break; } } #endif // #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> /// 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(); if (_SourcePath != null) { sLog.Debug("Generate shader source for '{0}'.", _SourcePath); } // Append imposed header - Every source shall compile with this header AppendHeader(ctx, cctx, shaderSource, cctx.ShaderVersion.VersionId); // Append required shader extensions - ARB_shading_language_include // Note: this extension, if supported, is required by the framework to compile correctly if (ctx.Caps.GlExtensions.ShadingLanguageInclude_ARB) { shaderSource.Add("#extension GL_ARB_shading_language_include : require\n"); } // Append required #define statments if (cctx.Defines != null) { foreach (string def in cctx.Defines) { shaderSource.Add(String.Format("#define {0}\n", def)); } } // Append specific source for composing shader essence AppendSourceStrings(shaderSource, shaderSourceStrings); // Log shader source uint sourcelineNo; sLog.Verbose("Original source code for shader '{0}' (comments hidden).", _SourcePath); sLog.Verbose("--------------------------------------------------------------------------------"); sourcelineNo = 0; foreach (string sourceline in shaderSource) { ++sourcelineNo; if (ShaderSourcePreprocessor.IsCommentLine(sourceline)) { continue; } sLog.Verbose("{0,4} | {1}", ++sourcelineNo, sourceline.Remove(sourceline.Length - 1, 1)); } sLog.Verbose("--------------------------------------------------------------------------------"); // Manage #include preprocessor directives in the case GL_ARB_shading_language_include is not supported if (ctx.Caps.GlExtensions.ShadingLanguageInclude_ARB == false) { shaderSource = ShaderIncludePreprocessor.Process(ctx.IncludeLibrary, cctx, shaderSource); } // Remove comment lines shaderSource.RemoveAll(delegate(string item) { return(ShaderSourcePreprocessor.IsCommentLine(item)); }); sLog.Verbose("Preprocessed source code for shader '{0}' (comments stripped).", _SourcePath); sLog.Verbose("--------------------------------------------------------------------------------"); sourcelineNo = 0; foreach (string sourceline in shaderSource) { sLog.Verbose("{0,4} | {1}", ++sourcelineNo, sourceline.Length > 0 ? sourceline.Remove(sourceline.Length - 1, 1) : String.Empty); } sLog.Verbose("--------------------------------------------------------------------------------"); return(shaderSource); }
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); }