OpenGL
OpenGL
Notes and reminders aroud my understanding of modern OpenGL (4.2+)
A program (the client) issues commands, and these commands are interpreted and processed by the GL (the server).
TODOs
-
Find a spec definition of Command Stream
-
Find a spec defintion of what it means for a command to “reach the GL server”. (In particular, does it mean it is actually on the GPU, or could it just reach an internal driver buffer in main memory?)
- I’d like to get a formal definition of what the spec exactly means by:
- vertex array (e.g. p362 sec 10.3.9)
Additionally, each vertex array has an associated binding so there is a buffer object binding for each of the vertex attribute arrays. The initial values for all buffer object bindings is zero. (p84 bottom)
- vertex attribute array e.g. first line p365
“If an enabled vertex attribute array is instanced”
- vertex array (e.g. p362 sec 10.3.9)
- Understand derived state:
Some of [the state] […] is derived state visible only by the effect it has on how OpenGL operates. (p3 top)
-
Complete the list of fixed-function stages
- Complete compute rendering command section
Conventions
“GLSL matrices are always column-major” (https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL))
Shader code use pre-multiplication: tansformation * vector
.
Prefixes in the C language:
- functions:
gl
- constants:
GL_
- types:
GL
(e.g.GLfloat
)
Data flow
Figure 3.1: Block diagram of the GL pipeline
Execution model
The application (client) issue commands executed by the GL (server). Commands are always processed in the order they are received.
Implementation can buffer commands in a command queue.
Context and Object model
A server may maintain a number of GL contexts, each of which is an encapsulation of current GL state and objects. A client may choose to be made current to any one of these contexts.
p3 1.2.2:
OpenGL contains […] many types of objects representing programmable shaders and the data they consume and generate, as well as other context state controlling non-programmable aspects of OpenGL.
Context state
Context state belongs to the GL context as a whole, rather than to specific instances of GL objects. It controls fixed-function stages of the GPU and specifies bindings of objects to the context. There are two types server state (majority) and client state. (p27 2.5)
Fixed function stages:
- Clipping
- Rasterization
- Framebuffer clear
- (TODO)
Generalities on objects
OpenGL API gives access to objects, which are identified by their name,
Each type of object has a distinct name space, where names are a GLuint
value (not actual text).
Names are distinct from the underlying objects:
glGen*()
functions reserve and return previously unused names,
but the objects are created and bound to names later, through other GL functions
(e.g. when names are bound to the context with glBind*()
).
Objects are modeled as a state vector, creating the object notably creates this state vector.
GL defines many types of objects, applications operate on instances of those objects. Specific instances are bound to a context.
Binding objects to the context determines which objects are used during commands execution.
“Standard” OpenGL Objects operations
The object model describes how most types of objects are managed by the application.
Available names are obtained and reserved with glGen*()
.
Underlying object is created and associated to a given name with first call to glBind*()
for this name: it creates a state vector for the object with default values by the spec.
Additionnaly, the bound object is the one affected by further GL operations/queries for this type.
Some type of objects have a target (such as Buffer Objects,
in which case there is one distinct binding point for each target.
Object might be created and associated to a new name at once with glCreate*()
.
Names are marked as unused with glDelete*()
, but the underlying object and its state are not deallocated until it is not longer in use.
Query commands start with glGet
.
Event Model
Fence
A Fence Object is associates with a Fence Command placed in the GL command stream.
The Fence Object is initially UNSIGNALED
, and becomes SIGNALED
at some point after the Fence Command is completed.
Fence command completion guarantees all preceding commands in the same command stream did complete.
Queries
Query Objects give access to asynchronous queries: a mechanism to return information about the processing of a sequence of GL commands.
Query object names are reserved with glGenQueries()
.
A query object is associated to the name with glBeginQuery*()
(or glQueryCounter()
for timer query), for provided target.
The state is queried via glGetQuery*Object*()
, notably the result with QUERY_RESULT
.
There is a query result buffer (buffer target GL_QUERY_BUFFER
) to place query results in server memory.
Timing
gl[Begin|End]Query(GL_TIME_ELAPSED)
: time elapsed queryglQueryCounter()
: timer query
Both record time after all previous commands have been fully realized (Note: there is an error in the API reference of glQueryCounter()
).
Note: The synchronous command glGetInteger*(TIMESTAMP)
returns the GL time after all previous commands have reached the GL server,
but have not yet necessarily completed (by opposition to a timer query, providing time after completion).
This distinction allows to measure latency.
Buffer Objects
Buffer objects contain a data store holding a fixed-size allocation of server memory, and provide a mechanism to allocate, initialize, destroy, read from, and write to such memory.
The data store of a buffer object is created via:
gl*BufferStorage()
to obtain an immutable-size buffer (content is mutable).gl*BufferData()
for a mutable buffer.
Buffer data can also be:
- modified
- mapped (for access in client address space),
- invalidated (
glInvalidateBuffer*Data()
, data then have undefined value) - copied
- queried (
glGet*BufferParameter*()
for parameters,glGet*BufferPointerv()
for mapped pointer,glGet*Buffer*Data()
for data)
Buffer usage is dictated by the target to which it is currently bound.
Buffer objects created by binding a name returned by
glGenBuffers
to any of the valid targets are formally equivalent.
(i.e. a buffer can be bound to distinct target at different points).
- Array buffer is a buffer from which generic vertex attributes are fetched. (i.e. the buffer target is
GL_ARRAY_BUFFER
) - Element buffer (or Index buffer) is the buffer from which vertex indices are fetched (target is
GL_ELEMENT_ARRAY_BUFFER
). - Uniform Buffer Objects are buffer objects used by an application to store uniform data for a shader program.
The shader program access the storage via GLSL uniform blocks (the GLSL grouping of uniforms)
(target is
GL_UNIFORM_BUFFER
). The link is made via a uniform buffer binding location. - Shader Storage Buffer Objects are accessed via GLSL shader storage block
(target is
GL_SHADER_STORAGE_BUFFER
).
Indexed targets
Some GL buffer targets are indexed. For indexed targets, there is an array of buffer object binding points, as well as the usual single general binding point (like for non-indexed targets).
A buffer can be bound to the binding point at binding index with
glBindBufferBase(target, index, buffer)
(or glBindBufferRange()
).
Binding index is the index into an indexed buffer object target. Not to be confused with the index of the resources in GLSL programs (e.g. block index for an interface block)
Indexed targets:
GL_ATOMIC_COUNTER_BUFFER
GL_TRANSFORM_FEEDBACK_BUFFER
GL_UNIFORM_BUFFER
GL_SHADER_STORAGE_BUFFER
Notes: Binding to an indexed binding point is required for specific use by GL
(notably, to access the buffer in a shader program),
and also binds to the general binding point (thus unbinding previously bound buffer).
Non-indexed binding (glBindBuffer()
) can also be used for indexed targets,
it only binds to the general binding point.
In both case, the general binding point allows to use general buffer object manipulation functions, whereas indexed binding usually means that the buffer content will be used (rather than just modified).
Vertex Array Object
Vertex Array Object (or VAO) capture state for rendering. A VAO represent a collection of sets of vertex attributes, each set values (the actual data) are stored outside the VAO, as an array in a buffer object data store (TODO is the buffer called vertex attribute array?).
Binding the VAO restore all captured state at once:
- Vertex format
- enabled arrays (
glEnableVertex[AttribArray|ArrayAttrib]()
). - data type, element count, normalization
- relative offset (added to the binding point base offset, thus allowing interleaving attributes)
- associated buffer binding point (binding index <-> vertex attribute index)
- enabled arrays (
- Buffer binding point state, shared by all vertex attributes pulled from this binding point
- source buffer object (buffer object <-> binding index)
- base offset into the buffer
- byte stride
- Instance divisor, for instanced rendering
- Index Buffer binding (The last buffer object bound to
GL_ELEMENT_ARRAY_BUFFER
while the VAO was bound)
Important: if the array corresponding to an attribute (required by a Vertex Shader) is not enabled, the corresponding element is taken from the current attribute state.
(glVertexAttrib*()
family of functions allow to set the default state for an attribute, which is used when the attribute array is not enabled)
Shader Objects & Program Objects
Shader objects to be used by one/several of the programmable stages are linked together into a Program object. Shader programs executed by these stages are called executable, all information necessary for defining each executable is encapsulated in a program object.
Interface blocks
Interface blocks are GLSL side constructs. They group input, output, uniform (in uniform blocks), buffer variables (in shader storage blocks).
storage_qualifier[in / out / uniform / buffer] block_name
{
<members>
} instance_name;
in
, out
can only be used between shaders stages (not as vertex shader input or fragment shader output).
Uniform blocks and shader storage blocks are collectively called buffer-backed blocks: the storage for their content come from a Buffer Object. Those buffers are indexed.
Program Interfaces & Introspection
An executable communicate with other pipeline stages or the application through a variety of interfaces. Each interface has a list of active resources (built at compile time).
GL provides functions to query properties of the interfaces of a program object:
glGetProgramInterfaceiv()
queries properties of the interface itself (e.g. number of active resources, maximal resource name length…)glGetProgramResourceiv()
queries properties of the active resource atindex
inprogramInterface
.glGetProgramResourceName()
queries the name of active resource atindex
inprogramInterface
.
For interface blocks, the active resources are the blocks themselves, not the variables they group:
GL_UNIFORM_BLOCK
active variables are accessible via theGL_UNIFORM
program interface.GL_SHADER_STORAGE_BLOCK
active variables are accessible via theGL_BUFFER_VARIABLE
program interface. (consistent with the name buffer variable for individual variable in a shader storage blocks.)
Program Pipeline Objects
A program pipeline object composes a pipeline by using shader stages from several program objects,
which must have the GL_PROGRAM_SEPARABLE
parameter set. (glProgramParameteri()
).
Note: they build on top of program objects, since they take shader stages from them.
Program pipeline objects allow to combine stages without requiring one program object instance for each combination.
Textures
Pack
: read from OpenGLUnpack
: write to OpenGL
glTexStorage*()
specifies the internal format, i.e. the format OpenGL will use to store the texture data.
Note: For the internalformat
parameter, use sized formats to be explicit (instead of letting implementation decide).
glTex*Image*()
is used to load data into an allocated texture. The format and type are describing the data provided by the application.
Textures have a target. Unlike buffers, a texture must always be bound to the same target it was initially created with.
Using textures with GLSL samplers
Texture names (the GLuint
returned by glGenTextures()
) have to be bound to texture image units (sometimes just called texture unit, warning: image unit are distinct, corresponding to image uniforms below),
Binding a texture with glBindTexure()
also binds it to the currently active texture image unit set with glActiveTexture()
.
GLSL shader programs can sample the texure via sampler
uniform variables (whose type must match the texture target), set to the texture image unit with glUniform()
.
Warning: A sampler
uniform variable is a GLSL type, distinct from a Sampler Object, which is an application object encapsulating sampling parameters.
Image load/store
image variables are GLSL uniform variables allowing arbitrary read/write to/from image within a Texture.
A given level (“image”) in a texture is bound to an image unit (distinct from texture image unit) with glBindImageTexture()
. The uniform value of an image
GLSL variable (of matching type) is set to the same image unit.
Framebuffers
A framebuffer is a collection of logical buffers (color, depth, stencil) used as destination of rendering operations, or source of readback operations.
Framebuffer Objects (FBOs) are user-created. Their logical buffers reference individual framebuffer–attachable images, either from textures (render to texture) or from renderbuffers (off-screen rendering), attached to a set of attachment points. An FBO must be complete to be used for rendering.
The default framebuffer is provided by the system upon context creation. It usually consists of multiple colors buffers ([front|back][left|right] buffer), depth buffer and stencil buffer.
GL has 2 active framebuffers:
- draw framebuffer (destination for rendering operations)
- read framebuffer (source for readback operations).
The separation between Draw and Read framebuffers exists so the Read frambebuffer can be blitted to the Draw framebuffer.
Buffers
- Color buffers can have up to 4 color components (RGBA) per pixel. All framebuffers might have multiple colors images that could be read/written.
- Depth buffer pixels are a single value (unsigned int or floating point)
- Stencil buffer pixel are single int value.
Renderbuffer
Renderbuffer objects contain images, intended to be used with Framebuffer Objects. Unlike textures, they are optimized for use as render targets (i.e. when there is no need to sample the image), and natively accomodate multisampling.
Vertex specification
glspec 10 p337
The process of specifying attributes of a vertex and passing them to a shader is referred to as transferring a vertex to the GL.
Vertices are specified with on or more generic vertex attributes (1, 2, 3 or 4 scalar values).
Vertex shaders access an array of 4-components generic vertex attributes, this array size is MAX_VERTEX_ATTRIBS
.
They are loaded into the shader attribute variable bound to the generic attribute index ([0..MAX_VERTEX_ATTRIBS[
).
glspec 10.2.1 p348
Matrices are loaded into these slots in column major order. Matrix columns are loaded in increasing slot numbers.
Current attribute values are transferred for a vertex when there is not vertex array enabled for a required attribute.
The value can be changed by glVertexAttrib*()
, and queried with glGetVertexAttrib*()
.
Vertex Arrays
Vertex data can be placed into arrays (Buffers with target GL_ARRAY_BUFFER
).
All the state to represent the vertex arrays is stored in a Vertex Array Object:
it encapsulates all state related to the definition of data used by the vertex processor, and notably collects the buffer objects to be used by the vertex stage.
glEnableVertexAttribArray()
(DSA variant: glEnableVertexArrayAttrib()
) enables an individual generic vertex attribute array in the selected VAO.
Separate format and data source
Specifying the organization of data store of generic vertex attribute for a VAO:
glVertexAttrib*Format(attribindex, size, type, normalized, relativeoffset)
(DSA variant: glVertexArrayAttrib*Format()
).
This stores in the VAO that the generic vertex attribute at index attribindex
will be fetched from an array of described format,
starting at relativeoffset
(allow interleaving different attributes in a single buffer).
Associating a vertex buffer object to a VAO’s buffer binding index:
glBindVertexBuffer(bindingindex, buffer, offset, stride)
(DSA variant: glVertexArrayVertexBuffer()
).
This provides a stride
and offset
for a buffer and attach it to the specified binding index of selected VAO.
Associating a vertex attribute to a VAO’s buffer binding index:
glVertexAttribBinding(attribindex, bindingindex)
(DSA variant: glVertexArrayAttribBinding()
).
The vertex attribute at attribindex
will be fetched from the buffer currently attached to bindingindex
of the selected VAO.
Vertex Attribute Divisor
A generic attribute is instanced when it divisor is non-zero. In this situation, the attribute advances once per divisor number of instances (implies instanced rendering). By opposition, attributes with the default divisor 0 advance once per vertex.
Divisor is set with glVertex*BindingDivisor()
.
Rendering commands
Rendering commands perform rendering into a framebuffer. They are:
- drawing commands
glBlitFramebuffer()
glClear()
,glClearBuffer*()
glDispatchCompute*()
Drawing commands
see: glspec 10.4
Drawing commands, of the form gl*Draw*()
, send vertices through OpenGL to be rendered.
Some parameters are shared by most commands:
GLenum
parametermode
is the primitive type.GLsizei
parametercount
is the number of vertices that will be tranferred.
gl[?Multi]Draw[Arrays|Elements][?Indirect]()
gl[?Multi]DrawElementsBaseVertex()
glMultiDraw[Arrays|Elements]IndirectCount()
Arrays|Elements
:Array
: fetches in contiguous sequence from enabled vertex arrays.Elements
: fetches from enabled vertex arrays at indices provided by the buffer bound toGL_ELEMENT_ARRAY_BUFFER
. Parametertype
indicates the integral type of the value in the element array buffer.- In both cases, the index of the current array element may be read by a VS via
gl_VertexID
.
Multi
: is equivalent to issuing parameterdrawcount
draw commands at once, parameters becoming pointers into arrays ofdrawcount
elements. VS can read the index of the draw viagl_DrawID
.BaseVertex
: the value ofGLint
parameterbasevertex
is added to the indices read from the element array buffer before pulling the vertex data. It allows to load vertices for distinct models in the sameGL_ARRAY_BUFFER
without requiring to re-index subsequent models. (Note: this is only available forElement
variant, becauseArray
already takes afirst
parameter).Count
:drawcount
is now a byte offset into the (server space) buffer bound toGL_PARAMETER_BUFFER
, where a singleGLsizei
value is read to provide the actual draw count.Indirect
: read some of the draw parameters from server space memory, from the buffer bound toGL_DRAW_INDIRECT_BUFFER
, offset by parameterindirect
bytes.
Instanced
gl[DrawArraysInstanced[?BaseInstance]()
glDrawElementsInstanced[?BaseVertex]}[?BaseInstance]()
Instanced
: the sequence of vertices will be drawn parameterinstance
times. Instanced vertex attribute array (non-zerodivisor
) will advance by one everydivisor
instances. VS may read the current instance index viagl_InstanceID
.BaseInstance
: parameterbaseinstance
specifies the first element within instanced vertex attributes (i.e. an index offset).
Indirect
GL_DRAW_INDIRECT_BUFFER
should contain data matching the corresponding struct (as tightly packed 32-bit values):
gl*DrawArraysIndirect*()
:struct DrawArraysIndirectCommand { uint count; uint instanceCount; uint first; uint baseInstance; };
gl*DrawElementsIndirect*()
:struct DrawElementsIndirectCommand { uint count; uint instanceCount; uint firstIndex; int baseVertex; uint baseInstance; }
In case of glMultiDraw[Arrays|Elements]Indirect()
, the buffer, offset by indirect
bytes, should contain at least drawcount
instances of the struct.
Each instance being stride
bytes appart (0 meaning tightly packed).
Range
glDrawRangeElements[?BaseVertex]()
Note: not usefull anymore, because the vertex data and indices live in server memory.
Range
: all element (indices) read from the element array buffer must have a value betweenstart
andend
parameters (prior to addingbasevertex
).
Compute
TODO
glDispatchCompute[?Indirect]()
Indirect
: read parameters from buffer bound toGL_DISPATCH_INDIRECT_BUFFER
.
Extensions
ARB
: Architecture Review Board (those are still considered extensions, not core)KHR
: Khronos (I think those are core)
Glossary
- DSA: Direct State Access. The ability to modify the GL object states without binding them. Introduced in OpenGL 4.5
- EGL: Mobile and Embedded Device Bindings
- FS: Fragment Shader
- GLX: X Window system Bindings
- MRT: Multiple Render Targets
- Render Target: Although not defined by the GL spec, render target is the collective term for either a render buffer or a texture being used as the target for rendering (see: https://stackoverflow.com/a/27186611/1027706)
- TCS: Tessellation Control Shader
- TES: Tessellation Evaluation Shader
- VAB: Vertex Attribute Binding (ARB_vertex_attrib_binding), the ability to change the mapping between vertex attribute and vertex buffer binding, and to specify vertex attribute format separately.
- Varyings: older name for the shader input and output variables between the vertex and fragment stages.
- Vertex Attribute: user-defined inputs for vertex shaders. They are passed via Vertex Arrays from data stored in Buffer Objects.
- VAO: Vertex Array Object is a list of the buffer objects used by the vertex stage, and how they map to Vertex Attributes in the Vertex Shader. Warning: “Vertex arrays”?? or “Vertex attribute arrays” seem to refer to the buffer storage for the vertex data, not the VAO!
- VBUM: Vertex Buffer Unified Memory (NV_vertex_buffer_unified_memory), a generalization of bindless texture to buffers.
- VS: Vertex Shader
- WGL: Microsoft Windows Bindings