Exemplo n.º 1
0
        /// <summary>
        /// Sets the data contained by the vertex buffer.
        /// </summary>
        private void SetDataInternal <T>(T[] data, Int32 offsetInBytes, Int32 countInBytes, SetDataOptions options)
        {
            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);

            try
            {
                using (OpenGLState.ScopedBindElementArrayBuffer(buffer))
                {
                    var isPartialUpdate = (offsetInBytes > 0 || countInBytes < SizeInBytes);
                    var isDiscarding    = (options == SetDataOptions.Discard);
                    if (isDiscarding || !isPartialUpdate)
                    {
                        gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, this.size, isPartialUpdate ? null : handle.AddrOfPinnedObject().ToPointer(), usage);
                        gl.ThrowIfError();
                    }

                    if (isPartialUpdate)
                    {
                        gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)offsetInBytes, (IntPtr)countInBytes, handle.AddrOfPinnedObject().ToPointer());
                        gl.ThrowIfError();
                    }
                }
            }
            finally
            {
                handle.Free();
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Sets the data contained by the index buffer.
        /// </summary>
        /// <param name="data">An array containing the data to set in the index buffer.</param>
        /// <param name="offset">The offset into <paramref name="data"/> at which to begin setting elements into the buffer.</param>
        /// <param name="count">The number of elements from <paramref name="data"/> to set into the buffer.</param>
        /// <param name="options">A hint to the underlying driver indicating whether data will be overwritten by this operation.</param>
        public override void SetData <T>(T[] data, Int32 offset, Int32 count, SetDataOptions options)
        {
            Contract.Require(data, nameof(data));
            Contract.EnsureRange(count > 0, nameof(count));
            Contract.EnsureRange(offset >= 0 && offset + count <= data.Length, nameof(offset));
            Contract.Ensure(count <= IndexCount, OpenGLStrings.DataTooLargeForBuffer);

            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);

            try
            {
                using (OpenGLState.ScopedBindElementArrayBuffer(buffer))
                {
                    if (options == SetDataOptions.Discard)
                    {
                        gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, this.size, null, usage);
                        gl.ThrowIfError();
                    }

                    var elementsize = GetElementSize();
                    var start       = new IntPtr(offset * elementsize);
                    var size        = new IntPtr(elementsize * count);

                    gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, start, size, handle.AddrOfPinnedObject().ToPointer());
                    gl.ThrowIfError();
                }
            }
            finally
            {
                handle.Free();
            }
        }
        /// <inheritdoc/>
        protected override void Dispose(Boolean disposing)
        {
            if (Disposed)
            {
                return;
            }

            if (disposing)
            {
                if (!Ultraviolet.Disposed)
                {
                    if (vao != 0)
                    {
                        Ultraviolet.QueueWorkItem((state) =>
                        {
                            var vaoName = ((OpenGLGeometryStream)state).vao;

                            gl.DeleteVertexArray(vaoName);
                            gl.ThrowIfError();

                            OpenGLState.DeleteVertexArrayObject(vaoName, 0, glElementArrayBufferBinding ?? 0);
                        }, this);
                    }
                }
                vbuffers.Clear();
                ibuffer = null;
            }

            base.Dispose(disposing);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Reallocates the renderbuffer's storage.
        /// </summary>
        /// <param name="width">The renderbuffer's width in pixels.</param>
        /// <param name="height">The renderbuffer's height in pixels.</param>
        private void AllocateRenderbufferStorage(Int32 width, Int32 height)
        {
            using (OpenGLState.ScopedBindRenderbuffer(renderbuffer, true))
            {
                switch (format)
                {
                case RenderBufferFormat.Color:
                    gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.IsGLES2 ? gl.GL_RGBA4 : gl.GL_RGBA8, width, height);
                    gl.ThrowIfError();
                    break;

                case RenderBufferFormat.Depth24Stencil8:
                    gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH24_STENCIL8, width, height);
                    gl.ThrowIfError();
                    break;

                case RenderBufferFormat.Depth32:
                    gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH_COMPONENT32, width, height);
                    gl.ThrowIfError();
                    break;

                case RenderBufferFormat.Depth16:
                    gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH_COMPONENT16, width, height);
                    gl.ThrowIfError();
                    break;

                case RenderBufferFormat.Stencil8:
                    gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_STENCIL_INDEX8, width, height);
                    break;

                default:
                    throw new NotSupportedException("format");
                }
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Initializes a new instance of the OpenGLRenderTarget2D class.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="width">The render target's width in pixels.</param>
        /// <param name="height">The render target's height in pixels.</param>
        /// <param name="usage">A <see cref="RenderTargetUsage"/> value specifying whether the
        /// render target's data is discarded or preserved when it is bound to the graphics device.</param>
        /// <param name="buffers">The collection of render buffers to attach to the target.</param>
        public OpenGLRenderTarget2D(UltravioletContext uv, Int32 width, Int32 height, RenderTargetUsage usage, IEnumerable <RenderBuffer2D> buffers = null)
            : base(uv)
        {
            Contract.EnsureRange(width > 0, nameof(width));
            Contract.EnsureRange(height > 0, nameof(height));

            // NOTE: If we're in an older version of GLES, we need to use glFramebufferTexture2D()
            glFramebufferTextureIsSupported = !gl.IsGLES || gl.IsVersionAtLeast(3, 2);

            var framebuffer = 0u;

            uv.QueueWorkItemAndWait(() =>
            {
                using (OpenGLState.ScopedCreateFramebuffer(out framebuffer))
                {
                    if (buffers != null)
                    {
                        foreach (OpenGLRenderBuffer2D buffer in buffers)
                        {
                            AttachRenderBuffer(buffer);
                        }
                    }
                }
            });

            this.width             = width;
            this.height            = height;
            this.framebuffer       = framebuffer;
            this.renderTargetUsage = usage;
        }
