WaltN's picture

Is glInterleavedArrays usable with VAOs?

Having previously used interleaved arrays (with the "old OpenGL"), I blindly assumed that glInterleavedArrays was useable with vertex-array objects. But my experience suggests that my assumption was all wet.

The subject code (much of it for context) appears below where a VAO and two VBOs (index and vertex) are created for an object. The vertex array is interleaved, compatible with T2fN3fV3f. (The code below does not include creation of the texture image.) Note that the index and vertex arrays are arguments to the StandardObject constructor. The helper method GenVBOID() is illustrated separately below.

        protected int VAOID { get; set; } // Vertex Array Object identifier
        protected List<int> VBOIDs = new List<int>();
        protected TexImage TexImage { get; set; }
 
        public int ElementCount { get; protected set; } // Number of elements in index array
 
        /// <summary>
        /// General form interleaved array constructor.
        /// </summary>
        /// <param name="x">Element (index) array (optional)</param>
        /// <param name="v">Vertex (interleaved) array</param>
        /// <param name="iPath">Path to texture image file</param>
        public StandardObject(int[] x, Vertex[] v, string iPath = null)
        {
            ErrorCode ec = GL.GetError(); // Clears errors
 
            int[] vaoid = new int[1]; // Temporary VAO identifier array
            GL.GenVertexArrays(1, vaoid);
            VAOID = vaoid[0]; // Store as scalar member
 
            ElementCount = x.Length;
            int vertexSize = Marshal.SizeOf(v[0]); // 32
 
            int vSize = v.Length * vertexSize;
            int xSize = x.Length * sizeof(int);
 
            // Local VBO IDs
            int vID;        // vertices
            int xID = 0;    // elements
 
            // Define VAO
            GL.BindVertexArray(VAOID);
 
            // VBO for elements (x)
            xID = GenVBOID(); // add a single buffer
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, xID);
            GL.BufferData(BufferTarget.ElementArrayBuffer,
                (IntPtr)xSize, x, BufferUsageHint.StaticDraw);
 
            // VBO for vertices (v)
            vID = GenVBOID(); // Add a single buffer
            GL.BindBuffer(BufferTarget.ArrayBuffer, vID);
            GL.BufferData<Vertex>(BufferTarget.ArrayBuffer, (IntPtr)vSize, v, BufferUsageHint.StaticDraw);
 
            GL.EnableVertexAttribArray((int)AttributeTypes.Vertex);
            GL.EnableVertexAttribArray((int)AttributeTypes.Normal);
            GL.EnableVertexAttribArray((int)AttributeTypes.TexCoord);
            //GL.InterleavedArrays(InterleavedArrayFormat.T2fN3fV3f, 0, (IntPtr)null); // Invalid operation
            GL.VertexAttribPointer((int)AttributeTypes.TexCoord, 2, VertexAttribPointerType.Float, false, vertexSize, 0);
            GL.VertexAttribPointer((int)AttributeTypes.Normal, 3, VertexAttribPointerType.Float, false, vertexSize, 8);
            GL.VertexAttribPointer((int)AttributeTypes.Vertex, 3, VertexAttribPointerType.Float, false, vertexSize, 20);
            //                    (     attributeIndex,       sz,             type,              nmlzd, stride, offset)
        /// <summary>
        /// Generates a buffer and records its ID in VBOIDs array.
        /// </summary>
        /// <returns>Buffer ID</returns>
        private int GenVBOID()
        {
            int id; // VBO ID
            GL.GenBuffers(1, out id); // Generate a single buffer
            VBOIDs.Add(id); // Record it for this instance
            return id; // Return it
        } // end GenVBOID

The crucial statements are the last group of statements in the first block of code, comprising three GL.EnableVertexAttribArray statements, a commented out GL.InterleavedArrays statement, and three GL.VertexAttribPointer statements. The code as illustrated (with the commented out glInterleavedArrays) works and works properly. But, I had to add the three GL.EnableVertexAttribArray statements and the three GL.VertexAttribPointer statements to get it to work. (I initially assumed that GL.InterleavedArrays would do the work for me.) As a matter of fact, the code continues to work if the GL.InterleavedArrays statement is not commented out. However, until I looked explicitly, I didn't realize that it was failing with an InvalidOperation error code.

I've tentatively concluded that glInterleavedArrays was not meant to work with VAOs. This after realizing that glInterleavedArrays is a compatibility extension as of GL 3.1 and reading that the InvalidOperation could be signaling the removal of glInterleavedArrays. (Or, perhaps signaling that glInterleavedArrays is incompatible with VAOs?)

I'm glad that I was able to get this to work because I'm a believer in interleaved array formats, but I'm surprised that I didn't read statements regarding this compatibility issue in either the Redbook or SuperBible 5.

My question is: Is my tentative conclusion an appropriate conclusion?


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
tksuoran's picture

I did not read your code fully, however, I can say the following:

  1. You should almost always use interleaved arrays
  2. You should not be using InterleavedArrays
  3. You achieve interleaved arrays using VertexAttribPointer
WaltN's picture

I struggled a bit to feel I was interpreting your enumeration correctly because (1) and (2) appear to be contradictory. However, your points do agree literally with my experience.

I would summarize by saying that, if one is using VAOs (which means glVertexAttribPointer is called for rather than glVertexPointer, etc.), they almost always should be using interleaved arrays, but they should not be trying to use glInterleavedArrays to construct and enable them. If the OpenGL literature had pointed something like that out, it would have saved me a lot of struggle and cussing.

Thanks for understanding and addressing my question.