/// <summary>
        /// Function to retrieve the size, in bytes, of the input object.
        /// </summary>
        private void UpdateVertexSize()
        {
            int size = 0;

            _slotSizes = new Dictionary <int, int>();

            // ReSharper disable once ForCanBeConvertedToForeach
            for (int i = 0; i < _elements.Length; ++i)
            {
                GorgonInputElement element = _elements[i];
                size += element.SizeInBytes;


                // Calculate the individual slot sizes.
                if (_slotSizes.TryGetValue(element.Slot, out int slotSize))
                {
                    slotSize += element.SizeInBytes;
                }
                else
                {
                    slotSize = element.SizeInBytes;
                }

                _slotSizes[element.Slot] = slotSize;
            }

            SizeInBytes = size;
        }
        /// <summary>
        /// Function to build an input layout using the fields from a value type.
        /// </summary>
        /// <param name="graphics">The graphics interface used to create the input layout.</param>
        /// <param name="type">The type to evaluate.</param>
        /// <param name="shader">Vertex shader to bind the layout with.</param>
        /// <returns>A new <see cref="GorgonInputLayout"/> for the type passed to <paramref name="type"/>.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="graphics"/>, <paramref name="type"/> or the <paramref name="shader"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentException">Thrown when an element with the same context, slot and index appears more than once in the members of the <paramref name="type"/>.
        /// <para>-or-</para>
        /// <para>Thrown if the <paramref name="type"/> does not have a <see cref="LayoutKind"/> of <see cref="LayoutKind.Sequential"/> or <see cref="LayoutKind.Explicit"/>.</para>
        /// </exception>
        /// <exception cref="GorgonException">Thrown when the type specified by <paramref name="type"/> is not safe for use with native functions (see <see cref="GorgonReflectionExtensions.IsFieldSafeForNative"/>).
        /// <para>-or-</para>
        /// <para>Thrown when the type specified by <paramref name="type"/> does not contain any public members.</para>
        /// </exception>
        /// <remarks>
        /// <para>
        /// This will build a new <see cref="GorgonInputLayout"/> using the fields within a value type (<c>struct</c>). Each of the members that are to be included in the layout must be decorated with a
        /// <see cref="InputElementAttribute"/>. If a member is not decorated with this attribute, then it will be ignored.
        /// </para>
        /// <para>
        /// The <paramref name="type"/> parameter must be an unmanaged value type (<c>struct</c>), reference types are not supported. The members of the type must also be public fields. Properties are not
        /// supported. Futhermore, the struct must be decorated with a <see cref="StructLayoutAttribute"/> that defines a <see cref="LayoutKind"/> of <see cref="LayoutKind.Sequential"/> or
        /// <see cref="LayoutKind.Explicit"/>. This is necessary to ensure that the member of the value type are in the correct order when writing to a <see cref="GorgonVertexBuffer"/> or when
        /// generating a <see cref="GorgonInputLayout"/> from a type.
        /// </para>
        /// <para>
        /// If the type specified by <paramref name="type"/> has members that are not primitive types or value types with a <see cref="StructLayoutAttribute"/>, or the member has a
        /// <see cref="MarshalAsAttribute"/>, then an exception is thrown.  Gorgon does not support marshalling of complex types for vertices.
        /// </para>
        /// <para>
        /// The types of the fields must be one of the following types:
        /// <para>
        /// <list type="bullet">
        ///		<item>
        ///			<description><see cref="byte"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="sbyte"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="short"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="ushort"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="int"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="uint"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="long"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="ulong"/></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="float"/></description>
        ///		</item>
        ///		<item>
        ///			<description><c>Vector2</c></description>
        ///		</item>
        ///		<item>
        ///			<description><c>Vector3</c></description>
        ///		</item>
        ///		<item>
        ///			<description><c>Vector4</c></description>
        ///		</item>
        ///		<item>
        ///			<description><see cref="GorgonColor"/></description>
        ///		</item>
        /// </list>
        /// </para>
        /// If the type of the member does not match, an exception will be thrown.
        /// </para>
        /// </remarks>
        /// <seealso cref="GorgonReflectionExtensions.IsFieldSafeForNative"/>
        public static GorgonInputLayout CreateUsingType(GorgonGraphics graphics, Type type, GorgonVertexShader shader)
        {
            if (graphics == null)
            {
                throw new ArgumentNullException(nameof(graphics));
            }

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

            if ((type.IsAutoLayout) || ((!type.IsLayoutSequential) && (!type.IsExplicitLayout)))
            {
                throw new ArgumentException(string.Format(Resources.GORGFX_ERR_LAYOUT_NOT_SEQUENTIAL_EXPLICIT, type.FullName));
            }

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

            int byteOffset = 0;
            List <(FieldInfo Field, InputElementAttribute InputElement)> members = GetFieldInfoList(type);

            if (members.Count == 0)
            {
                throw new GorgonException(GorgonResult.CannotCreate, string.Format(Resources.GORGFX_ERR_VERTEX_NO_FIELDS, type.FullName));
            }

            var elements = new GorgonInputElement[members.Count];

            for (int i = 0; i < elements.Length; i++)
            {
                (FieldInfo Field, InputElementAttribute InputElement) = members[i];

                BufferFormat format      = InputElement.Format;
                string       contextName = InputElement.Context;

                // Try to determine the format from the type.
                if ((format == BufferFormat.Unknown) && (!_typeMapping.TryGetValue(Field.FieldType, out format)))
                {
                    throw new GorgonException(GorgonResult.CannotCreate, string.Format(Resources.GORGFX_ERR_LAYOUT_INVALID_ELEMENT_TYPE, Field.FieldType.FullName));
                }

                var element = new GorgonInputElement(contextName, format,
                                                     (InputElement.AutoOffset ? byteOffset : InputElement.Offset),
                                                     InputElement.Index, InputElement.Slot, InputElement.Instanced,
                                                     InputElement.Instanced ? InputElement.InstanceCount : 0);

                FindDuplicateElements(elements, element, i, nameof(element));

                elements[i] = element;
                byteOffset += element.SizeInBytes;
            }

            return(new GorgonInputLayout(graphics, type.Name, shader, elements));
        }
        /// <summary>
        /// Function to determine if an element already exists with the same context, index and slot.
        /// </summary>
        /// <param name="elements">The list of elements to compare against.</param>
        /// <param name="element">The element to search for.</param>
        /// <param name="index">The index of the current element.</param>
        /// <param name="parameterName">The name of the parameter being validated.</param>
        private static void FindDuplicateElements(IList <GorgonInputElement> elements, GorgonInputElement element, int index, string parameterName)
        {
            for (int i = 0; i < elements.Count; ++i)
            {
                // Skip the element that we're testing against.
                if (index == i)
                {
                    continue;
                }

                GorgonInputElement currentElement = elements[i];

                if ((string.Equals(currentElement.Context, element.Context, StringComparison.OrdinalIgnoreCase)) &&
                    (currentElement.Index == element.Index) && (currentElement.Slot == element.Slot))
                {
                    throw new ArgumentException(
                              string.Format(Resources.GORGFX_ERR_LAYOUT_ELEMENT_IN_USE, element.Offset, element.Context), parameterName);
                }
            }
        }
        /// <summary>
        /// Function to convert this input layout into a <see cref="GorgonStreamOutLayout"/>
        /// </summary>
        /// <param name="stream">[Optional] The output stream to use.</param>
        /// <param name="slot">[Optional] The associated stream output buffer that is bound to the pipeline.</param>
        /// <returns>A new <see cref="GorgonStreamOutLayout"/> derived from this input layout.</returns>
        /// <remarks>
        /// <para>
        /// When streaming data out from the GPU, the layout of that data must be defined as a <see cref="GorgonStreamOutLayout"/>, which is quite similar to an input layout. For convenience, this method
        /// will create a new <see cref="GorgonStreamOutLayout"/> that uses the same semantics as the input layout.
        /// </para>
        /// </remarks>
        /// <seealso cref="GorgonStreamOutLayout"/>
        public GorgonStreamOutLayout ToStreamOutLayout(int stream = 0, byte slot = 0)
        {
            var elements = new GorgonStreamOutElement[_elements.Length];

            for (int i = 0; i < _elements.Length; ++i)
            {
                // Only allow 64 elements.
                if (i > 64)
                {
                    break;
                }

                GorgonInputElement inputElement = _elements[i];

                var info = new GorgonFormatInfo(inputElement.Format);

                elements[i] = new GorgonStreamOutElement(inputElement.Context, 0, (byte)info.ComponentCount, slot, inputElement.Index, stream);
            }

            return(new GorgonStreamOutLayout(Name + " (SO)", elements));
        }
        /// <summary>
        /// Function to normalize the offsets in the element list.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is used to rebuild the offsets for the elements assigned to this layout. Ensure that this input layout is not bound to the pipeline before normalizing. Otherwise, an invalid binding will
        /// occur.
        /// </para>
        /// <para>
        /// This will use the order of the elements as they appear in the list passed to this object to determine the offsets.
        /// </para>
        /// </remarks>
        public void NormalizeOffsets()
        {
            int lastOffset = 0;

            var elements = new D3D11.InputElement[_elements.Length];

            for (int i = 0; i < _elements.Length - 1; i++)
            {
                GorgonInputElement oldElement = _elements[i];
                _elements[i] = new GorgonInputElement(oldElement.Context,
                                                      oldElement.Format,
                                                      lastOffset,
                                                      oldElement.Index,
                                                      oldElement.Slot,
                                                      oldElement.Instanced,
                                                      oldElement.InstanceCount);
                lastOffset += oldElement.SizeInBytes;
                elements[i] = _elements[i].D3DInputElement;
            }

            BuildD3DLayout();
        }