Exemplo n.º 6
0
        /// <inheritdoc/>
        public override void Attach(RenderBuffer2D buffer)
        {
            Contract.Require(buffer, nameof(buffer));
            Contract.Ensure <ArgumentException>(
                buffer.Width == width &&
                buffer.Height == height, OpenGLStrings.RenderBufferIsWrongSize);
            Contract.EnsureNotDisposed(this, Disposed);

            Ultraviolet.ValidateResource(buffer);

            var sdlBuffer = (OpenGLRenderBuffer2D)buffer;

            Ultraviolet.QueueWorkItemAndWait(() =>
            {
                using (OpenGLState.ScopedBindFramebuffer(framebuffer))
                {
                    AttachRenderBuffer(sdlBuffer);

                    framebufferStatus = gl.CheckNamedFramebufferStatus(framebuffer, gl.GL_FRAMEBUFFER);
                    gl.ThrowIfError();
                }
            });

            sdlBuffer.MarkAttached();

            buffers.Add(sdlBuffer);
        }
        /// <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 sdlIndexBuffer     = (OpenGLIndexBuffer)ibuffer;
            var sdlIndexBufferName = sdlIndexBuffer.OpenGLName;

            this.ibuffer = sdlIndexBuffer;

            if (IsUsingVertexArrayObject)
            {
                using (OpenGLState.ScopedBindVertexArrayObject(vao, 0, 0, true))
                {
                    gl.BindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, sdlIndexBufferName);
                    gl.ThrowIfError();
                }
            }

            this.glElementArrayBufferBinding = sdlIndexBufferName;
            this.indexBufferElementType      = ibuffer.IndexElementType;
        }
Exemplo n.º 8
0
 /// <summary>
 /// Processes a resize operation for this texture.
 /// </summary>
 private void ProcessResize()
 {
     using (OpenGLState.ScopedBindTexture2D(texture, true))
     {
         gl.TexImage2D(gl.GL_TEXTURE_2D, 0, (int)internalformat,
                       width, height, 0, format, type, null);
     }
 }
