/// <inheritdoc /> public void Copy(int readIndex, int writeIndex, int count) { GL.CopyNamedBufferSubData( readBuffer: Id, writeBuffer: Id, readOffset: new IntPtr(readIndex * ElementSize), writeOffset: new IntPtr(writeIndex * ElementSize), size: count * ElementSize); GlDebug.ThrowIfGlError("copying buffer sub-data"); }
/// <summary> /// Initializes a new instance of the <see cref="GlVertexBufferObject{T}"/> class. SIDE EFFECT: New buffer will be bound to the given target. /// </summary> /// <param name="target">OpenGL buffer target specification.</param> /// <param name="usage">OpenGL buffer usage specification.</param> /// <param name="elementCapacity">The maximum number of elements to be stored in the buffer.</param> /// <param name="elementData">The data to populate the buffer with.</param> public GlVertexBufferObject(BufferTarget target, BufferUsageHint usage, int elementCapacity, T[] elementData) { this.Capacity = elementCapacity; this.Id = GL.GenBuffer(); GlDebug.ThrowIfGlError("creating buffer"); GL.BindBuffer(target, this.Id); // NB: Side effect - leaves this buffer bound. GlDebug.ThrowIfGlError("binding buffer"); GL.BufferData(target, ElementSize * elementCapacity, elementData, usage); GlDebug.ThrowIfGlError("creating buffer data store"); }
public static GlUniformBlockBinding Bind(int programId, string blockName) { GlUniformBlockBinding binding; // Get the block index var uniformBlockIndex = GL.GetUniformBlockIndex(programId, blockName); GlDebug.ThrowIfGlError("getting uniform block index"); if (uniformBlockIndex == (int)All.InvalidIndex) { throw new ArgumentException($"Uniform block '{blockName}' not found.", nameof(blockName)); } // Check if this block already has an assigned binding point. GL.GetActiveUniformBlock(programId, uniformBlockIndex, ActiveUniformBlockParameter.UniformBlockBinding, out int bindingPointIndex); GlDebug.ThrowIfGlError("getting uniform block binding"); if (bindingPointIndex != 0) { // Block already has a binding (might be specified in the shader), so.. // If we've already created a binding ref for this block index, use that. // Otherwise create a new one. if (!BindingsByBindingPoint.TryGetValue(bindingPointIndex, out binding)) { binding = new GlUniformBlockBinding(bindingPointIndex, blockName); } } else { // Block doesn't already have a binding, so.. // If we've already created a binding point for this block name, use that. // Otherwise assign a brand new one. if (!BindingsByBlockName.TryGetValue(blockName, out binding)) { // TODO: Not right. should also skip bindings already in use in OpenGL (because theyre specified in shaders) // that we aren't aware of yet. might be better to not maintain a record ourselves and always just query opengl int firstUnusedBindingPoint = 1; while (BindingsByBindingPoint.ContainsKey(firstUnusedBindingPoint)) { firstUnusedBindingPoint++; } binding = new GlUniformBlockBinding(firstUnusedBindingPoint, blockName); } // Bind this block to the binding point GL.UniformBlockBinding(programId, uniformBlockIndex, binding.BindingPoint); GlDebug.ThrowIfGlError("setting uniform block binding"); } return(binding); }
private void Delete(bool finalizing) { if (!finalizing) { GC.SuppressFinalize(this); } isDeleted = true; GL.DeleteBuffer(Id); GlDebug.ThrowIfGlError("deleting buffer"); }
/// <summary> /// Initializes a new instance of the <see cref="GlUniformBufferObject{T}"/> class. SIDE EFFECT: New buffer will be bound to the UniformBuffer target. /// </summary> /// <param name="programId">ID of a linked program that uses the block that this buffer stores the data for.</param> /// <param name="blockName">The name of the block in the program that this buffer stores the data for.</param> /// <param name="usage">OpenGL buffer usage specification.</param> /// <param name="elementCapacity">The maximum number of elements to be stored in the buffer.</param> /// <param name="elementData">The data to populate the buffer with.</param> /// <remarks> /// TODO: I don't like the fact that we pass program ID and block name here (since this could be used by multiple programs) - would rather pass metadata - GlUniformBlockInfo or something. /// </remarks> internal GlUniformBufferObject(int programId, string blockName, BufferUsageHint usage, int elementCapacity, T[] elementData) { this.setter = GlMarshal.MakeBufferObjectUniformSetter <T>(programId, blockName); this.Capacity = elementCapacity; this.bindingRef = new GlUniformBlockBindingRef(programId, blockName); GL.BindBuffer(BufferTarget.UniformBuffer, this.Id); // NB: Side effect - leaves this buffer bound. GlDebug.ThrowIfGlError("Binding uniform buffer"); var blockIndex = GL.GetUniformBlockIndex(programId, blockName); GlDebug.ThrowIfGlError("Getting uniform block index"); GL.GetActiveUniformBlock(programId, blockIndex, ActiveUniformBlockParameter.UniformBlockDataSize, out elementSize); GlDebug.ThrowIfGlError("Getting uniform block size"); GL.BufferData(BufferTarget.UniformBuffer, elementSize * elementCapacity, elementData, usage); GlDebug.ThrowIfGlError("Creating uniform buffer data store"); // TODO: Not always what we want.. Replace by Bind(int index) in UBO class..? GL.BindBufferBase(BufferRangeTarget.UniformBuffer, bindingRef.BindingPoint, bindingRef.BufferRef.Id); GlDebug.ThrowIfGlError("setting uniform buffer binding"); }
/// <inheritdoc /> public T this[int index] { get { T data = default; GL.GetNamedBufferSubData( buffer: Id, offset: new IntPtr(index * ElementSize), size: ElementSize, data: ref data); GlDebug.ThrowIfGlError("getting buffer sub-data"); return(data); } set { GL.NamedBufferSubData( buffer: Id, offset: new IntPtr(index * ElementSize), size: ElementSize, data: ref value); GlDebug.ThrowIfGlError("setting buffer sub-data"); } }
public GlBuffer() { this.id = GL.GenBuffer(); GlDebug.ThrowIfGlError("generating buffer"); }
/// <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); }