/// <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 oglBuffer = (OpenGLRenderBuffer2D)buffer;

            Ultraviolet.QueueWorkItem(state =>
            {
                using (OpenGLState.ScopedBindFramebuffer(framebuffer))
                {
                    AttachRenderBuffer(oglBuffer);

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

            oglBuffer.MarkAttached();

            buffers.Add(oglBuffer);
        }
        /// <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.IsReadBufferAvailable)
                    {
                        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;
                }
            }
        }
        /// <inheritdoc/>
        public override void Invalidate(Boolean color, Boolean depth, Boolean stencil, Boolean depthStencil, Int32 colorOffset, Int32 colorCount)
        {
            Contract.EnsureNotDisposed(this, Disposed);

            // If we don't have any support for framebuffer invalidation, simply do nothing.
            if (!gl.IsFramebufferInvalidationAvailable)
            {
                return;
            }

            // If we're relying on EXT_discard_framebuffer, then we can only discard the first color
            // attachment. If that leaves us with nothing to do, bail out.
            if (!gl.IsInvalidateSubdataAvailable)
            {
                if (colorOffset != 0)
                {
                    color = false;
                    if (!depth && !stencil && !depthStencil)
                    {
                        return;
                    }
                }

                if (colorCount > 1)
                {
                    colorCount = 1;
                }
            }

            // Bind the framebuffer (if we're not using DSA) and perform the invalidation.
            unsafe
            {
                var numAttachments =
                    (color ? colorCount : 0) +
                    (depth ? 1 : 0) +
                    (stencil ? 1 : 0) +
                    (depthStencil ? 1 : 0);

                var attachments   = stackalloc UInt32[numAttachments];
                var attachmentsIx = 0;

                if (color)
                {
                    for (int i = 0; i < colorCount; i++)
                    {
                        attachments[attachmentsIx++] = (UInt32)(gl.GL_COLOR_ATTACHMENT0 + colorOffset + i);
                    }
                }

                if (depth)
                {
                    attachments[attachmentsIx++] = gl.GL_DEPTH_ATTACHMENT;
                }

                if (stencil)
                {
                    attachments[attachmentsIx++] = gl.GL_STENCIL_ATTACHMENT;
                }

                if (depthStencil)
                {
                    attachments[attachmentsIx++] = gl.GL_DEPTH_STENCIL_ATTACHMENT;
                }

                if (gl.IsInvalidateSubdataAvailable)
                {
                    // The EXT DSA extension doesn't seem to provide glInvalidateNamedFramebuffer, so...
                    using (OpenGLState.ScopedBindFramebuffer(framebuffer, gl.IsEXTDirectStateAccessAvailable))
                    {
                        if (gl.IsEXTDirectStateAccessAvailable)
                        {
                            gl.InvalidateFramebuffer(gl.GL_FRAMEBUFFER, numAttachments, (IntPtr)attachments);
                            gl.ThrowIfError();
                        }
                        else
                        {
                            gl.InvalidateNamedFramebufferData(gl.GL_FRAMEBUFFER, framebuffer, numAttachments, (IntPtr)attachments);
                            gl.ThrowIfError();
                        }
                    }
                }
                else
                {
                    using (OpenGLState.ScopedBindFramebuffer(framebuffer, true))
                    {
                        gl.DiscardFramebufferEXT(gl.GL_FRAMEBUFFER, numAttachments, (IntPtr)attachments);
                        gl.ThrowIfError();
                    }
                }
            }
        }