Exemplo n.º 9
0
        /// <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);
                }
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Applies the effect pass state to the device.
        /// </summary>
        public override void Apply()
        {
            Contract.EnsureNotDisposed(this, Disposed);

            var program = programs[programIndex];

            OpenGLState.UseProgram(program);

            foreach (var uniform in program.Uniforms)
            {
                uniform.Apply();
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Initializes a new instance of the OpenGLRenderBuffer2D class.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="format">The render buffer's format.</param>
        /// <param name="width">The render buffer's width in pixels.</param>
        /// <param name="height">The render buffer's height in pixels.</param>
        /// <param name="options">The render buffer's configuration options.</param>
        public OpenGLRenderBuffer2D(UltravioletContext uv, RenderBufferFormat format, Int32 width, Int32 height, RenderBufferOptions options)
            : base(uv)
        {
            Contract.EnsureRange(width > 0, nameof(width));
            Contract.EnsureRange(height > 0, nameof(height));

            this.format           = format;
            this.width            = width;
            this.height           = height;
            this.immutable        = (options & RenderBufferOptions.ImmutableStorage) == RenderBufferOptions.ImmutableStorage;
            this.willNotBeSampled = (options & RenderBufferOptions.WillNotBeSampled) == RenderBufferOptions.WillNotBeSampled;

            if (willNotBeSampled)
            {
                using (var state = OpenGLState.ScopedCreateRenderbuffer(out renderbuffer))
                {
                    AllocateRenderbufferStorage(width, height);
                }
            }
            else
            {
                switch (format)
                {
                case RenderBufferFormat.Color:
                    this.texture = new OpenGLTexture2D(uv, gl.IsGLES2 ? gl.GL_RGBA : gl.GL_RGBA8, width, height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, IntPtr.Zero, immutable, true);
                    break;

                case RenderBufferFormat.Depth24Stencil8:
                    this.texture = new OpenGLTexture2D(uv, gl.GL_DEPTH24_STENCIL8, width, height, gl.GL_DEPTH_STENCIL, gl.GL_UNSIGNED_INT_24_8, IntPtr.Zero, immutable, true);
                    break;

                case RenderBufferFormat.Depth32:
                    this.texture = new OpenGLTexture2D(uv, gl.GL_DEPTH_COMPONENT32, width, height, gl.GL_DEPTH_COMPONENT, gl.GL_UNSIGNED_INT, IntPtr.Zero, immutable, true);
                    break;

                case RenderBufferFormat.Depth16:
                    this.texture = new OpenGLTexture2D(uv, gl.GL_DEPTH_COMPONENT16, width, height, gl.GL_DEPTH_COMPONENT, gl.GL_UNSIGNED_SHORT, IntPtr.Zero, immutable, true);
                    break;

                case RenderBufferFormat.Stencil8:
                    this.texture = new OpenGLTexture2D(uv, gl.GL_STENCIL_INDEX8, width, height, gl.GL_STENCIL, gl.GL_UNSIGNED_INT, IntPtr.Zero, immutable, true);
                    break;

                default:
                    throw new NotSupportedException(nameof(format));
                }
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Applies the geometry stream to the graphics device.
        /// </summary>
        public void Apply()
        {
            Contract.EnsureNotDisposed(this, Disposed);

            if (IsUsingVertexArrayObject)
            {
                OpenGLState.BindVertexArrayObject(vao, 0, glElementArrayBufferBinding ?? 0);
            }
            else
            {
                if (ibuffer != null)
                {
                    OpenGLState.BindElementArrayBuffer(ibuffer.OpenGLName);
                }
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Applies the state object's values to the OpenGL context.
        /// </summary>
        public void Apply()
        {
            switch (stateType)
            {
            case OpenGLStateType.None:
                break;

            case OpenGLStateType.ActiveTexture:
                Apply_ActiveTexture();
                break;

            case OpenGLStateType.BindTexture2D:
                Apply_BindTexture2D();
                break;

            case OpenGLStateType.BindVertexArrayObject:
                Apply_BindVertexArrayObject();
                break;

            case OpenGLStateType.BindArrayBuffer:
                Apply_BindArrayBuffer();
                break;

            case OpenGLStateType.BindElementArrayBuffer:
                Apply_BindElementArrayBuffer();
                break;

            case OpenGLStateType.BindFramebuffer:
                Apply_BindFramebuffer();
                break;

            case OpenGLStateType.BindRenderbuffer:
                Apply_BindRenderbuffer();
                break;

            case OpenGLStateType.UseProgram:
                Apply_UseProgram();
                break;

            default:
                throw new InvalidOperationException();
            }

            OpenGLState.VerifyCache();
        }
Exemplo n.º 14
0
        /// <summary>
        /// Sets the texture's data.
        /// </summary>
        /// <param name="level">The mipmap level for which to set data.</param>
        /// <param name="rect">A rectangle describing the position and size of the data to set.</param>
        /// <param name="data">A pointer to the data to set.</param>
        /// <param name="offset">The index of the first element to set.</param>
        /// <param name="count">The number of elements to set.</param>
        /// <param name="stride">The number of elements in one row of data.</param>
        /// <param name="format">The format of the data being set.</param>
        /// <param name="origin">A <see cref="SetDataOrigin"/> value specifying the origin of the texture data in <paramref name="data"/>.</param>
        private unsafe void SetDataInternal(Int32 level, Rectangle?rect, IntPtr data, Int32 offset, Int32 count, Int32 stride, TextureDataFormat format, SetDataOrigin origin)
        {
            var region = rect ?? new Rectangle(0, 0, width, height);

            if (region.Width * region.Height != count)
            {
                throw new InvalidOperationException(UltravioletStrings.BufferIsWrongSize);
            }

            const Int32 SizeOfTextureElementInBytes = 4;

            var flipHorizontally = (origin == SetDataOrigin.TopRight || origin == SetDataOrigin.BottomRight);
            var flipVertically   = (origin == SetDataOrigin.TopLeft || origin == SetDataOrigin.TopRight);

            TextureUtil.ReorientTextureData(data.ToPointer(), region.Width, region.Height,
                                            SizeOfTextureElementInBytes, flipHorizontally, flipVertically);

            if (flipHorizontally)
            {
                region = new Rectangle((width - region.Width) - region.X, region.Y, region.Width, region.Height);
            }
            if (flipVertically)
            {
                region = new Rectangle(region.X, (height - region.Height) - region.Y, region.Width, region.Height);
            }

            using (OpenGLState.ScopedBindTexture2D(texture))
            {
                gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, stride);
                gl.ThrowIfError();

                gl.PixelStorei(gl.GL_UNPACK_ALIGNMENT, 1);
                gl.ThrowIfError();

                gl.TextureSubImage2D(texture, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height,
                                     GetOpenGLTextureFormat(format), gl.GL_UNSIGNED_BYTE, data.ToPointer());
                gl.ThrowIfError();

                gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, 0);
                gl.ThrowIfError();
            }
        }
Exemplo n.º 15
0
        /// <summary>
        /// Sets the data contained by the vertex buffer.
        /// </summary>
        /// <param name="data">An array containing the data to set in the vertex buffer.</param>
        /// <param name="offset">The offset into <paramref name="data"/> at which to begin setting elements into the buffer.</param>
        /// <param name="count">The number of elements from <paramref name="data"/> to set into the buffer.</param>
        /// <param name="options">A hint to the underlying driver indicating whether data will be overwritten by this operation.</param>
        public override void SetData <T>(T[] data, Int32 offset, Int32 count, SetDataOptions options)
        {
            Contract.Require(data, nameof(data));
            Contract.EnsureRange(count > 0, nameof(count));
            Contract.EnsureRange(offset >= 0 && offset + count <= data.Length, nameof(offset));
            Contract.Ensure(count <= VertexCount, OpenGLStrings.DataTooLargeForBuffer);

            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);

            try
            {
                using (OpenGLState.ScopedBindArrayBuffer(buffer))
                {
                    if (options == SetDataOptions.Discard)
                    {
                        gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, this.size, null, usage);
                        gl.ThrowIfError();

                        /* FIX:
                         * I have no idea why the following code is necessary, but
                         * it seems to fix flickering sprites on Intel HD 4000 devices. */
                        if (OpenGLState.SupportsVertexArrayObjects)
                        {
                            var vao = (uint)OpenGLState.GL_VERTEX_ARRAY_BINDING;
                            gl.BindVertexArray(vao);
                            gl.ThrowIfError();
                        }
                    }

                    var start = new IntPtr(offset * vdecl.VertexStride);
                    var size  = new IntPtr(vdecl.VertexStride * count);

                    gl.NamedBufferSubData(buffer, gl.GL_ARRAY_BUFFER, start, size, handle.AddrOfPinnedObject().ToPointer());
                    gl.ThrowIfError();
                }
            }
            finally
            {
                handle.Free();
            }
        }
Exemplo n.º 16
0
        /// <summary>
        /// Initializes a new instance of the OpenGLIndexBuffer.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="itype">The index element type.</param>
        /// <param name="icount">The index element count.</param>
        /// <param name="usage">The buffer's usage type.</param>
        public OpenGLIndexBuffer(UltravioletContext uv, IndexBufferElementType itype, Int32 icount, UInt32 usage)
            : base(uv, itype, icount)
        {
            Contract.EnsureRange(icount >= 0, nameof(icount));

            this.usage = usage;
            this.size  = new IntPtr(GetElementSize() * icount);

            var buffer = 0u;

            uv.QueueWorkItemAndWait(() =>
            {
                using (OpenGLState.ScopedCreateElementArrayBuffer(out buffer))
                {
                    gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, size, null, usage);
                    gl.ThrowIfError();
                }
            });

            this.buffer = buffer;
        }
Exemplo n.º 17
0
        /// <summary>
        /// Sets the data contained by the index buffer.
        /// </summary>
        /// <param name="data">An array containing the data to set in the index buffer.</param>
        public override void SetData <T>(T[] data)
        {
            Contract.Require(data, nameof(data));
            Contract.Ensure(data.Length <= IndexCount, OpenGLStrings.DataTooLargeForBuffer);

            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);

            try
            {
                using (OpenGLState.ScopedBindElementArrayBuffer(buffer))
                {
                    var size = new IntPtr(GetElementSize() * data.Length);
                    gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, IntPtr.Zero, size, handle.AddrOfPinnedObject().ToPointer());
                    gl.ThrowIfError();
                }
            }
            finally
            {
                handle.Free();
            }
        }
