Example #1
0
 /// <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);
 }
Example #4
0
        /// <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));
        }
Example #7
0
        /// <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));
        }
Example #8
0
            /// <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
        }
Example #10
0
        /// <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;
        }
Example #11
0
        /// <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
        }
Example #12
0
        /// <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);
        }