Exemplo n.º 1
0
        public void Shader_TestCompilationParam_Defines()
        {
            using (Shader shader = new Shader(ShaderType.VertexShader)) {
                ShaderCompilerContext cctx = new ShaderCompilerContext()
                {
                    Defines = new List <string> {
                        "TEST_ENABLED"
                    }
                };

                shader.LoadSource(new [] {
                    "void main() {",
                    "#if TEST_ENABLED",
                    "	gl_Position = vec4(0, 0, 0, 1);",
                    "#else",
                    "	gl_Position = vec4(0, 0, 0, 1)",
                    "#endif",
                    "}",
                });
                Assert.IsFalse(shader.IsCompiled);

                // A #define TEST_ENABLED 1 is defined before the loaded source
                Assert.DoesNotThrow(() => shader.Create(_Context, cctx));
                Assert.IsTrue(shader.IsCompiled);

                // Requesting a specific shader version, automatic #version respect it
                Assert.IsTrue(Array.Exists(shader.CompiledStrings, (string item) => item == "#define TEST_ENABLED 1\n"));
            }
        }
		/// <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));
		}
Exemplo n.º 3
0
		/// <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;
			}
		}
Exemplo n.º 4
0
        public void Shader_TestCompilationParam_VersionWithProfile()
        {
            using (Shader shader = new Shader(ShaderType.VertexShader)) {
                ShaderCompilerContext cctx = new ShaderCompilerContext()
                {
                    ShaderVersion = new GlslVersion(1, 5, GlslVersion.ApiGlsl)
                };

                shader.LoadSource(new [] {
                    "void main() {",
                    "	gl_Position = vec4(0, 0, 0, 1);",
                    "}",
                });
                Assert.IsFalse(shader.IsCompiled);

                Assert.DoesNotThrow(() => shader.Create(_Context, cctx));
                Assert.IsTrue(shader.IsCompiled);

                // Requesting a specific shader version, automatic #version respect it
                Assert.AreEqual("#version 150 compatibility\n", shader.CompiledStrings[0]);
            }
        }
Exemplo n.º 5
0
        public void Shader_TestCompilationParam()
        {
            using (Shader shader = new Shader(ShaderType.VertexShader)) {
                ShaderCompilerContext cctx = null;

                cctx = new ShaderCompilerContext();
                shader.LoadSource(new [] {
                    "void main() {",
                    "	gl_Position = vec4(0, 0, 0, 1);",
                    "}",
                });
                Assert.IsFalse(shader.IsCompiled);

                // Compiler context is required (use Create(GraphicsContext) instead)
                Assert.Throws <ArgumentNullException>(() => shader.Create(_Context, null));
                Assert.IsFalse(shader.IsCompiled);

                // Default compiler context let compile
                Assert.DoesNotThrow(() => shader.Create(_Context, new ShaderCompilerContext()));
                Assert.IsTrue(shader.IsCompiled);
            }
        }
Exemplo n.º 6
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);
			}
Exemplo n.º 7
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;
		}
Exemplo n.º 8
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);
		}
Exemplo n.º 9
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));
		}
Exemplo n.º 10
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);
		}
Exemplo n.º 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
		}
Exemplo n.º 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);
		}
Exemplo n.º 14
0
		/// <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)
		{
			if (ctx == null)
				throw new ArgumentNullException("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
			
			// Ensure cached shader objects
			foreach (ShaderObject shaderObject in _ProgramObjects) {
				// Create shader object, if necessary
				if (shaderObject.Exists(ctx) == false)
					shaderObject.Create(ctx, cctx);
				// 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 untill
					// 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
		}
Exemplo n.º 15
0
		/// <summary>
		/// Create this ShaderProgram.
		/// </summary>
		/// <param name="ctx">
		/// A <see cref="GraphicsContext"/> used for creating this object.
		/// </param>
		public override void Create(GraphicsContext ctx)
		{
			if (ctx == null)
				throw new ArgumentNullException("ctx");

			// Create default compilation, but only if necessary
			if (ReferenceEquals(CompilationParams, null))
				_CompilationParams = new ShaderCompilerContext(ctx.ShadingVersion);
			// Base implementation
			base.Create(ctx);
		}
Exemplo n.º 16
0
		/// <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));
		}