Exemplo n.º 18
0
        /// <summary>
        /// Gets the render target's data.
        /// </summary>
        /// <param name="data">An array to populate with the render target's data.</param>
        /// <param name="region">The region of the render target from which to retrieve data.</param>
        private unsafe void GetDataInternal(Color[] data, Rectangle region)
        {
            using (OpenGLState.ScopedBindFramebuffer(framebuffer, true))
            {
                fixed(Color *pData = data)
                {
                    if (!gl.IsGLES2)
                    {
                        gl.ReadBuffer(gl.GL_COLOR_ATTACHMENT0);
                        gl.ThrowIfError();
                    }

                    gl.ReadPixels(region.X, region.Y, region.Width, region.Height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pData);
                    gl.ThrowIfError();
                }
            }

            // OpenGL texture data is stored upside down and in the wrong color format,
            // so we need to convert that to what our caller expects.
            var rowsToProcess = (Height % 2 == 0) ? Height / 2 : 1 + Height / 2;

            for (int y = 0; y < rowsToProcess; y++)
            {
                var ySrc = (y);
                var yDst = (Height - 1) - y;

                for (int x = 0; x < Width; x++)
                {
                    var ixSrc = (ySrc * Width) + x;
                    var ixDst = (yDst * Width) + x;

                    var colorSrc = new Color(data[ixSrc].PackedValue);
                    var colorDst = new Color(data[ixDst].PackedValue);

                    data[ixDst] = colorSrc;
                    data[ixSrc] = colorDst;
                }
            }
        }
Exemplo n.º 19
0
        /// <summary>
        /// Initializes a new instance of the OpenGLVertexBuffer.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="vdecl">The vertex declaration for this buffer.</param>
        /// <param name="vcount">The number of vertices in the buffer.</param>
        /// <param name="usage">The buffer's usage type.</param>
        public OpenGLVertexBuffer(UltravioletContext uv, VertexDeclaration vdecl, Int32 vcount, UInt32 usage)
            : base(uv, vdecl, vcount)
        {
            Contract.Require(vdecl, nameof(vdecl));

            this.vdecl = vdecl;
            this.usage = usage;
            this.size  = new IntPtr(vdecl.VertexStride * vcount);

            var buffer = 0u;

            uv.QueueWorkItemAndWait(() =>
            {
                using (OpenGLState.ScopedCreateArrayBuffer(out buffer))
                {
                    gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, size, null, usage);
                    gl.ThrowIfError();
                }
            });

            this.buffer = buffer;
        }
