示例#1
0
        /// <summary>
        /// Constructs (via Linq expressions) a delegate for setting the default uniform block of a given program from an equivalent .NET struct.
        /// Struct fields and uniforms are matched by name - which must match exactly.
        /// </summary>
        /// <typeparam name="T">The type of .NET struct that is the equivalent of the default uniform block.</typeparam>
        /// <param name="programId">The name of the Open GL program.</param>
        /// <returns>A delegate to invoke to set the default uniform block.</returns>
        /// <remarks>
        /// NB: Much better than reflection every time, but probably less efficient than it could be due to clunky assembly of expressions.
        /// A prime candidate for replacement by source generation.
        /// </remarks>
        public static Action <T> MakeDefaultUniformBlockSetter <T>(int programId)
            where T : struct
        {
            var inputParam = Expression.Parameter(typeof(T));
            var setters    = new List <Expression>();

            var publicFields = typeof(T).GetFields();

            GlDebug.WriteLine($"{typeof(T).FullName} public fields that will be mapped to uniforms by name: {string.Join(", ", publicFields.Select(f => f.Name))}");
            foreach (var field in publicFields)
            {
                var uniformLocation = GL.GetUniformLocation(programId, field.Name);
                if (uniformLocation == -1)
                {
                    throw new ArgumentException($"Uniform struct contains field '{field.Name}', which does not exist as a uniform in this program.");
                }

                if (!DefaultBlockUniformSettersByType.TryGetValue(field.FieldType, out var uniformSetter))
                {
                    throw new ArgumentException($"Uniform struct contains field of unsupported type {field.FieldType}", nameof(T));
                }

                setters.Add(Expression.Invoke(
                                uniformSetter,
                                Expression.Constant(uniformLocation),
                                Expression.Field(inputParam, field)));
            }

            // TODO: Check for any uniforms that we missed, and either throw or debug warn..
            ////GL.GetProgram(programId, GetProgramParameterName.ActiveUniforms, ..)

            return(Expression.Lambda <Action <T> >(Expression.Block(setters), inputParam).Compile());
        }
示例#2
0
        /// <summary>
        /// Constructs (via Linq expressions) a delegate for setting a uniform block of a given program from an equivalent .NET struct.
        /// Struct fields and uniforms are matched by name - which must match exactly.
        /// </summary>
        /// <typeparam name="T">The type of .NET struct that is the equivalent of the uniform block.</typeparam>
        /// <param name="programId">The name of the Open GL program.</param>
        /// <param name="blockName">The name of the uniform block.</param>
        /// <returns>A delegate to invoke to set the uniform block. In order, the parameters are buffer ID, index within the buffer (in multiples of the block - NOT byte offset), and the value to set.</returns>
        public static Action <int, int, T> MakeBufferObjectUniformSetter <T>(int programId, string blockName)
            where T : struct
        {
            var publicFields = typeof(T).GetFields();

            GlDebug.WriteLine($"{typeof(T).FullName} public fields that will be mapped to uniforms by name: {string.Join(", ", publicFields.Select(f => f.Name))}");

            var uniformNames   = publicFields.Select(fi => $"{blockName}.{fi.Name}").ToArray();
            var uniformIndices = new int[uniformNames.Length];

            GL.GetUniformIndices(programId, uniformNames.Length, uniformNames, uniformIndices);
            GlDebug.ThrowIfGlError("getting uniform indices");
            if (uniformIndices.Any(i => i == (int)All.InvalidIndex))
            {
                // TODO: Actually say which ones weren't found
                throw new ArgumentException($"At least one of {string.Join(", ", publicFields.Select(fi => $"{blockName}.{fi.Name}"))} could not be found in the program");
            }

            //// TODO: check for any extras?

            var uniformOffsets = new int[uniformNames.Length];

            GL.GetActiveUniforms(programId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformOffset, uniformOffsets);
            var arrayStrides = new int[uniformNames.Length];

            GL.GetActiveUniforms(programId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformArrayStride, arrayStrides);
            var matrixStrides = new int[uniformNames.Length];

            GL.GetActiveUniforms(programId, uniformIndices.Length, uniformIndices, ActiveUniformParameter.UniformMatrixStride, matrixStrides);

            var pointerParam     = Expression.Parameter(typeof(IntPtr));
            var blockParam       = Expression.Parameter(typeof(T));
            var blockExpressions = new List <Expression>();

            for (int i = 0; i < publicFields.Length; i++)
            {
                //// TODO: the below is the default for non-array, non-matrix types (but should work when stride is zero?)
                //// if an array or matrix type, need to copy it in pieces, paying attention to strides.. How to tell..
                var structureToPtrMethod = typeof(Marshal)
                                           .GetMethod(nameof(Marshal.StructureToPtr), 1, new[] { Type.MakeGenericMethodParameter(0), typeof(IntPtr), typeof(bool) })
                                           .MakeGenericMethod(publicFields[i].FieldType);

                blockExpressions.Add(Expression.Call(
                                         structureToPtrMethod,
                                         Expression.Field(blockParam, publicFields[i]),
                                         Expression.Add(pointerParam, Expression.Constant(uniformOffsets[i])),
                                         Expression.Constant(false)));
            }

            var setFields = Expression.Lambda <Action <IntPtr, T> >(Expression.Block(blockExpressions), pointerParam, blockParam).Compile();

            void Set(int bufferId, int index, T param)
            {
                GL.BindBuffer(BufferTarget.UniformBuffer, bufferId);
                GlDebug.ThrowIfGlError("binding buffer");
                var bufferPtr = GL.MapBuffer(BufferTarget.UniformBuffer, BufferAccess.WriteOnly);

                GlDebug.ThrowIfGlError("mapping buffer");
                setFields(bufferPtr, param);
                GL.UnmapBuffer(BufferTarget.UniformBuffer);
                GlDebug.ThrowIfGlError("unmapping buffer");
            }

            return(Set);
        }