Example #1
0
        /// <summary>
        /// Creates a <see cref="VertexAttribDescription"/> where the format of the data declared
        /// in the shader isn't the same as the format the data will be read as.
        /// </summary>
        /// <param name="attribType">The type of the attribute declared in the shader.</param>
        /// <param name="normalized">Whether the vertex data should be normalized before being loaded into the shader.</param>
        /// <param name="dataBaseType">The base type in which the data will be read from the buffer.</param>
        /// <param name="attribDivisor">The divisor that defines how reading this attribute advances on instanced rendering.</param>
        public VertexAttribDescription(AttributeType attribType, bool normalized, AttributeBaseType dataBaseType, uint attribDivisor = 0)
        {
            ValidateAttribDivisor(attribDivisor);

            if (normalized)
            {
                if (!TrippyUtils.IsVertexAttribIntegerType(dataBaseType))
                {
                    throw new ArgumentException("For normalized vertex attributes, the dataBaseType must be an integer", nameof(dataBaseType));
                }

                if (!(TrippyUtils.IsVertexAttribFloatType(attribType) || TrippyUtils.IsVertexAttribDoubleType(attribType)))
                {
                    throw new ArgumentException("For normalized vertex attributes, the attribType must be a float or a double", nameof(attribType));
                }
            }

            Normalized            = normalized;
            AttribDivisor         = attribDivisor;
            AttribBaseType        = dataBaseType;
            AttribType            = attribType;
            Size                  = TrippyUtils.GetVertexAttribTypeSize(attribType);
            AttribIndicesUseCount = TrippyUtils.GetVertexAttribTypeIndexCount(attribType);
            SizeInBytes           = TrippyUtils.GetVertexAttribSizeInBytes(dataBaseType) * (uint)Size * AttribIndicesUseCount;
        }
        /// <summary>
        /// Attaches a <see cref="RenderbufferObject"/> to this <see cref="FramebufferObject"/> in a specified attachment point.
        /// </summary>
        /// <param name="renderbuffer">The <see cref="RenderbufferObject"/> to attach.</param>
        /// <param name="attachmentPoint">The attachment point to attach the <see cref="RenderbufferObject"/> to.</param>
        public void Attach(RenderbufferObject renderbuffer, FramebufferAttachmentPoint attachmentPoint)
        {
            if (renderbuffer == null)
            {
                throw new ArgumentNullException(nameof(renderbuffer));
            }

            ValidateAttachmentTypeExists(attachmentPoint);
            ValidateAttachmentTypeNotUsed(attachmentPoint);

            if (attachmentPoint == FramebufferAttachmentPoint.Depth && !renderbuffer.IsDepthOnly)
            {
                throw new InvalidFramebufferAttachmentException("When attaching a renderbuffer to a depth attachment point, the renderbuffer's format must be depth-only");
            }

            if (attachmentPoint == FramebufferAttachmentPoint.DepthStencil && !renderbuffer.IsDepthStencil)
            {
                throw new InvalidFramebufferAttachmentException("When attaching a renderbuffer to a depth-stencil attachment point, the renderbuffer's format must be depth-stencil");
            }

            if (attachmentPoint == FramebufferAttachmentPoint.Stencil && !renderbuffer.IsStencilOnly)
            {
                throw new InvalidFramebufferAttachmentException("When attaching a renderbuffer to a stencil attachment point, the renderbuffer's format must be stencil-only");
            }

            if (TrippyUtils.IsFramebufferAttachmentPointColor(attachmentPoint) && !renderbuffer.IsColorRenderableFormat)
            {
                throw new InvalidFramebufferAttachmentException("When attaching a renderbuffer to a color attachment point, the renderbuffer's format must be color-renderable");
            }

            GraphicsDevice.Framebuffer = this;
            GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, (FramebufferAttachment)attachmentPoint, RenderbufferTarget.Renderbuffer, renderbuffer.Handle);
            renderbufferAttachments.Add(new FramebufferRenderbufferAttachment(renderbuffer, attachmentPoint));
        }