Exemplo n.º 20
0
        /// <summary>
        /// Sets the data contained by the vertex buffer.
        /// </summary>
        private void SetDataInternal <T>(T[] data, Int32 offsetInBytes, Int32 countInBytes, SetDataOptions options)
        {
            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);

            try
            {
                using (OpenGLState.ScopedBindArrayBuffer(buffer))
                {
                    var isPartialUpdate = (offsetInBytes > 0 || countInBytes < SizeInBytes);
                    var isDiscarding    = (options == SetDataOptions.Discard);
                    if (isDiscarding || !isPartialUpdate)
                    {
                        gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, this.size, isPartialUpdate ? null : handle.AddrOfPinnedObject().ToPointer(), usage);
                        gl.ThrowIfError();

                        /* FIX:
                         * I have no idea why the following code is necessary, but
                         * it seems to fix flickering sprites on Intel HD 4000 devices. */
                        if (OpenGLState.SupportsVertexArrayObjects)
                        {
                            var vao = (uint)OpenGLState.GL_VERTEX_ARRAY_BINDING;
                            gl.BindVertexArray(vao);
                            gl.ThrowIfError();
                        }
                    }

                    if (isPartialUpdate)
                    {
                        gl.NamedBufferSubData(buffer, gl.GL_ARRAY_BUFFER, (IntPtr)offsetInBytes, (IntPtr)countInBytes, handle.AddrOfPinnedObject().ToPointer());
                        gl.ThrowIfError();
                    }
                }
            }
            finally
            {
                handle.Free();
            }
        }
Exemplo n.º 21
0
        /// <inheritdoc/>
        public override void SetDataAligned <T>(T[] data, Int32 dataOffset, Int32 dataCount, Int32 bufferOffset, out Int32 bufferSize, SetDataOptions options)
        {
            Contract.Require(data, nameof(data));
            Contract.EnsureRange(dataCount > 0, nameof(dataCount));
            Contract.EnsureRange(dataOffset >= 0 && dataOffset + dataCount <= data.Length, nameof(dataOffset));
            Contract.EnsureRange(bufferOffset >= 0, nameof(bufferOffset));
            Contract.Ensure(dataCount <= IndexCount, OpenGLStrings.DataTooLargeForBuffer);

            var indexStride = GetElementSize();

            bufferSize = indexStride * dataCount;

            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);

            try
            {
                var caps = (OpenGLGraphicsCapabilities)Ultraviolet.GetGraphics().Capabilities;
                if (caps.SupportsMapBufferRange && caps.MinMapBufferAlignment > 0)
                {
                    bufferSize = Math.Min(Math.Max(caps.MinMapBufferAlignment, MathUtil.FindNextPowerOfTwo(bufferSize)), SizeInBytes - bufferOffset);
                }

                using (OpenGLState.ScopedBindElementArrayBuffer(buffer))
                {
                    var isPartialUpdate = (bufferOffset > 0 || bufferSize < SizeInBytes);
                    var isDiscarding    = (options == SetDataOptions.Discard);
                    if (isDiscarding || !isPartialUpdate)
                    {
                        gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, this.size, isPartialUpdate ? null : handle.AddrOfPinnedObject().ToPointer(), usage);
                        gl.ThrowIfError();
                    }

                    if (isPartialUpdate)
                    {
                        if (caps.SupportsMapBufferRange)
                        {
                            var bufferRangeAccess = gl.GL_MAP_WRITE_BIT | (options == SetDataOptions.NoOverwrite ? gl.GL_MAP_UNSYNCHRONIZED_BIT : 0);
                            var bufferRangePtr    = (Byte *)gl.MapNamedBufferRange(buffer, gl.GL_ELEMENT_ARRAY_BUFFER,
                                                                                   (IntPtr)bufferOffset, (IntPtr)bufferSize, bufferRangeAccess);
                            gl.ThrowIfError();

                            var sourceRangePtr    = (Byte *)handle.AddrOfPinnedObject() + (dataOffset * indexStride);
                            var sourceSizeInBytes = dataCount * indexStride;

                            for (int i = 0; i < sourceSizeInBytes; i++)
                            {
                                *bufferRangePtr++ = *sourceRangePtr++;
                            }

                            gl.UnmapNamedBuffer(buffer, gl.GL_ELEMENT_ARRAY_BUFFER);
                            gl.ThrowIfError();
                        }
                        else
                        {
                            gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER,
                                                  (IntPtr)bufferOffset, (IntPtr)bufferSize, (Byte *)handle.AddrOfPinnedObject().ToPointer() + (dataOffset * indexStride));
                            gl.ThrowIfError();
                        }
                    }
                }
            }
            finally
            {
                handle.Free();
            }
        }
