misc

OpenGL ES 3.0 (GLES30) Object State Tables as C++

Spec PDF: GL ES 3.0.6 (November 1, 2019)

Godbolt with no errors: x86-64 gcc 14.2 -std=c++20: https://godbolt.org/z/cbYEb939G

(Full Godbolt URL embed: https://godbolt.org/#z:OYLghAFBqd[…])

gles30_state_tables.cpp

//#! cc -std=c++20

// GL ES 3.0.6 (November 1, 2019):
// https://registry.khronos.org/OpenGL/specs/es/3.0/es_spec_3.0.pdf

#include <cstdint>
#include <array>
#include <string>
#include <vector>

// Types

using uint = uint32_t; // "Z+"
using ptr = intptr_t; // "Y"
using usize = size_t; // "Z+" with i64 getter.
using ufloat = float; // "R+" (>= 0.0f)
template<uint Possibilities=0, bool OrMore=false> using GLenum = uint32_t;

// (Easier to read array types as N,T instead of T,N)
template<uint N, class T> using arr = std::array<T,N>;
using std::vector;
using std::string;
using bytes = vector<std::byte>;

// -

class Buffer;
class Framebuffer;
class Program;
class Query;
class Renderbuffer;
class Sampler;
class Shader;
class Sync;
class Texture;
class TransformFeedback;
class VertexArray;

template<class T> using id = uint;

// Externs and Constexprs

extern int WINDOW_WIDTH;
extern int WINDOW_HEIGHT;
extern GLenum<> BACK;
extern GLenum<> CCW;
extern GLenum<> DONT_CARE;
extern GLenum<> TEXTURE0;
extern GLenum<> ALWAYS;
extern GLenum<> KEEP;
extern GLenum<> LESS;
extern GLenum<> ONE;
extern GLenum<> ZERO;
extern GLenum<> FUNC_ADD;
extern GLenum<> STATIC_DRAW;
extern GLenum<> RGBA4;
extern GLenum<> NEAREST_MIPMAP_LINEAR;
extern GLenum<> LINEAR;
extern GLenum<> REPEAT;
extern GLenum<> LEQUAL;
extern GLenum<> SYNC_FENCE;
extern GLenum<> UNSIGNALED;
extern GLenum<> SYNC_GPU_COMMANDS_COMPLETE;
extern GLenum<> RED;
extern GLenum<> GREEN;
extern GLenum<> BLUE;
extern GLenum<> ALPHA;
extern GLenum<> FLOAT;
extern GLenum<> INTERLEAVED_ATTRIBS;

//extern GLenum<> COLOR_ATTACHMENT0;
//extern GLenum<> NONE;
constexpr GLenum<> COLOR_ATTACHMENT0 = 0x8CE0;
constexpr GLenum<> NONE = 0;

// ImplementationLimits

struct ImplementationLimits {
   // p273: Table 6.28: Implementation Dependent Values
   usize MAX_ELEMENT_INDEX = (1 << 24) - 1;
   uint SUBPIXEL_BITS = 4;
   uint MAX_3D_TEXTURE_SIZE = 256;
   uint MAX_TEXTURE_SIZE = 2048;
   uint MAX_ARRAY_TEXTURE_LAYERS = 256;
   ufloat MAX_TEXTURE_LOD_BIAS = 2.0;
   uint MAX_CUBE_MAP_TEXTURE_SIZE = 2048;
   uint MAX_RENDERBUFFER_SIZE = 2048;
   uint MAX_DRAW_BUFFERS = 4;
   uint MAX_COLOR_ATTACHMENTS = 4;
   arr<2, uint> MAX_VIEWPORT_DIMS;
   arr<2, ufloat> ALIASED_POINT_SIZE_RANGE = {1, 1};
   arr<2, ufloat> ALIASED_LINE_WIDTH_RANGE = {1, 1};

   // p274: Table 6.29: Implementation Dependent Values (cont.)
   uint MAX_ELEMENTS_INDICES = 0;
   uint MAX_ELEMENTS_VERTICES = 0;
   vector<GLenum<>> COMPRESSED_TEXTURE_FORMATS;
   vector<GLenum<>> PROGRAM_BINARY_FORMATS;
   vector<GLenum<>> SHADER_BINARY_FORMATS;
   bool SHADER_COMPILER = true;

   struct PerShaderPrecisionFormat {
      arr<2, uint> range;
      uint precision;
   };
   arr<2*2*3, PerShaderPrecisionFormat> shaderPrecisionFormats;

   usize MAX_SERVER_WAIT_TIMEOUT = 0;

   // p275: Table 6.30: Implementation Dependent Version and Extension Support
   vector<string> EXTENSIONS;
   uint MAJOR_VERSION = 3;
   uint MINOR_VERSION = 0;
   string RENDERER;
   string SHADER_LANGUAGE_VERSION;
   string VENDOR;
   string VERSION;

   // p276: Table 6.31: Implementation Dependent Vertex Shader Limits
   uint MAX_VERTEX_ATTRIBS = 16;
   uint MAX_VERTEX_UNIFORM_COMPONENTS = 1024;
   uint MAX_VERTEX_UNIFORM_VECTORS = 256;
   uint MAX_VERTEX_UNIFORM_BLOCKS = 12;
   uint MAX_VERTEX_OUTPUT_COMPONENTS = 64;
   uint MAX_VERTEX_TEXTURE_IMAGE_UNITS = 16;

   // p277: Table 6.32: Implementation Dependent Fragment Shader Limits
   uint MAX_FRAGMENT_UNIFORM_COMPONENTS = 896;
   uint MAX_FRAGMENT_UNIFORM_VECTORS = 224;
   uint MAX_FRAGMENT_UNIFORM_BLOCKS = 12;
   uint MAX_FRAGMENT_INPUT_COMPONENTS = 60;
   uint MAX_TEXTURE_IMAGE_UNITS = 16;
   int MIN_PROGRAM_TEXEL_OFFSET = -8;
   int MAX_PROGRAM_TEXEL_OFFSET = 7;

   // p278: Table 6.33: Implementation Dependent Aggregate Shader Limits
   uint MAX_UNIFORM_BUFFER_BINDINGS = 24;
   usize MAX_UNIFORM_BLOCK_SIZE = 16384;
   uint UNIFORM_BUFFER_OFFSET_ALIGNMENT = 256;
   uint MAX_COMBINED_UNIFORM_BLOCKS = 24;
   constexpr usize MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS() const {
      return MAX_VERTEX_UNIFORM_BLOCKS * MAX_UNIFORM_BLOCK_SIZE / 4
        + MAX_VERTEX_UNIFORM_COMPONENTS;
   }
   constexpr usize MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS() const {
      return MAX_FRAGMENT_UNIFORM_BLOCKS * MAX_UNIFORM_BLOCK_SIZE / 4
      + MAX_FRAGMENT_UNIFORM_COMPONENTS;
   }
   uint MAX_VARYING_COMPONENTS = 60;
   uint MAX_VARYING_VECTORS = 15;
   uint MAX_COMBINED_TEXTURE_IMAGE_UNITS = 32;

   // p279: Table 6.34: Implementation Dependent Transform Feedback Limits:
   uint MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS = 64;
   uint MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = 4;
   uint MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS = 4;

   // p281: Table 6.36: Miscellaneous
   arr<3, id<Query>> CURRENT_QUERY = {0};
   id<Buffer> COPY_READ_BUFFER_BINDING = 0;
   id<Buffer> COPY_WRITE_BUFFER_BINDING = 0;

   // Implied:
   usize _MAX_STENCIL_BITS = 8;
};
static constexpr auto LIMITS = ImplementationLimits{};

// ContextState

struct ContextState {
   // p248: Table 6.3: Vertex Array Data (not in vertex array objects)
   id<Buffer> ARRAY_BUFFER_BINDING = 0;
   id<VertexArray> VERTEX_ARRAY_BINDING;
   bool PRIMITIVE_RESTART_FIXED_INDEX = false;

   // p250: Table 6.5: Transformation State
   arr<4, int> VIEWPORT = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
   arr<2, ufloat> DEPTH_RANGE = {0, 1};
   id<TransformFeedback> TRANSFORM_FEEDBACK_BINDING = 0;

   // p251: Table 6.6: Rasterization
   bool RASTERIZER_DISCARD = false;
   ufloat LINE_WIDTH = 1.0;
   bool CULL_FACE = false;
   GLenum<3> CULL_FACE_MODE = BACK;
   GLenum<2> FRONT_FACE = CCW;
   float POLYGON_OFFSET_FACTOR = 0;
   float POLYGON_OFFSET_UNITS = 0;
   bool POLYGON_OFFSET_FILL = false;

   // p252: Table 6.7: Multisampling
   bool SAMPLE_ALPHA_TO_COVERAGE = false;
   bool SAMPLE_COVERAGE = false;
   ufloat SAMPLE_COVERAGE_VALUE = 1.0f;
   bool SAMPLE_COVERAGE_INVERT = false;

   // p253: Table 6.8: Textures (selector, state per texture unit)
   GLenum<32,true> ACTIVE_TEXTURE = TEXTURE0;
   arr<LIMITS.MAX_COMBINED_TEXTURE_IMAGE_UNITS, id<Texture>> TEXTURE_BINDING_2D = {0};
   arr<LIMITS.MAX_COMBINED_TEXTURE_IMAGE_UNITS, id<Texture>> TEXTURE_BINDING_3D = {0};
   arr<LIMITS.MAX_COMBINED_TEXTURE_IMAGE_UNITS, id<Texture>> TEXTURE_BINDING_2D_ARRAY = {0};
   arr<LIMITS.MAX_COMBINED_TEXTURE_IMAGE_UNITS, id<Texture>> TEXTURE_BINDING_CUBE_MAP = {0};
   arr<LIMITS.MAX_COMBINED_TEXTURE_IMAGE_UNITS, id<Sampler>> SAMPLER_BINDING = {0};

   // p256: Table 6.11: Pixel Operations
   bool SCISSOR_TEST = false;
   arr<4, int> SCISSOR_BOX = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
   bool STENCIL_TEST = false;
   GLenum<8> STENCIL_FUNC = ALWAYS;
   uint STENCIL_VALUE_MASK = (1 << LIMITS._MAX_STENCIL_BITS)-1;
   uint STENCIL_REF = 0;
   GLenum<8> STENCIL_FAIL = KEEP;
   GLenum<8> STENCIL_PASS_DEPTH_FAIL = KEEP;
   GLenum<8> STENCIL_PASS_DEPTH_PASS = KEEP;
   GLenum<8> STENCIL_BACK_FUNC = ALWAYS;
   uint STENCIL_BACK_VALUE_MASK = (1 << LIMITS._MAX_STENCIL_BITS)-1;
   uint STENCIL_BACK_REF = 0;
   GLenum<8> STENCIL_BACK_FAIL = KEEP;
   GLenum<8> STENCIL_BACK_FAIL_PASS_DEPTH_FAIL = KEEP;
   GLenum<8> STENCIL_BACK_FAIL_PASS_DEPTH_PASS = KEEP;
   bool DEPTH_TEST = false;
   GLenum<8> DEPTH_FUNC = LESS;
   bool BLEND = false;
   GLenum<19> BLEND_SRC_RGB = ONE;
   GLenum<19> BLEND_SRC_ALPHA = ONE;
   GLenum<19> BLEND_DST_RGB = ZERO;
   GLenum<19> BLEND_DST_ALPHA = ZERO;
   GLenum<5> BLEND_EQUATION_RGB = FUNC_ADD;
   GLenum<5> BLEND_EQUATION_ALPHA = FUNC_ADD;
   arr<4, float> BLEND_COLOR = {0,0,0,0};
   bool DITHER = true;

   // p257: Table 6.12: Framebuffer Control
   arr<4, bool> COLOR_WRITEMASK = {true, true, true, true};
   bool DPETH_WRITEMASK = true;
   uint STENCIL_WRITEMASK = ~0;
   uint STENCIL_BACK_WRITEMASK = ~0;
   arr<4, float> COLOR_CLEAR_VALUE = {0,0,0,0};
   ufloat DEPTH_CLEAR_VALUE = 1.0;
   uint STENCIL_CLEAR_VALUE = 0;
   id<Framebuffer> DRAW_FRAMEBUFFER_BINDING = 0;
   id<Framebuffer> READ_FRAMEBUFFER_BINDING = 0;
   id<Renderbuffer> RENDERBUFFER_BINDING = 0;

   // p261: Table 6.16: Pixels
   uint UNPACK_IMAGE_HEIGHT = 0;
   uint UNPACK_SKIP_IMAGES = 0;
   uint UNPACK_ROW_LENGTH = 0;
   uint UNPACK_SKIP_ROWS = 0;
   uint UNPACK_SKIP_PIXELS = 0;
   uint UNPACK_ALIGNMENT = 0;
   uint PACK_ROW_LENGTH = 0;
   uint PACK_SKIP_ROWS = 0;
   uint PACK_SKIP_PIXELS = 0;
   uint PACK_ALIGNMENT = 0;
   id<Buffer> PIXEL_PACK_BUFFER_BINDING = 0;
   id<Buffer> PIXEL_UNPACK_BUFFER_BINDING = 0;

   // p263: Table 6.18: Program Object State
   id<Program> CURRENT_PROGRAM = 0;

   // p267: Table 6.22: Vertex Shader State (not part of program objects)
   arr<LIMITS.MAX_VERTEX_ATTRIBS, arr<4, float>> CURRENT_VERTEX_ATTRIB = 0;

   // p269: Table 6.24: Transform Feedback State
   id<Buffer> TRANSFORM_FEEDBACK_BUFFER_BINDING = 0;

   // p270: Table 6.25: Uniform Buffer Binding State
   id<Buffer> UNIFORM_BUFFER_BINDING = 0;

   struct PerUniformBuffer {
      id<Buffer> UNIFORM_BUFFER_BINDING = 0;
      usize UNIFORM_BUFFER_START = 0;
      usize UNIFORM_BUFFER_SIZE = 0;
   };
   arr<LIMITS.MAX_UNIFORM_BUFFER_BINDINGS, PerUniformBuffer> uniformBuffers;

   // p272: Table 6.27: Hints
   GLenum<3> GENERATE_MIPMAP_HINT = DONT_CARE;
   GLenum<3> FRAGMENT_SHADER_DERIVATIVE_HINT = DONT_CARE;
};

// BufferState

struct BufferState {
   // p249: Table 6.4: Buffer Object State
   usize BUFFER_SIZE = 0;
   GLenum<9> BUFFER_USAGE = STATIC_DRAW;
   uint BUFFER_ACCESS_FLAGS = 0;
   bool BUFFER_MAPPED = false;
   ptr BUFFER_MAP_POINTER = 0;
   usize BUFFER_MAP_OFFSET = 0;
   usize BUFFER_MAP_LENGTH = 0;
};

// FramebufferState

struct FramebufferState {
   // p258: Table 6.13: Framebuffer (state per framebuffer object)
   arr<LIMITS.MAX_DRAW_BUFFERS, GLenum<11,true>> DRAW_BUFFERi = {COLOR_ATTACHMENT0, 0 };
   GLenum<11,true> READ_BUFFER; // Queried from READ_FRAMEBUFFER

   // p259: Table 6.14: Framebuffer (state per attachment point)
   struct PerAttachment {
      GLenum<4> FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = NONE;
      union {
        id<Renderbuffer> rb = 0;
        id<Texture> tex;
      } FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = {};
      uint FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0;
      uint FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = NONE;
      int FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER = 0; // Errata: uint?
      GLenum<2> FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING;
      GLenum<4> FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE;
      uint FRAMEBUFFER_ATTACHMENT_RED_SIZE;
      uint FRAMEBUFFER_ATTACHMENT_GREEN_SIZE;
      uint FRAMEBUFFER_ATTACHMENT_BLUE_SIZE;
      uint FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE;
      uint FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE;
      uint FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE;
   };
   arr<LIMITS.MAX_COLOR_ATTACHMENTS, PerAttachment> attachments;

   // p280: Table 6.35: Framebuffer Dependent Values
   uint SAMPLE_BUFFERS = 0;
   uint SAMPLES = 0;
   uint MAX_SAMPLES = 4;
   uint RED_BITS;
   uint GREEN_BITS;
   uint BLUE_BITS;
   uint ALPHA_BITS;
   uint DEPTH_BITS;
   uint STENCIL_BITS;
   GLenum<> IMPLEMENTATION_COLOR_READ_TYPE; // Queried from READ_FRAMEBUFFER.
   GLenum<> IMPLEMENTATION_COLOR_READ_FORMAT; // Queried from READ_FRAMEBUFFER.
};

static_assert(FramebufferState{}.DRAW_BUFFERi[0] == COLOR_ATTACHMENT0);
static_assert(FramebufferState{}.DRAW_BUFFERi[1] == 0);
static_assert(FramebufferState{}.DRAW_BUFFERi[2] == 0);

// ProgramState

struct ProgramState {
   // p263: Table 6.18: Program Object State
   bool DELETE_STATUS = false;
   bool LINK_STATUS = false;
   bool VALIDATE_STATUS = false;
   vector<id<Shader>> ATTACHED_SHADERS = {};
   string INFO_LOG = "";

   struct PerActiveUniform {
      int location;
      uint size;
      GLenum<> type;
      string name;
      // p265: Table 6.20: Program Object State (cont.)
      GLenum<27> UNIFORM_TYPE;
      uint UNIFORM_SIZE;
      int UNIFORM_BLOCK_INDEX;
      int UNIFORM_OFFSET;
      // p266: Table 6.21: Program Object State (cont.)
      int UNIFORM_ARRAY_STRIDE;
      int UNIFORM_MATRIX_STRIDE;
      bool UNIFORM_IS_ROW_MAJOR;
   };
   vector<PerActiveUniform> ACTIVE_UNIFORMS = {};
   //vector<PerActiveAttribute> ACTIVE_ATTRIBUTES = {}; // Moved below.
   bool PROGRAM_BINARY_RETRIEVABLE_HINT = false;
   bytes PROGRAM_BINARY = {};

   // p264: Table 6.19: Program Object State (cont.)
   struct PerActiveAttribute {
      int location;
      uint size;
      GLenum<> type;
      string name;
   };
   vector<PerActiveAttribute> ACTIVE_ATTRIBUTES = {}; // Actually p263.

   GLenum<2> TRANSFORM_FEEDBACK_BUFFER_MODE = INTERLEAVED_ATTRIBS;
   struct PerActiveTfVarying {
      uint size;
      GLenum<> type;
      string name;
   };
   vector<PerActiveTfVarying> TRANSFORM_FEEDBACK_VARYINGS = {};

   // p265: Table 6.20: Program Object State (cont.)
   //vector<PerActiveUniformBlock> ACTIVE_UNIFORM_BLOCKS = {}; // Moved below.
   struct PerActiveUniformBlock {
      // p266: Table 6.21: Program Object State (cont.)
      uint UNIFORM_BLOCK_BINDING = 0;
      uint UNIFORM_BLOCK_DATA_SIZE;
      vector<uint> UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES;
      bool UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = false;
      bool UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = false;
   };
   vector<PerActiveUniformBlock> ACTIVE_UNIFORM_BLOCKS = {}; // Actually p265.
};

// QueryState

struct QueryState {
   // p268: Table 6.23: Query Object State
   uint QUERY_RESULT = 0;
   bool QUERY_RESULT_AVAILABLE = false;
};

// RenderbufferState

struct RenderbufferState {
   // p260: Table 6.15: Renderbuffer (state per renderbuffer object)
   uint RENDERBUFFER_WIDTH = 0;
   uint RENDERBUFFER_HEIGHT = 0;
   GLenum<43> RENDERBUFFER_INTERNAL_FORMAT = RGBA4;
   uint RENDERBUFFER_RED_SIZE = 0;
   uint RENDERBUFFER_GREEN_SIZE = 0;
   uint RENDERBUFFER_BLUE_SIZE = 0;
   uint RENDERBUFFER_ALPHA_SIZE = 0;
   uint RENDERBUFFER_DEPTH_SIZE = 0;
   uint RENDERBUFFER_STENCIL_SIZE = 0;
   uint RENDERBUFFER_SAMPLES = 0;
};

// SamplerState

struct SamplerState {
   // p255: Table 6.10: Textures (state per sampler object)
   GLenum<6> TEXTURE_MIN_FILTER = NEAREST_MIPMAP_LINEAR;
   GLenum<6> TEXTURE_MAG_FILTER = LINEAR;
   GLenum<4> TEXTURE_WRAP_S = REPEAT;
   GLenum<4> TEXTURE_WRAP_T = REPEAT;
   GLenum<4> TEXTURE_WRAP_R = REPEAT;

   float TEXTURE_MIN_LOD = -1000;
   float TEXTURE_MAX_LOD = 1000;
   GLenum<2> TEXTURE_COMPARE_MODE = NONE;
   GLenum<8> TEXTURE_COMPARE_FUNC = LEQUAL;
};

// ShaderState

struct ShaderState {
   // p262: Table 6.17: Shader Object State
   GLenum<3> SHADER_TYPE;
   bool DELETE_STATUS = false;
   bool COMPILE_STATUS = false;
   string ShaderInfoLog = "";
   uint INFO_LOG_LENGTH = 0;
   string ShaderSource = "";
   uint SHADER_SOURCE_LENGTH = 0;
};

// SyncState

struct SyncState {
   // p271: Table 6.26: Sync (state per sync object)
   GLenum<1> OBJECT_TYPE = SYNC_FENCE;
   GLenum<2> SYNC_STATUS = UNSIGNALED;
   GLenum<1> SYNC_CONDITION = SYNC_GPU_COMMANDS_COMPLETE;
   uint SYNC_FLAGS = 0;
};

// TextureState

struct TextureState {
   // p254: Table 6.9: Textures (state per texture object)
   GLenum<6> TEXTURE_SWIZZLE_R = RED;
   GLenum<6> TEXTURE_SWIZZLE_G = GREEN;
   GLenum<6> TEXTURE_SWIZZLE_B = BLUE;
   GLenum<6> TEXTURE_SWIZZLE_A = ALPHA;

   GLenum<6> TEXTURE_MIN_FILTER = NEAREST_MIPMAP_LINEAR;
   GLenum<6> TEXTURE_MAG_FILTER = LINEAR;
   GLenum<4> TEXTURE_WRAP_S = REPEAT;
   GLenum<4> TEXTURE_WRAP_T = REPEAT;
   GLenum<4> TEXTURE_WRAP_R = REPEAT;

   float TEXTURE_MIN_LOD = -1000;
   float TEXTURE_MAX_LOD = 1000;
   uint TEXTURE_BASE_LEVEL = 0;
   uint TEXTURE_MAX_LEVEL = 1000;
   GLenum<2> TEXTURE_COMPARE_MODE = NONE;
   GLenum<8> TEXTURE_COMPARE_FUNC = LEQUAL;
   bool TEXTURE_IMMUTABLE_FORMAT = false;
   uint TEXTURE_IMMUTABLE_LEVELS = 0;
};

// TransformFeedbackState

struct TransformFeedbackState {
   // p269: Table 6.24: Transform Feedback State
   struct PerBinding {
      id<Buffer> TRANSFORM_FEEDBACK_BUFFER_BINDING = 0;
      usize TRANSFORM_FEEDBACK_BUFFER_START = 0;
      usize TRANSFORM_FEEDBACK_BUFFER_SIZE = 0;
   };
   arr<LIMITS.MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, PerBinding> bindings = {};

   bool TRANSFORM_FEEDBACK_PAUSED = false;
   bool TRANSFORM_FEEDBACK_ACTIVE = false;
};

// VertexArrayState

struct VertexArrayState {
   // p247: Table 6.2: Vertex Array Object State
   struct PerAttrib {
      bool VERTEX_ATTRIB_ARRAY_ENABLED = false;
      GLenum<5> VERTEX_ATTRIB_ARRAY_SIZE = 4;
      uint VERTEX_ATTRIB_ARRAY_STRIDE = 0;
      GLenum<9> VERTEX_ATTRIB_ARRAY_TYPE = FLOAT;
      bool VERTEX_ATTRIB_ARRAY_NORMALIZED = false;
      bool VERTEX_ATTRIB_ARRAY_INTEGER = false;
      uint VERTEX_ATTRIB_ARRAY_DIVISOR = 0;
      ptr VERTEX_ATTRIB_ARRAY_POINTER = 0;
      id<Buffer> VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0;
   };
   arr<LIMITS.MAX_VERTEX_ATTRIBS, PerAttrib> attribs;

   id<Buffer> ELEMENT_ARRAY_BUFFER_BINDING = 0;
};