Example #3
0
        /// <summary>
        /// Creates a <see cref="Framebuffer2D"/> with the given width, height, and other optional parameters.
        /// </summary>
        /// <param name="graphicsDevice">The <see cref="GraphicsDevice"/> this <see cref="Framebuffer2D"/> will use.</param>
        /// <param name="width">The width of the <see cref="Framebuffer2D"/>'s image.</param>
        /// <param name="height">The height of the <see cref="Framebuffer2D"/>'s image.</param>
        /// <param name="depthStencilFormat">The depth-stencil format for an optional renderbuffer attachment.</param>
        /// <param name="samples">The amount of samples for the <see cref="Framebuffer2D"/>'s image.</param>
        /// <param name="imageFormat">The format of the <see cref="Framebuffer2D"/>'s image.</param>
        /// <param name="useDepthStencilTexture">Whether to use a texture for the depth-stencil buffer instead of a renderbuffer.</param>
        public Framebuffer2D(GraphicsDevice graphicsDevice, uint width, uint height,
                             DepthStencilFormat depthStencilFormat, uint samples = 0,
                             TextureImageFormat imageFormat = TextureImageFormat.Color4b, bool useDepthStencilTexture = false)
        {
            Framebuffer = new FramebufferObject(graphicsDevice);
            Texture     = new Texture2D(graphicsDevice, width, height, false, samples, imageFormat);

            if (depthStencilFormat != DepthStencilFormat.None)
            {
                if (useDepthStencilTexture)
                {
                    TextureImageFormat dsFormat  = TrippyUtils.DepthStencilFormatToTextureFormat(depthStencilFormat);
                    Texture2D          dsTexture = new Texture2D(graphicsDevice, width, height, false, samples, dsFormat);
                    Framebuffer.Attach(dsTexture, TrippyUtils.GetCorrespondingTextureFramebufferAttachmentPoint(dsFormat));
                }
                else
                {
                    RenderbufferObject rbo = new RenderbufferObject(graphicsDevice, width, height, (RenderbufferFormat)depthStencilFormat, samples);
                    Framebuffer.Attach(rbo, TrippyUtils.GetCorrespondingRenderbufferFramebufferAttachmentPoint(rbo.Format));
                }
            }

            Framebuffer.Attach(Texture, FramebufferAttachmentPoint.Color0);
            Framebuffer.UpdateFramebufferData();
        }
Example #4
0
        /// <summary>
        /// Creates a <see cref="VertexAttribDescription"/> that specifies padding for the amount
        /// of bytes used by a specified <see cref="AttributeType"/>.
        /// </summary>
        /// <remarks>
        /// Padding indicators ignore padding based on type that occurs when using compensation
        /// for struct padding (which is the default behavior in <see cref="VertexArray"/>).
        /// </remarks>
        public static VertexAttribDescription CreatePadding(AttributeType attribType)
        {
            TrippyUtils.GetVertexAttribTypeData(attribType, out uint indexUseCount, out int size, out AttributeBaseType baseType);
            uint typeSize = TrippyUtils.GetVertexAttribSizeInBytes(baseType);

            return(new VertexAttribDescription(typeSize * (uint)size * indexUseCount));
        }
Example #5
0
            public unsafe void CallGlVertexAttribPointer(GL gl)
            {
                source.BufferSubset.Buffer.GraphicsDevice.BindBuffer(source.BufferSubset);
                uint offs   = offset + source.BufferSubset.StorageOffsetInBytes;
                uint stride = source.BufferSubset.ElementSize;

                for (uint i = 0; i < source.AttribDescription.AttribIndicesUseCount; i++)
                {
                    if (!source.AttribDescription.Normalized && source.AttribDescription.AttribBaseType == VertexAttribPointerType.Double)
                    {
                        gl.VertexAttribLPointer(index + i, source.AttribDescription.Size, VertexAttribPointerType.Double, stride, (void *)offs);
                    }
                    else if (!source.AttribDescription.Normalized && TrippyUtils.IsVertexAttribIntegerType(source.AttribDescription.AttribType))
                    {
                        gl.VertexAttribIPointer(index + i, source.AttribDescription.Size, source.AttribDescription.AttribBaseType, stride, (void *)offs);
                    }
                    else
                    {
                        gl.VertexAttribPointer(index + i, source.AttribDescription.Size, source.AttribDescription.AttribBaseType, source.AttribDescription.Normalized, stride, (void *)offs);
                    }

                    gl.EnableVertexAttribArray(index + i);
                    if (source.AttribDescription.AttribDivisor != 0)
                    {
                        gl.VertexAttribDivisor(index + i, source.AttribDescription.AttribDivisor);
                    }
                    offs += source.AttribDescription.SizeInBytes / source.AttribDescription.AttribIndicesUseCount;
                }
            }