Exemplo n.º 22
0
        /// <summary>
        /// Resets the OpenGL context to its values prior to this object's application.
        /// </summary>
        public void Dispose()
        {
            if (disposed)
            {
                return;
            }

            switch (stateType)
            {
            case OpenGLStateType.None:
                break;

            case OpenGLStateType.ActiveTexture:
                Dispose_ActiveTexture();
                break;

            case OpenGLStateType.BindTexture2D:
                Dispose_BindTexture2D();
                break;

            case OpenGLStateType.BindVertexArrayObject:
                Dispose_BindVertexArrayObject();
                break;

            case OpenGLStateType.BindArrayBuffer:
                Dispose_BindArrayBuffer();
                break;

            case OpenGLStateType.BindElementArrayBuffer:
                Dispose_BindElementArrayBuffer();
                break;

            case OpenGLStateType.BindFramebuffer:
                Dispose_BindFramebuffer();
                break;

            case OpenGLStateType.BindRenderbuffer:
                Dispose_BindRenderbuffer();
                break;

            case OpenGLStateType.UseProgram:
                Dispose_UseProgram();
                break;

            case OpenGLStateType.CreateTexture2D:
                Dispose_CreateTexture2D();
                break;

            case OpenGLStateType.CreateArrayBuffer:
                Dispose_CreateArrayBuffer();
                break;

            case OpenGLStateType.CreateElementArrayBuffer:
                Dispose_CreateElementArrayBuffer();
                break;

            case OpenGLStateType.CreateFramebuffer:
                Dispose_CreateFramebuffer();
                break;

            case OpenGLStateType.CreateRenderbuffer:
                Dispose_CreateRenderbuffer();
                break;

            default:
                throw new InvalidOperationException();
            }

            OpenGLState.VerifyCache();

            disposed = true;
            pool.Release(this);
        }
Exemplo n.º 23
0
        /// <summary>
        /// Initializes a new instance of the <see cref="OpenGLGraphicsCapabilities"/> class.
        /// </summary>
        /// <param name="configuration">The configuration settings for the Ultraviolet context.</param>
        internal unsafe OpenGLGraphicsCapabilities(OpenGLUltravioletConfiguration configuration)
        {
            this.maximumTextureSize = gl.GetInteger(gl.GL_MAX_TEXTURE_SIZE);
            gl.ThrowIfError();

            var viewportDims = stackalloc int[2];

            gl.GetIntegerv(gl.GL_MAX_VIEWPORT_DIMS, viewportDims);
            gl.ThrowIfError();

            this.maximumViewportWidth  = viewportDims[0];
            this.maximumViewportHeight = viewportDims[1];

            this.supportsDepthStencilTextures = !gl.IsGLES2 || gl.IsExtensionSupported("GL_OES_packed_depth_stencil");

            if (gl.IsGLES2 && !this.supportsDepthStencilTextures)
            {
                // HACK: Guess what? The Visual Studio Emulator for Android flat-out lies about this.
                // So it seems the only reliable way to determine support for depth/stencil is to
                // actually try to create one and see if it fails. I hate Android emulators.
                var rb = gl.GenRenderbuffer();
                using (var state = OpenGLState.ScopedBindRenderbuffer(rb, true))
                {
                    gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH24_STENCIL8, 32, 32);
                    this.supportsDepthStencilTextures = (gl.GetError() == gl.GL_NO_ERROR);
                }
                gl.DeleteRenderBuffers(rb);
            }

            this.SupportsNonZeroBaseInstance = SupportsInstancedRendering && !gl.IsGLES &&
                                               (gl.IsVersionAtLeast(4, 2) || gl.IsExtensionSupported("GL_ARB_base_instance"));

            this.SupportsIndependentSamplerState = (gl.IsGLES ? gl.IsVersionAtLeast(3, 0) : gl.IsVersionAtLeast(3, 3)) ||
                                                   gl.IsExtensionSupported("GL_ARB_sampler_objects");

            this.SupportsIntegerVertexAttributes         = !gl.IsGLES2 || gl.IsExtensionSupported("GL_EXT_gpu_shader4");
            this.SupportsDoublePrecisionVertexAttributes = !gl.IsGLES && gl.IsVersionAtLeast(4, 1);

            this.SupportsMapBufferRange = true;
            if (gl.IsGLES2)
            {
                this.SupportsMapBufferRange =
                    gl.IsExtensionSupported("GL_ARB_map_buffer_range") ||
                    gl.IsExtensionSupported("GL_EXT_map_buffer_range");
            }

            this.MinMapBufferAlignment = gl.IsExtensionSupported("GL_ARB_map_buffer_alignment") ?
                                         gl.GetInteger(gl.GL_MIN_MAP_BUFFER_ALIGNMENT) : 0;

            // There seems to be a bug in the version of Mesa which is distributed
            // with Ubuntu 16.04 that causes long stalls when using glMapBufferRange.
            // Testing indicates that this is fixed in 11.2.2.
            var version          = gl.GetString(gl.GL_VERSION);
            var versionMatchMesa = Regex.Match(version, "Mesa (?<major>\\d+).(?<minor>\\d+).(?<build>\\d+)");

            if (versionMatchMesa != null && versionMatchMesa.Success)
            {
                var mesaMajor   = Int32.Parse(versionMatchMesa.Groups["major"].Value);
                var mesaMinor   = Int32.Parse(versionMatchMesa.Groups["minor"].Value);
                var mesaBuild   = Int32.Parse(versionMatchMesa.Groups["build"].Value);
                var mesaVersion = new Version(mesaMajor, mesaMinor, mesaBuild);
                if (mesaVersion < new Version(11, 2, 2))
                {
                    configuration.UseBufferMapping = false;
                }
            }

            // If we've been explicitly told to disable buffer mapping, override the caps from the driver.
            if (!configuration.UseBufferMapping)
            {
                this.SupportsMapBufferRange = false;
            }
        }
