/// <summary>
        /// Binds the geometry stream's buffers to the device in preparation for rendering.
        /// </summary>
        private void BindBuffers(UInt32?program, UInt32?offset)
        {
            unsafe
            {
                var previousBuffer = (uint)OpenGLState.GL_ARRAY_BUFFER_BINDING;

                if (IsUsingVertexArrayObject)
                {
                    using (OpenGLState.ScopedBindVertexArrayObject(vao, glElementArrayBufferBinding ?? 0, force: !gl.IsVertexAttribBindingAvailable))
                    {
                        if (program.HasValue)
                        {
                            DisableVertexAttributesOnCachedProgram();
                        }

                        if (gl.IsVertexAttribBindingAvailable)
                        {
                            for (int i = 0; i < vbuffers.Count; i++)
                            {
                                var binding = vbuffers[i];
                                BindVertexAttributesForBuffer_NewAPI(binding.VertexBuffer,
                                                                     (UInt32)i, (UInt32)binding.InstanceFrequency, program, offset);
                            }
                        }
                        else
                        {
                            for (int i = 0; i < vbuffers.Count; i++)
                            {
                                var binding = vbuffers[i];
                                BindVertexAttributesForBuffer_OldAPI(binding.VertexBuffer,
                                                                     (UInt32)i, (UInt32)binding.InstanceFrequency, program, offset);
                            }
                        }
                    }
                }
                else
                {
                    if (program.HasValue)
                    {
                        DisableVertexAttributesOnCachedProgram();
                    }

                    for (int i = 0; i < vbuffers.Count; i++)
                    {
                        var binding = vbuffers[i];
                        BindVertexAttributesForBuffer_OldAPI(binding.VertexBuffer,
                                                             (UInt32)i, (UInt32)binding.InstanceFrequency, program, offset);
                    }

                    OpenGLState.BindArrayBuffer(previousBuffer);
                }
            }
        }
        /// <inheritdoc/>
        public override void Attach(IndexBuffer ibuffer)
        {
            Contract.Require(ibuffer, nameof(ibuffer));
            Contract.EnsureNot(HasIndices, UltravioletStrings.GeometryStreamAlreadyHasIndices);
            Contract.EnsureNotDisposed(this, Disposed);

            Ultraviolet.ValidateResource(ibuffer);

            var oglIndexBuffer     = (OpenGLIndexBuffer)ibuffer;
            var oglIndexBufferName = oglIndexBuffer.OpenGLName;

            this.ibuffer = oglIndexBuffer;

            if (IsUsingVertexArrayObject)
            {
                using (OpenGLState.ScopedBindVertexArrayObject(vao, 0, force: true))
                {
                    OpenGLState.BindElementArrayBuffer(oglIndexBufferName);
                }
            }

            this.glElementArrayBufferBinding = oglIndexBufferName;
            this.indexBufferElementType      = ibuffer.IndexElementType;
        }
        /// <summary>
        /// Binds the specified buffer's vertex attributes to the currently cached program using the new API.
        /// </summary>
        private unsafe void BindVertexAttributesForBuffer_NewAPI(OpenGLVertexBuffer vbuffer, UInt32 binding, UInt32 frequency, UInt32?program, UInt32?offset)
        {
            using (OpenGLState.ScopedBindVertexArrayObject(vao, glElementArrayBufferBinding ?? 0))
            {
                if (program.HasValue || offset.HasValue)
                {
                    gl.VertexArrayVertexBuffer(vao, binding, vbuffer.OpenGLName, (IntPtr)(offset ?? 0), vbuffer.VertexDeclaration.VertexStride);
                    gl.ThrowIfError();
                }

                if (program.HasValue)
                {
                    gl.VertexArrayBindingDivisor(vao, binding, frequency);
                    gl.ThrowIfError();

                    var position = 0u;
                    foreach (var element in vbuffer.VertexDeclaration)
                    {
                        var name      = GetVertexAttributeNameFromUsage(element.Usage, element.Index);
                        var size      = 0;
                        var stride    = 0;
                        var normalize = false;
                        var type      = GetVertexFormatGL(element.Format, out size, out stride, out normalize);

                        var category = OpenGLAttribCategory.Single;
                        var location = (UInt32)OpenGLState.CurrentProgram.GetAttribLocation(name, out category);
                        if (location >= 0)
                        {
                            gl.VertexArrayAttribBinding(vao, location, binding);
                            gl.ThrowIfError();

                            gl.EnableVertexArrayAttrib(vao, location);
                            gl.ThrowIfError();

                            unsafe
                            {
                                switch (category)
                                {
                                case OpenGLAttribCategory.Single:
                                {
                                    gl.VertexArrayAttribFormat(vao, location, size, type, normalize, position);
                                    gl.ThrowIfError();
                                }
                                break;

                                case OpenGLAttribCategory.Double:
                                {
                                    if (!gl.IsDoublePrecisionVertexAttribAvailable)
                                    {
                                        throw new NotSupportedException(OpenGLStrings.DoublePrecisionVAttribsNotSupported);
                                    }

                                    gl.VertexArrayAttribLFormat(vao, location, size, type, position);
                                    gl.ThrowIfError();
                                }
                                break;

                                case OpenGLAttribCategory.Integer:
                                {
                                    if (!gl.IsIntegerVertexAttribAvailable)
                                    {
                                        throw new NotSupportedException(OpenGLStrings.IntegerVAttribsNotSupported);
                                    }

                                    gl.VertexArrayAttribIFormat(vao, location, size, type, position);
                                    gl.ThrowIfError();
                                }
                                break;
                                }
                            }
                        }

                        position += (uint)stride;
                    }
                }
            }
        }