Example #6
0
        internal ShaderUniform(ShaderProgram owner, int uniformLoc, string name, int size, UniformType type)
        {
            OwnerProgram    = owner;
            UniformLocation = uniformLoc;
            Size            = size;
            UniformType     = type;

            // The name might come as array name for array uniforms.
            // We need to turn the name "arrayUniform[0]" into just "arrayUniform"
            int nameIndexOfThing = name.LastIndexOf('[');

            Name = nameIndexOfThing > 0 ? name.Substring(0, name.Length - nameIndexOfThing + 1) : name;

            IsSamplerType = TrippyUtils.IsUniformSamplerType(type);

            if (IsSamplerType)
            {
                textureValues           = new Texture[size];
                textureLastAppliedUnits = new int[size];
                textureLastAppliedUnits.AsSpan().Fill(-1);
            }
            else
            {
                textureValues           = null;
                textureLastAppliedUnits = null;
            }
        }
Example #7
0
        /// <summary>
        /// Creates a <see cref="VertexAttribDescription"/> where the format of the data declared
        /// in the shader is the same as present in the buffer and no conversion needs to be done.
        /// </summary>
        /// <param name="attribType">The type of attribute declared in the shader.</param>
        /// <param name="attribDivisor">The divisor that defines how reading this attribute advances on instanced rendering.</param>
        public VertexAttribDescription(AttributeType attribType, uint attribDivisor = 0)
        {
            ValidateAttribDivisor(attribDivisor);

            TrippyUtils.GetVertexAttribTypeData(attribType, out AttribIndicesUseCount, out Size, out AttribBaseType);
            SizeInBytes   = TrippyUtils.GetVertexAttribSizeInBytes(AttribBaseType) * (uint)Size * AttribIndicesUseCount;
            AttribType    = attribType;
            Normalized    = false;
            AttribDivisor = attribDivisor;
        }
        /// <summary>
        /// Attaches a <see cref="Texture"/> to this <see cref="FramebufferObject"/> in a specified attachment point.
        /// </summary>
        /// <param name="texture">The <see cref="Texture"/> to attach.</param>
        /// <param name="attachmentPoint">The attachment point to attach the <see cref="Texture"/> to.</param>
        public void Attach(Texture texture, FramebufferAttachmentPoint attachmentPoint)
        {
            if (texture == null)
            {
                throw new ArgumentNullException(nameof(texture));
            }

            ValidateAttachmentTypeExists(attachmentPoint);
            ValidateAttachmentTypeNotUsed(attachmentPoint);

            if (attachmentPoint == FramebufferAttachmentPoint.Depth && !TrippyUtils.IsImageFormatDepthOnly(texture.ImageFormat))
            {
                throw new InvalidFramebufferAttachmentException("When attaching a texture to a depth attachment point, the texture's format must be depth-only");
            }

            if (attachmentPoint == FramebufferAttachmentPoint.DepthStencil && !TrippyUtils.IsImageFormatDepthStencil(texture.ImageFormat))
            {
                throw new InvalidFramebufferAttachmentException("When attaching a texture to a depth-stencil attachment point, the texture's format must be depth-stencil");
            }

            if (attachmentPoint == FramebufferAttachmentPoint.Stencil && !TrippyUtils.IsImageFormatStencilOnly(texture.ImageFormat))
            {
                throw new InvalidFramebufferAttachmentException("When attaching a texture to a stencil attachment point, the texture's format must be stencil-only");
            }

            if (TrippyUtils.IsFramebufferAttachmentPointColor(attachmentPoint) && !TrippyUtils.IsImageFormatColorRenderable(texture.ImageFormat))
            {
                throw new InvalidFramebufferAttachmentException("When attaching a texture to a color attachment point, the texture's format must be color-renderable");
            }

            GraphicsDevice.Framebuffer = this;
            if (texture is Texture1D)
            {
                GL.FramebufferTexture1D(FramebufferTarget.Framebuffer, (FramebufferAttachment)attachmentPoint, texture.TextureType, texture.Handle, 0);
            }
            else if (texture is Texture2D)
            {
                GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, (FramebufferAttachment)attachmentPoint, texture.TextureType, texture.Handle, 0);
            }
            else
            {
                throw new InvalidFramebufferAttachmentException("This texture type cannot be attached to a framebuffer");
            }
            textureAttachments.Add(new FramebufferTextureAttachment(texture, attachmentPoint));
        }