Exemplo n.º 24
0
        /// <summary>
        /// Applies the sampler state to the device.
        /// </summary>
        /// <param name="sampler">The sampler index on which to set the state.</param>
        internal void Apply(Int32 sampler)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            if (Ultraviolet.GetGraphics().Capabilities.SupportsIndependentSamplerState)
            {
                throw new InvalidOperationException(UltravioletStrings.GenericError);
            }

            OpenGLState.ActiveTexture((uint)(gl.GL_TEXTURE0 + sampler));

            gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, GetTextureAddressModeGL(AddressU));
            gl.ThrowIfError();

            gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, GetTextureAddressModeGL(AddressV));
            gl.ThrowIfError();

            if (MipMapLevelOfDetailBias != 0)
            {
                gl.ThrowIfGLES(OpenGLStrings.UnsupportedLODBiasGLES);

                gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_LOD_BIAS, MipMapLevelOfDetailBias);
                gl.ThrowIfError();
            }

            switch (Filter)
            {
            case TextureFilter.Point:
                gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, 1f);
                gl.ThrowIfError();

                gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_NEAREST);
                gl.ThrowIfError();

                gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_NEAREST);
                gl.ThrowIfError();
                break;

            case TextureFilter.Linear:
                if (gl.IsAnisotropicFilteringAvailable)
                {
                    gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, 1f);
                    gl.ThrowIfError();
                }

                gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR);
                gl.ThrowIfError();

                gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR);
                gl.ThrowIfError();
                break;

            case TextureFilter.Anisotropic:
                if (gl.IsAnisotropicFilteringAvailable)
                {
                    gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, Math.Min(1f, MaxAnisotropy));
                    gl.ThrowIfError();
                }

                gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR);
                gl.ThrowIfError();

                gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR);
                gl.ThrowIfError();
                break;

            default:
                throw new NotSupportedException();
            }
        }
Exemplo n.º 25
0
        /// <summary>
        /// Sets the texture's data.
        /// </summary>
        /// <param name="level">The mipmap level for which to set data.</param>
        /// <param name="rect">A rectangle describing the position and size of the data to set.</param>
        /// <param name="data">A pointer to the data to set.</param>
        /// <param name="offset">The index of the first element to set.</param>
        /// <param name="count">The number of elements to set.</param>
        /// <param name="stride">The number of elements in one row of data.</param>
        /// <param name="format">The format of the data being set.</param>
        /// <param name="origin">A <see cref="SetDataOrigin"/> value specifying the origin of the texture data in <paramref name="data"/>.</param>
        private unsafe void SetDataInternal(Int32 level, Rectangle?rect, IntPtr data, Int32 offset, Int32 count, Int32 stride, TextureDataFormat format, SetDataOrigin origin)
        {
            var region = rect ?? new Rectangle(0, 0, width, height);

            if (region.Width * region.Height != count)
            {
                throw new InvalidOperationException(UltravioletStrings.BufferIsWrongSize);
            }

            const Int32 SizeOfTextureElementInBytes = 4;

            var flipHorizontally = (origin == SetDataOrigin.TopRight || origin == SetDataOrigin.BottomRight);
            var flipVertically   = (origin == SetDataOrigin.TopLeft || origin == SetDataOrigin.TopRight);

            TextureUtil.ReorientTextureData(data.ToPointer(), region.Width, region.Height,
                                            SizeOfTextureElementInBytes, flipHorizontally, flipVertically);

            if (flipHorizontally)
            {
                region = new Rectangle((width - region.Width) - region.X, region.Y, region.Width, region.Height);
            }
            if (flipVertically)
            {
                region = new Rectangle(region.X, (height - region.Height) - region.Y, region.Width, region.Height);
            }

            using (OpenGLState.ScopedBindTexture2D(texture))
            {
                var rowLengthSupported        = (!gl.IsGLES2 || gl.IsExtensionSupported("GL_EXT_unpack_subimage"));
                var rowLengthFallbackRequired = (stride != 0 && !rowLengthSupported);
                if (rowLengthFallbackRequired)
                {
                    var ptr = (Byte *)data.ToPointer();
                    for (int i = 0; i < height; i++)
                    {
                        gl.TextureSubImage2D(texture, gl.GL_TEXTURE_2D, level, region.X, region.Y + i, region.Width, 1,
                                             GetOpenGLTextureFormat(format), gl.GL_UNSIGNED_BYTE, ptr);

                        ptr += stride * 4;
                    }
                }
                else
                {
                    if (rowLengthSupported)
                    {
                        gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, stride);
                        gl.ThrowIfError();
                    }

                    gl.PixelStorei(gl.GL_UNPACK_ALIGNMENT, 1);
                    gl.ThrowIfError();

                    gl.TextureSubImage2D(texture, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height,
                                         GetOpenGLTextureFormat(format), gl.GL_UNSIGNED_BYTE, data.ToPointer());
                    gl.ThrowIfError();

                    if (rowLengthSupported)
                    {
                        gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, 0);
                        gl.ThrowIfError();
                    }
                }
            }
        }
