/// <summary> /// Get uniform value (vec3 variable or bvec3 variable). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="x"> /// A <see cref="Single"/> holding the returned uniform variabile data (first component). /// </param> /// <param name="y"> /// A <see cref="Single"/> holding the returned uniform variabile data (second component). /// </param> /// <param name="z"> /// A <see cref="Single"/> holding the returned uniform variabile data (third component). /// </param> public void GetUniform(GraphicsContext ctx, string uniformName, out float x, out float y, out float z) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(ctx, uniformName); if (uniform == null || uniform.Location == -1) { throw new InvalidOperationException(String.Format("uniform {0} is not active", uniformName)); } CheckUniformType(uniform, Gl.FLOAT_VEC3, Gl.BOOL_VEC3); float[] value = new float[4]; // Set uniform value Gl.GetUniform(ObjectName, uniform.Location, value); x = value[0]; y = value[1]; z = value[2]; }
/// <summary> /// Get uniform value (ivec4 variable or bvec4 variable). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="x"> /// A <see cref="Int32"/> holding the returned uniform variabile data (first component). /// </param> /// <param name="y"> /// A <see cref="Int32"/> holding the returned uniform variabile data (second component). /// </param> /// <param name="z"> /// A <see cref="Int32"/> holding the returned uniform variabile data (third component). /// </param> /// <param name="w"> /// A <see cref="Int32"/> holding the returned uniform variabile data (fourth component). /// </param> public void GetUniform(GraphicsContext ctx, string uniformName, out int x, out int y, out int z, out int w) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(ctx, uniformName); if (uniform == null || uniform.Location == -1) { throw new InvalidOperationException(String.Format("uniform {0} is not active", uniformName)); } CheckUniformType(uniform, Gl.INT_VEC4, Gl.BOOL_VEC4); int[] value = new int[4]; // Get uniform value (using int variant) Gl.GetUniform(ObjectName, uniform.Location, value); x = value[0]; y = value[1]; z = value[2]; w = value[3]; }
/// <summary> /// Get uniform value (dvec4 variable). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="x"> /// A <see cref="Double"/> holding the returned uniform variabile data (first component). /// </param> /// <param name="y"> /// A <see cref="Double"/> holding the returned uniform variabile data (second component). /// </param> /// <param name="z"> /// A <see cref="Double"/> holding the returned uniform variabile data (third component). /// </param> /// <param name="w"> /// A <see cref="Double"/> holding the returned uniform variabile data (fourth component). /// </param> public void GetUniform(GraphicsContext ctx, string uniformName, out double x, out double y, out double z, out double w) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(ctx, uniformName); if (uniform == null || uniform.Location == -1) { throw new InvalidOperationException(String.Format("uniform {0} is not active", uniformName)); } CheckUniformType(uniform, Gl.DOUBLE_VEC4); double[] value = new double[4]; // Set uniform value Gl.GetUniform(ObjectName, uniform.Location, value); x = value[0]; y = value[1]; z = value[2]; w = value[3]; }
public DebugLinesMaterial(ITagContainer diContainer) : base(diContainer.GetTag <GraphicsDevice>(), GetPipeline(diContainer)) { Configure() .Add(Projection = new UniformBinding <Matrix4x4>(this)) .Add(View = new UniformBinding <Matrix4x4>(this)) .Add(World = new UniformBinding <Matrix4x4>(this)) .NextBindingSet(); }
public UIMaterial(ITagContainer diContainer, bool isFont) : base(diContainer.GetTag <GraphicsDevice>(), GetPipeline(diContainer, isFont)) { Configure() .Add(Texture = new TextureBinding(this)) .Add(Sampler = new SamplerBinding(this)) .Add(ScreenSize = new UniformBinding <Vector2>(this)) .NextBindingSet(); }
public MapUntexturedMaterial(ITagContainer diContainer) : base(diContainer.GetTag <GraphicsDevice>(), GetPipeline(diContainer)) { Configure() .Add(Projection = new UniformBinding <Matrix4x4>(this)) .Add(View = new UniformBinding <Matrix4x4>(this)) .Add(World = new UniformBinding <Matrix4x4>(this)) .Add(Uniforms = new UniformBinding <ModelStandardMaterialUniforms>(this)) .Add(PixelCounter = new UniformBinding <uint>(this)) .NextBindingSet(); }
public DebugIconMaterial(ITagContainer diContainer) : base(diContainer.GetTag <GraphicsDevice>(), GetPipeline(diContainer)) { Configure() .Add(Texture = new TextureBinding(this)) .Add(Sampler = new SamplerBinding(this)) .Add(Projection = new UniformBinding <Matrix4x4>(this)) .Add(View = new UniformBinding <Matrix4x4>(this)) .Add(World = new UniformBinding <Matrix4x4>(this)) .Add(Uniforms = new UniformBinding <DebugIconUniforms>(this)) .NextBindingSet(); }
public ModelSkinnedMaterial(ITagContainer diContainer) : base(diContainer.GetTag <GraphicsDevice>(), GetPipeline(diContainer)) { Configure() .Add(MainTexture = new TextureBinding(this)) .Add(Sampler = new SamplerBinding(this)) .Add(Projection = new UniformBinding <Matrix4x4>(this)) .Add(View = new UniformBinding <Matrix4x4>(this)) .Add(World = new UniformBinding <Matrix4x4>(this)) .Add(Uniforms = new UniformBinding <ModelStandardMaterialUniforms>(this)) .Add(Pose = new SkeletonPoseBinding(this)) .NextBindingSet(); }
/// <summary> /// Set uniform state variable (variant type). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="m"> /// A <see cref="IMatrix4x4"/> holding the uniform variabile data. /// </param> public void SetVariantUniform(GraphicsContext ctx, string uniformName, IMatrix4x4 m) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(uniformName); switch (uniform.UniformType) { case ShaderUniformType.Mat4x4: SetUniform(ctx, uniformName, (Matrix4x4)m); break; case ShaderUniformType.DoubleMat4x4: SetUniform(ctx, uniformName, (MatrixDouble4x4)m); break; default: throw new ShaderException("unable to set double-precision floating-point matrix 4x4 data to uniform of type {0}", uniform.UniformType); } }
/// <summary> /// Get uniform value (boolean variable). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="v"> /// A <see cref="Boolean"/> holding the returned uniform variabile data. /// </param> public void GetUniform(GraphicsContext ctx, string uniformName, out bool v) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(ctx, uniformName); if (uniform == null || uniform.Location == -1) { throw new InvalidOperationException(String.Format("uniform {0} is not active", uniformName)); } CheckUniformType(uniform, Gl.BOOL); int[] value = new int[1]; // Get uniform value (using int variant) Gl.GetUniform(ObjectName, uniform.Location, value); v = value[0] != 0; }
/// <summary> /// Set uniform state variable (variant type). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="m"> /// A <see cref="Matrix3x3"/> holding the uniform variabile data. /// </param> public void SetVariantUniform(GraphicsContext ctx, string uniformName, Matrix3x3d m) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(ctx, uniformName); switch (uniform.UniformType) { case ShaderUniformType.Mat3x3: SetUniform(ctx, uniformName, (Matrix3x3f)m); break; #if !MONODROID case ShaderUniformType.DoubleMat3x3: SetUniform(ctx, uniformName, m); break; #endif default: throw new ShaderException("unable to set double-precision floating-point matrix 3x3 data to uniform of type {0}", uniform.UniformType); } }
/// <summary> /// Set uniform variable from double-precision floating-point data (variant type). /// </summary> /// <param name="ctx"> /// A <see cref="GraphicsContext"/> used for operations. /// </param> /// <param name="uniformName"> /// A <see cref="String"/> that specify the variable name in the shader source. /// </param> /// <param name="x"> /// A <see cref="Double"/> holding the uniform variabile data (first component). /// </param> /// <param name="y"> /// A <see cref="Double"/> holding the uniform variabile data (second component). /// </param> /// <param name="z"> /// A <see cref="Double"/> holding the uniform variabile data (third component). /// </param> /// <param name="w"> /// A <see cref="Double"/> holding the uniform variabile data (fourth component). /// </param> /// <remarks> /// <para> /// The uniform variable type could be one of the following : /// - float, vec2, vec3, vec4 /// - double, dvec2, dvec3, dvec4 /// - int, ivec2, ivec3, ivec4 /// - uint, uvec2, uvec3, uvec4 /// </para> /// <para> /// In the case the uniofmr variable length is less than 4, the higher components specified as /// arguments are ignored. The single-precision floating-point data is converted accordingly /// to the uniform variable type. /// </para> /// </remarks> public void SetVariantUniform(GraphicsContext ctx, string uniformName, double x, double y, double z, double w) { if (ctx == null) { throw new ArgumentNullException("ctx"); } UniformBinding uniform = GetUniform(ctx, uniformName); switch (uniform.UniformType) { case ShaderUniformType.Float: SetUniform(ctx, uniformName, (float)x); break; case ShaderUniformType.Vec2: SetUniform(ctx, uniformName, (float)x, (float)y); break; case ShaderUniformType.Vec3: SetUniform(ctx, uniformName, (float)x, (float)y, (float)z); break; case ShaderUniformType.Vec4: SetUniform(ctx, uniformName, (float)x, (float)y, (float)z, (float)w); break; #if !MONODROID case ShaderUniformType.Double: SetUniform(ctx, uniformName, x); break; case ShaderUniformType.DoubleVec2: SetUniform(ctx, uniformName, x, y); break; case ShaderUniformType.DoubleVec3: SetUniform(ctx, uniformName, x, y, z); break; case ShaderUniformType.DoubleVec4: SetUniform(ctx, uniformName, x, y, z, w); break; #endif case ShaderUniformType.Int: SetUniform(ctx, uniformName, (int)x); break; case ShaderUniformType.IntVec2: SetUniform(ctx, uniformName, (int)x, (int)y); break; case ShaderUniformType.IntVec3: SetUniform(ctx, uniformName, (int)x, (int)y, (int)z); break; case ShaderUniformType.IntVec4: SetUniform(ctx, uniformName, (int)x, (int)y, (int)z); break; case ShaderUniformType.UInt: SetUniform(ctx, uniformName, (uint)x); break; case ShaderUniformType.UIntVec2: SetUniform(ctx, uniformName, (uint)x, (uint)y); break; case ShaderUniformType.UIntVec3: SetUniform(ctx, uniformName, (uint)x, (uint)y, (uint)z); break; case ShaderUniformType.UIntVec4: SetUniform(ctx, uniformName, (uint)x, (uint)y, (uint)z); break; default: throw new ShaderException("unable to set double-precision floating-point data to uniform os type {0}", uniform.UniformType); } }
/// <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 }
public GlobalBindingPair(UniformBinding binding, ConstantBufferDataProvider dataProvider) { Binding = binding; DataProvider = dataProvider; }
/// <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 }
private static void CheckUniformType(UniformBinding uniform, params int[] uniformRequestTypes) { if (uniform == null) throw new ArgumentNullException("uniform"); if (uniformRequestTypes == null) throw new ArgumentNullException("uniformRequestTypes"); /* ! Check: required uniform type shall correspond */ foreach (int uniformReqtype in uniformRequestTypes) if ((int)uniform.UniformType == uniformReqtype) return; // 3.3.0 NVIDIA 310.44 confuse float uniforms (maybe in structs?) as vec4 if (uniform.UniformType == ShaderUniformType.Vec4) return; throw new InvalidOperationException("uniform type mismatch"); }