Example #9
0
        /// <summary>
        /// Creates a <see cref="Texture"/> with specified <see cref="TextureType"/> and <see cref="TextureImageFormat"/>.
        /// </summary>
        /// <param name="graphicsDevice">The <see cref="GraphicsDevice"/> this resource will use.</param>
        /// <param name="type">The type of texture (or texture target) the texture will be.</param>
        /// <param name="imageFormat">The type of image format this texture will store.</param>
        internal Texture(GraphicsDevice graphicsDevice, TextureType type, TextureImageFormat imageFormat) : base(graphicsDevice)
        {
            if (!Enum.IsDefined(typeof(TextureType), type))
            {
                throw new FormatException("Invalid texture target");
            }

            if (!Enum.IsDefined(typeof(TextureImageFormat), imageFormat))
            {
                throw new FormatException("Invalid texture image format");
            }

            TextureType = type;
            ImageFormat = imageFormat;
            TrippyUtils.GetTextureFormatEnums(imageFormat, out PixelInternalFormat, out PixelType, out PixelFormat);
            lastBindUnit     = 0;
            IsMipmapped      = false;
            isNotMipmappable = !TrippyUtils.IsTextureTypeMipmappable(type);
            Handle           = GL.GenTexture();
        }
Example #10
0
        private void EnsureAttribsValid(VertexAttribSource[] attribSources)
        {
            if (attribSources.Length == 0)
            {
                throw new ArgumentException("You can't create a " + nameof(VertexArray) + " with no attributes", nameof(attribSources));
            }

            uint attribIndexCount = 0;

            for (int i = 0; i < attribSources.Length; i++)
            {
                attribIndexCount += attribSources[i].AttribDescription.AttribIndicesUseCount;

                if (attribSources[i].AttribDescription.AttribDivisor != 0)
                {
                    if (!GraphicsDevice.IsVertexAttribDivisorAvailable)
                    {
                        throw new PlatformNotSupportedException("Vertex attribute divisors are notsupported on this system");
                    }
                }
            }

            if (!GraphicsDevice.IsDoublePrecisionVertexAttribsAvailable)
            {
                for (int i = 0; i < attribSources.Length; i++)
                {
                    if (TrippyUtils.IsVertexAttribDoubleType(attribSources[i].AttribDescription.AttribType))
                    {
                        throw new PlatformNotSupportedException("Double precition vertex attributes are not supported on this system");
                    }
                }
            }

            if (attribIndexCount > GraphicsDevice.MaxVertexAttribs)
            {
                throw new PlatformNotSupportedException("The current system doesn't support this many vertex attributes");
            }
        }