Exemplo n.º 26
0
        /// <summary>
        /// Creates the underlying native OpenGL texture with the specified format and data.
        /// </summary>
        /// <param name="uv">The Ultraviolet context.</param>
        /// <param name="internalformat">The texture's internal format.</param>
        /// <param name="width">The texture's width in pixels.</param>
        /// <param name="height">The texture's height in pixels.</param>
        /// <param name="format">The texel format.</param>
        /// <param name="type">The texel data type.</param>
        /// <param name="pixels">A pointer to the beginning of the texture's pixel data.</param>
        /// <param name="immutable">A value indicating whether to use immutable texture storage.</param>
        private void CreateNativeTexture(UltravioletContext uv, UInt32 internalformat, Int32 width, Int32 height, UInt32 format, UInt32 type, void *pixels, Boolean immutable)
        {
            if (uv.IsRunningInServiceMode)
            {
                throw new NotSupportedException(UltravioletStrings.NotSupportedInServiceMode);
            }

            this.width          = width;
            this.height         = height;
            this.internalformat = internalformat;
            this.format         = format;
            this.type           = type;
            this.immutable      = immutable;

            this.texture = uv.QueueWorkItemAndWait(() =>
            {
                uint glname;

                using (OpenGLState.ScopedCreateTexture2D(out glname))
                {
                    if (!gl.IsGLES2)
                    {
                        gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_LEVEL, 0);
                        gl.ThrowIfError();
                    }

                    gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR);
                    gl.ThrowIfError();

                    gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR);
                    gl.ThrowIfError();

                    gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, (int)gl.GL_CLAMP_TO_EDGE);
                    gl.ThrowIfError();

                    gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, (int)gl.GL_CLAMP_TO_EDGE);
                    gl.ThrowIfError();

                    if (immutable)
                    {
                        if (gl.IsTextureStorageAvailable)
                        {
                            gl.TextureStorage2D(glname, gl.GL_TEXTURE_2D, 1, internalformat, width, height);
                            gl.ThrowIfError();

                            if (pixels != null)
                            {
                                gl.TextureSubImage2D(glname, gl.GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, pixels);
                                gl.ThrowIfError();
                            }
                        }
                        else
                        {
                            gl.TextureImage2D(glname, gl.GL_TEXTURE_2D, 0, (int)internalformat, width, height, 0, format, type, pixels);
                            gl.ThrowIfError();
                        }
                    }
                }

                if (!immutable)
                {
                    using (OpenGLState.ScopedBindTexture2D(glname, true))
                    {
                        gl.TexImage2D(gl.GL_TEXTURE_2D, 0, (int)internalformat, width, height, 0, format, type, pixels);
                        gl.ThrowIfError();
                    }
                }

                return(glname);
            });
        }
Exemplo n.º 27
0
        /// <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)
        {
            var caps = Ultraviolet.GetGraphics().Capabilities;

            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 (!caps.SupportsDoublePrecisionVertexAttributes)
                                    {
                                        throw new NotSupportedException();
                                    }

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

                                case OpenGLAttribCategory.Integer:
                                {
                                    if (!caps.SupportsIntegerVertexAttributes)
                                    {
                                        throw new NotSupportedException();
                                    }

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

                        position += (uint)stride;
                    }
                }
            }
        }
Exemplo n.º 28
0
        /// <summary>
        /// Binds the specified buffer's vertex attributes to the currently cached program using the old API.
        /// </summary>
        private unsafe void BindVertexAttributesForBuffer_OldAPI(OpenGLVertexBuffer vbuffer, UInt32 binding, UInt32 frequency, UInt32?program, UInt32?offset)
        {
            OpenGLState.BindArrayBuffer(vbuffer.OpenGLName);

            var position = offset ?? this.offset;
            var caps     = Ultraviolet.GetGraphics().Capabilities;

            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)
                {
                    if (program.HasValue)
                    {
                        if (gl.IsGLES2)
                        {
                            if (frequency != 0)
                            {
                                throw new NotSupportedException();
                            }
                        }
                        else
                        {
                            gl.VertexAttribDivisor(location, frequency);
                            gl.ThrowIfError();
                        }

                        gl.EnableVertexAttribArray(location);
                        gl.ThrowIfError();
                    }

                    switch (category)
                    {
                    case OpenGLAttribCategory.Single:
                    {
                        gl.VertexAttribPointer(location, size, type, normalize, vbuffer.VertexDeclaration.VertexStride, (void *)(position));
                        gl.ThrowIfError();
                    }
                    break;

                    case OpenGLAttribCategory.Double:
                    {
                        if (!caps.SupportsDoublePrecisionVertexAttributes)
                        {
                            throw new NotSupportedException();
                        }

                        gl.VertexAttribLPointer(location, size, type, vbuffer.VertexDeclaration.VertexStride, (void *)(position));
                        gl.ThrowIfError();
                    }
                    break;

                    case OpenGLAttribCategory.Integer:
                    {
                        if (!caps.SupportsIntegerVertexAttributes)
                        {
                            throw new NotSupportedException();
                        }

                        gl.VertexAttribIPointer(location, size, type, vbuffer.VertexDeclaration.VertexStride, (void *)(position));
                        gl.ThrowIfError();
                    }
                    break;
                    }
                }

                position += (uint)stride;
            }
        }