Vertex List Descriptor

1.0.0-beta

Summary

A cross-platform way to describe vertex memory layouts so that APIs can interoperate without requiring knowledge of the underlying data structures.

Terms

  1. Descriptor: A set of values that describe an associated dataset.
  2. Coordinate: A numeric value member of a set of values that define a specific location.
  3. Vertex: A structure that holds a contiguous block of Coordinate values defining a point. It may contain other fields.
  4. Dimensionality: Number of Coordinates required to define the location of a Vertex.
  5. Pointer: A platform native integer value representing a memory address
  6. Offset: A numeric value added or subtracted from a Pointer to retrieve data at a relative memory location.
  7. Array: A contiguous block of memory with elements directly after each other
  8. Linked List: A chain of Nodes
  9. Node: A structure with a Pointer to another Node
  10. Stride: The distance in bytes from element to element in an Array, in case of a Linked List the distance from a Node start to the Pointer it holds for the next Node

The Vertex List Descriptor

Memory Layout

0   uint8   version
1   uint8   data_type
2   uint8   list_type
3   uint8   indirection
4   uint64  count
12  void*   data
20  uint16  stride
22  uint16  structure_offset
24  uint16  pointer_offset
26  uint8   dimensionality
27  uint8   coordinate_system

Consider that the size of void* at offset 12 may be 32-bit on some platforms, but we need to ensure that stride is at byte offset 20. One way to ensure this is to add padding for 32-bit (see _padding_ below).

For brevity we will call a structure that holds coordinates a Vertex, but it may be more complex than just the simple definition of a spatial point position.

The following values are provided by the descriptor. The byte offsets are in brackets []:

Sample C++ implementation

VertexArrayDescriptor.h cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <cstdint>

struct VertexArrayDescriptor {
    std::uint8_t version = 1;
    std::uint8_t data_type = 0;
    std::uint8_t list_type = 0;
    std::uint8_t indirection = 0;
    std::uint64_t count = 0;
    void*         data = nullptr;

#if UINTPTR_MAX == 0xFFFFFFFFULL
    std::uint32_t _padding_ = 0;
#endif
    std::uint16_t stride = 0;
    std::uint16_t structure_offset = 0;
    std::uint16_t pointer_offset = 0;


};

Implementations should add static asserts to ensure alignment and may add strong types via getters and setters.

Constraints

Common Vertex List types

A non-exhaustive list of common representations of coordinate data.

In illustrations below:

Note that 1-based subscript is used in illustrations (simplifies the syntax for the last element in the series).

In all cases below, will the vertex array descriptor have:

Structure holding Coordinates (Vertex)

Even though the structures that hold coordinates can be more complex than simple vertices we will still call them Vertices for simplicity. A Vertex is a structure of the form {pre, (x, y), post} where pre and post are optional data before and after the coordinates (X and Y).

Arrays of Vertices

Contiguous memory of Vertices of the form [v1, v2, ... vn]. If we expand with our definition of Vertex the memory can be seen like this: [{pre_1, (x1, y1), post_1}, {pre_2, (x2, y2), post_2}, ... {pre_n, (xn, yn), post_n}]

To Define the Vertex Array Descriptor we provide

Arrays of Pointers to Vertices

Pointers adjacent in memory with a stride from element to element as size of pointer: [p1, p2 ..., pn] where p1, p2, etc. are pointers to Vertices e.g. p1 -> v1, p2 -> v2, etc.

To Define the Vertex Array Descriptor we provide

Arrays of structures with pointers to Vertices

Arrays hold structures that in turn hold pointers to Vertices. These structures can hold other data as well. For instance we could have {pre1, p1, post1}, {pre2, p2, post2},....

Assume the elements are of type Elem with pointer p to Vertices, e.g. Elem1.p -> v1, Elem2.p -> v2, etc.

To Define the Vertex Array Descriptor we provide

Linked list of structures

Linked lists are chains of pointers to Nodes. Nodes can be located anywhere in memory, each node has a pointer to the next Node in the list and also holds the coordinate values of a specific vertex. Coordinates are held directly by the Node or in a nested Vertex structure.

For example: n1 -> {pre_1, (x1, y1), inter_1, n2, post_1}, n2 -> {pre_2, (x2, y2), inter_2, n3, post_2}, the location of the pointer to the next node is specified by a pointer offset, and the structure offset, as before, is the distance from the start of the structure to the first coordinate.

Please note that the pointer to next Node could also appear before the vertex, for instance: n1 -> [pre_1, n2, inter_1, (x1, y1), post_1].

To Define the Vertex Array Descriptor we provide

Linked list of pointers to structures

We can have a linked list where each node does not directly hold our vertex, but rather points to it. n1 -> {pre1, p1, inter_1, n2, post_1}, where p1 -> v1.

To Define the Vertex Array Descriptor we provide

Future versions

Version field needs to remain fixed as the first value and incremented when the memory layout or structure changes. The Version value should be incremented by 1 when the structure is changed.

Older readers may not assume the structure of a descriptor with a newer unknown version, but a best effort should be made to avoid breaking changes:

If a breaking change is made it should be clearly noted in an updated specification. Doing this will allow API developers to check what versions their API can support. Once a breaking change is approved, the entire structure with exception of the Version field may be restructured and the size of fields modified. While this is not foreseen, it cannot be precluded.

Note: Version numbers refer to the descriptor format, not the specification version


Copyright 2025 Jasper Schellingerhout. All rights reserved.