Example #11
0
        /// <summary>
        /// Compiles shaders using the code from the different XShaderCode fields and creates a
        /// GL Program Object by linking them together. Then, queries the active attributes and
        /// ensures they match the provided ones in <see cref="specifiedAttribs"/>.
        /// </summary>
        /// <param name="graphicsDevice">The <see cref="GraphicsDevice"/> the <see cref="ShaderProgram"/> will use.</param>
        /// <param name="activeAttribs">The active attributes found by querying the linked program.</param>
        /// <param name="getLogs">Whether to get compilation and linking logs from the shaders and program.</param>
        /// <returns>The handle of the newly created GL Program Object.</returns>
        /// <exception cref="ArgumentNullException"/>
        /// <exception cref="InvalidOperationException"/>
        /// <exception cref="ShaderCompilationException"/>
        /// <exception cref="ProgramLinkException"/>
        internal uint CreateInternal(GraphicsDevice graphicsDevice, out ActiveVertexAttrib[] activeAttribs,
                                     out bool hasVs, out bool hasGs, out bool hasFs, bool getLogs = false)
        {
            VertexShaderLog   = null;
            FragmentShaderLog = null;
            GeometryShaderLog = null;
            ProgramLog        = null;

            if (graphicsDevice == null)
            {
                throw new ArgumentNullException(nameof(graphicsDevice));
            }

            if (!HasVertexShader)
            {
                throw new InvalidOperationException("A vertex shader must be specified");
            }

            if (!HasAttribsSpecified)
            {
                throw new InvalidOperationException("Vertex attributes must be specified");
            }

            // glCreateShader returns non-zero values so we can set these as default no problem
            uint vsHandle = 0;
            uint gsHandle = 0;
            uint fsHandle = 0;

            uint programHandle = 0;
            bool success       = false;

            // We encapsulate the logic in a try catch so whether there is an
            // exception or not, we glDeleteShader all the shader handles
            try
            {
                // We create the vertex shader, compile the code and check it's status
                vsHandle = graphicsDevice.GL.CreateShader(ShaderType.VertexShader);
                hasVs    = true;

                graphicsDevice.GL.ShaderSource(vsHandle, VertexShaderCode);
                graphicsDevice.GL.CompileShader(vsHandle);
                graphicsDevice.GL.GetShader(vsHandle, ShaderParameterName.CompileStatus, out int compileStatus);
                if (getLogs || compileStatus == (int)GLEnum.False)
                {
                    VertexShaderLog = graphicsDevice.GL.GetShaderInfoLog(vsHandle);
                    if (compileStatus == (int)GLEnum.False)
                    {
                        throw new ShaderCompilationException(ShaderType.VertexShader, VertexShaderLog);
                    }
                }

                if (HasGeometryShader)
                {
                    // If we have code for it, we create the geometry shader, compile the code and check it's status
                    gsHandle = graphicsDevice.GL.CreateShader(ShaderType.GeometryShader);
                    hasGs    = true;
                    graphicsDevice.GL.ShaderSource(gsHandle, GeometryShaderCode);
                    graphicsDevice.GL.CompileShader(gsHandle);
                    graphicsDevice.GL.GetShader(gsHandle, ShaderParameterName.CompileStatus, out compileStatus);
                    if (getLogs || compileStatus == (int)GLEnum.False)
                    {
                        GeometryShaderLog = graphicsDevice.GL.GetShaderInfoLog(gsHandle);
                        if (compileStatus == (int)GLEnum.False)
                        {
                            throw new ShaderCompilationException(ShaderType.GeometryShader, GeometryShaderLog);
                        }
                    }
                }
                else
                {
                    hasGs = false;
                }

                if (HasFragmentShader)
                {
                    // If we have code for it, we create the fragment shader, compile the code and check it's status
                    fsHandle = graphicsDevice.GL.CreateShader(ShaderType.FragmentShader);
                    hasFs    = true;
                    graphicsDevice.GL.ShaderSource(fsHandle, FragmentShaderCode);
                    graphicsDevice.GL.CompileShader(fsHandle);
                    graphicsDevice.GL.GetShader(fsHandle, ShaderParameterName.CompileStatus, out compileStatus);
                    if (getLogs || compileStatus == (int)GLEnum.False)
                    {
                        FragmentShaderLog = graphicsDevice.GL.GetShaderInfoLog(fsHandle);
                        if (compileStatus == (int)GLEnum.False)
                        {
                            throw new ShaderCompilationException(ShaderType.FragmentShader, FragmentShaderLog);
                        }
                    }
                }
                else
                {
                    hasFs = false;
                }

                // We create the gl program object
                programHandle = graphicsDevice.GL.CreateProgram();

                // We loop through all the attributes declared for the shader and bind them all to the correct location
                uint attribIndex = 0;
                for (uint i = 0; i < specifiedAttribs.Length; i++)
                {
                    // Some attributes use more than 1 location, those ones we bind only once.
                    // A null or empty name means we skip that attrib because the shader doesn't use it.
                    // We still, though, have to advance attribIndex.
                    if (!string.IsNullOrWhiteSpace(specifiedAttribs[i].Name))
                    {
                        graphicsDevice.GL.BindAttribLocation(programHandle, attribIndex, specifiedAttribs[i].Name);
                    }

                    attribIndex += TrippyUtils.GetVertexAttribTypeIndexCount(specifiedAttribs[i].AttribType);
                }

                // We attach all the shaders we have to the program
                graphicsDevice.GL.AttachShader(programHandle, vsHandle);
                if (gsHandle != 0)
                {
                    graphicsDevice.GL.AttachShader(programHandle, gsHandle);
                }
                if (fsHandle != 0)
                {
                    graphicsDevice.GL.AttachShader(programHandle, fsHandle);
                }

                // We link the program and check it's status
                graphicsDevice.GL.LinkProgram(programHandle);
                graphicsDevice.GL.GetProgram(programHandle, ProgramPropertyARB.LinkStatus, out int linkStatus);
                if (getLogs || linkStatus == (int)GLEnum.False)
                {
                    ProgramLog = graphicsDevice.GL.GetProgramInfoLog(programHandle);
                    if (linkStatus == (int)GLEnum.False)
                    {
                        throw new ProgramLinkException(graphicsDevice.GL.GetProgramInfoLog(programHandle));
                    }
                }

                // We detach (and later delete) the shaders. These aren't actually detached
                // nor deleted until the program using them is done with them
                graphicsDevice.GL.DetachShader(programHandle, vsHandle);
                if (gsHandle != 0)
                {
                    graphicsDevice.GL.DetachShader(programHandle, gsHandle);
                }
                if (fsHandle != 0)
                {
                    graphicsDevice.GL.DetachShader(programHandle, fsHandle);
                }

                // We query the vertex attributes that were actually found on the compiled shader program
                activeAttribs = CreateActiveAttribArray(graphicsDevice, programHandle);

                // We ensure the queried vertex attributes match the user-provided ones
                if (!DoVertexAttributesMatch(activeAttribs, specifiedAttribs))
                {
                    throw new InvalidOperationException("The specified vertex attributes don't match the ones declared in the shaders");
                }

                // Done!
                success = true;
                return(programHandle);
            }
            catch
            {
                // If something went wrong, we're not returning a ShaderProgram, but we might have
                // created the GL Program Object depending on what failed, so let's delete that.
                graphicsDevice.GL.DeleteProgram(programHandle);
                throw; // We re-throw the exception.
            }
            finally
            {
                // glDeleteShader calls get ignored if the shader handle is 0
                graphicsDevice.GL.DeleteShader(vsHandle);
                graphicsDevice.GL.DeleteShader(gsHandle);
                graphicsDevice.GL.DeleteShader(fsHandle);
                graphicsDevice.OnShaderCompiled(this, success);
            }
        }
Example #12
0
 /// <summary>
 /// Creates a <see cref="VertexAttribDescription"/> that specifies padding for an
 /// amount of bytes calculated based on the baseType and size parameters.
 /// </summary>
 /// <remarks>
 /// Padding indicators ignore padding based on type that occurs when using compensation
 /// for struct padding (which is the default behavior in <see cref="VertexArray"/>).
 /// </remarks>
 public static VertexAttribDescription CreatePadding(AttributeBaseType baseType, uint size)
 {
     return(new VertexAttribDescription(TrippyUtils.GetVertexAttribSizeInBytes(baseType) * size));
 }
Example #13
0
        /// <summary>
        /// Updates the places where vertex data is read from for this <see cref="VertexArray"/>.
        /// Call this whenever you modify a buffer subset used by this <see cref="VertexArray"/>.
        /// </summary>
        /// <param name="compensateStructPadding">Whether to compensate for struct padding. Default is true.</param>
        /// <param name="paddingPackValue">The struct packing value for compensating for padding. Default is 4.</param>
        public void UpdateVertexAttributes(bool compensateStructPadding = true, uint paddingPackValue = 4)
        {
            // Makes all glVertexAttribPointer calls to specify the vertex attrib data on the VAO and enables the vertex attributes.
            // The parameters of glVertexAttribPointer are calculated based on the VertexAttribSource-s from AttribSources

            GraphicsDevice.VertexArray = this;

            AttribCallDesc[] calls = new AttribCallDesc[attribSources.Length];

            uint attribIndex = 0;

            for (int i = 0; i < calls.Length; i++)
            {
                calls[i] = new AttribCallDesc
                {
                    source        = attribSources[i],
                    index         = attribIndex,
                    originalIndex = i
                };
                attribIndex += calls[i].source.AttribDescription.AttribIndicesUseCount;
            }

            // Sort by buffer object, so all sources that share BufferObject are grouped together.
            // This facilitates calculating the offset values, since we only need to work with one offset at a time
            // rather than save the offset of each buffer simultaneously.
            Array.Sort(calls);
            // The calls array is now sorted. First by buffer handle, secondly by attrib index
            // and lastly, if two items have the same buffer handle and attrib index, they are sorted
            // by the original index they had in the calls array.
            // This logic is implemented in AttribCallDesc.CompareTo()

            if (compensateStructPadding)
            {
                #region CalculateOffsetsWithPadding
                uint offset = 0;
                BufferObjectSubset      prevSubset      = null; // Setting this to null ensures the first for loop will enter the "different subset" if and initialize these variables
                VertexAttribPointerType currentBaseType = 0;

                for (int i = 0; i < calls.Length; i++)
                {
                    if (calls[i].source.BufferSubset != prevSubset)
                    {
                        // it's a different buffer subset, so let's calculate the padding values as for a new, different struct
                        offset          = 0;
                        prevSubset      = calls[i].source.BufferSubset;
                        currentBaseType = calls[i].source.AttribDescription.AttribBaseType;
                        calls[i].offset = 0;
                    }
                    else if (currentBaseType != calls[i].source.AttribDescription.AttribBaseType)
                    {
                        // the base type has changed, let's ensure padding is applied to offset
                        currentBaseType = calls[i].source.AttribDescription.AttribBaseType;
                        if (!calls[i].source.IsPadding)
                        {                                                                                                       // We add the manual padding, unless it is padding added specifically by the user
                            uint packval = Math.Min(TrippyUtils.GetVertexAttribSizeInBytes(currentBaseType), paddingPackValue); // offset should be aligned by the default packing value or the size of the base type
                            offset = (offset + packval - 1) / packval * packval;                                                // Make offset be greater or equal to offset and divisible by packval
                        }
                    }

                    calls[i].offset = offset;
                    offset         += calls[i].source.AttribDescription.SizeInBytes;
                }
                #endregion
            }
            else
            {
                #region CalculateOffsetsWithoutPadding
                uint offset           = 0;
                uint prevBufferHandle = 0;
                for (int i = 0; i < calls.Length; i++)
                {
                    if (prevBufferHandle != calls[i].source.BufferSubset.BufferHandle)
                    {
                        prevBufferHandle = calls[i].source.BufferSubset.BufferHandle;
                        offset           = 0;
                    }

                    calls[i].offset = offset;
                    offset         += calls[i].source.AttribDescription.SizeInBytes;
                }
                #endregion
            }

            for (int i = 0; i < calls.Length; i++)
            {
                calls[i].CallGlVertexAttribPointer(GL);
            }

            GL.BindBuffer(BufferTargetARB.ElementArrayBuffer, IndexBuffer == null ? 0 : IndexBuffer.BufferHandle);
        }