OpenGL Cheatsheet

Drawing

Use this OpenGL reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Draw Call Fundamentals

Every draw call reads vertex data from the currently bound VAO and submits primitives to the pipeline.

glBindVertexArray(vao);
glUseProgram(prog);

// Non-indexed
glDrawArrays(GL_TRIANGLES, 0, 6);  // first=0, count=6 vertices

// Indexed
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)0);
// last arg = byte offset into the EBO (not a pointer when EBO is bound)

Primitive Modes

ModeDescriptionMin verts to draw
GL_POINTSOne point per vertex1
GL_LINESIndependent line segments (2 verts each)2
GL_LINE_STRIPConnected line segments2
GL_LINE_LOOPStrip + closing segment2
GL_TRIANGLESIndependent triangles (3 verts each)3
GL_TRIANGLE_STRIPShared-edge triangles3
GL_TRIANGLE_FANFan from first vertex3
GL_LINES_ADJACENCYLines with 2 adjacency verts each4
GL_LINE_STRIP_ADJACENCYStrip with adjacency4
GL_TRIANGLES_ADJACENCYTriangles with edge neighbors6
GL_TRIANGLE_STRIP_ADJACENCYStrip with adjacency6
GL_PATCHESPatch for tessellation shadersGL_PATCH_VERTICES
// Set patch size for tessellation
glPatchParameteri(GL_PATCH_VERTICES, 3);  // triangles (default)

All Draw Functions

Non-indexed

glDrawArrays(mode, first, count);

// Instanced — gl_InstanceID in shader
glDrawArraysInstanced(mode, first, count, instanceCount);

// Instanced with base instance (gl_BaseInstance)
glDrawArraysInstancedBaseInstance(mode, first, count, instanceCount, baseInstance);

// Multi-draw — separate range per sub-call
const GLint   firsts[]  = { 0, 6 };
const GLsizei counts[]  = { 6, 3 };
glMultiDrawArrays(mode, firsts, counts, 2);

// Indirect (command from GPU buffer)
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuf);
glDrawArraysIndirect(mode, (void*)offset);

// Multi indirect (GL 4.3+)
glMultiDrawArraysIndirect(mode, (void*)offset, drawCount, stride);

Indexed

glDrawElements(mode, count, indexType, offsetPtr);

glDrawElementsBaseVertex(mode, count, indexType, offsetPtr, baseVertex);
// baseVertex added to each index before fetching (allows shared VBO regions)

glDrawElementsInstanced(mode, count, indexType, offsetPtr, instanceCount);

glDrawElementsInstancedBaseVertex(mode, count, indexType, offsetPtr,
                                   instanceCount, baseVertex);

glDrawElementsInstancedBaseVertexBaseInstance(mode, count, indexType, offsetPtr,
                                              instanceCount, baseVertex, baseInstance);

glDrawRangeElements(mode, start, end, count, indexType, offsetPtr);
// Hint: indices are in [start, end] — may improve performance

glMultiDrawElements(mode, counts, indexType, offsets, drawCount);

glMultiDrawElementsBaseVertex(mode, counts, indexType, offsets, drawCount, baseVertices);

glDrawElementsIndirect(mode, indexType, (void*)offset);

glMultiDrawElementsIndirect(mode, indexType, (void*)offset, drawCount, stride);

Indirect draw command structs

// For glDrawArraysIndirect
struct DrawArraysIndirectCommand {
    GLuint count;
    GLuint instanceCount;
    GLuint first;
    GLuint baseInstance;
};

// For glDrawElementsIndirect
struct DrawElementsIndirectCommand {
    GLuint count;
    GLuint instanceCount;
    GLuint firstIndex;   // index into EBO (in element units, not bytes)
    GLint  baseVertex;
    GLuint baseInstance;
};

Instanced Rendering

Draw many copies of the same geometry in one call. Use gl_InstanceID or per-instance vertex attributes.

// Per-instance vertex attribute (divisor = 1 means advance once per instance)
glVertexAttribDivisor(attribIndex, 1);  // 0 = per-vertex (default), N = per N instances

// DSA equivalent
glVertexArrayBindingDivisor(vao, bindingIndex, 1);
// Vertex shader
layout(location = 2) in vec3 aOffset;  // divisor=1 attribute

void main() {
    gl_Position = uVP * vec4(aPos + aOffset, 1.0);
}

Transform Feedback

Capture transformed vertices back to a buffer before rasterization.

// Setup
GLuint tfo;
glGenTransformFeedbacks(1, &tfo);
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfo);

const char* varyings[] = { "outPos", "outVel" };
glTransformFeedbackVaryings(prog, 2, varyings, GL_INTERLEAVED_ATTRIBS);
// GL_SEPARATE_ATTRIBS: one binding point per varying
glLinkProgram(prog);  // must relink after setting varyings

glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, captureBuffer);

// Draw
glEnable(GL_RASTERIZER_DISCARD);  // optional: skip fragment stage
glBeginTransformFeedback(GL_TRIANGLES);
    glDrawArrays(GL_TRIANGLES, 0, vertCount);
glEndTransformFeedback();
glDisable(GL_RASTERIZER_DISCARD);

Conditional Rendering

Draw only if a query recorded at least one sample.

GLuint query;
glGenQueries(1, &query);
glBeginQuery(GL_SAMPLES_PASSED, query);
    // bounding box draw
glEndQuery(GL_SAMPLES_PASSED);

glBeginConditionalRender(query, GL_QUERY_WAIT);
    // expensive draw — skipped if query result == 0
glEndConditionalRender();

Conditional render modes

ModeBehavior
GL_QUERY_WAITStall until result available
GL_QUERY_NO_WAITSkip if result unavailable (may draw anyway)
GL_QUERY_BY_REGION_WAITStall; only renders in screen region
GL_QUERY_BY_REGION_NO_WAITNo stall; only in region

Occlusion Queries

GLuint query;
glGenQueries(1, &query);

glBeginQuery(GL_SAMPLES_PASSED, query);  // or GL_ANY_SAMPLES_PASSED
    // draw occluder
glEndQuery(GL_SAMPLES_PASSED);

// Non-blocking check
GLint available = 0;
glGetQueryObjectiv(query, GL_QUERY_RESULT_AVAILABLE, &available);
if (available) {
    GLuint result;
    glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result);
}

Query types

TypeWhat it counts
GL_SAMPLES_PASSEDFragments that pass depth/stencil
GL_ANY_SAMPLES_PASSEDReturns 1 or 0 (boolean)
GL_ANY_SAMPLES_PASSED_CONSERVATIVEConservative (faster) boolean
GL_PRIMITIVES_GENERATEDPrimitives emitted by geometry stage
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTENPrimitives written to XFB buffer
GL_TIME_ELAPSEDGPU time in nanoseconds
GL_TIMESTAMPAbsolute GPU timestamp

Timer Queries

GLuint queries[2];
glGenQueries(2, queries);

glQueryCounter(queries[0], GL_TIMESTAMP);
    // draw calls
glQueryCounter(queries[1], GL_TIMESTAMP);

GLuint64 t0, t1;
glGetQueryObjectui64v(queries[0], GL_QUERY_RESULT, &t0);
glGetQueryObjectui64v(queries[1], GL_QUERY_RESULT, &t1);
double ms = (t1 - t0) / 1e6;

Scissor Test

glEnable(GL_SCISSOR_TEST);
glScissor(x, y, width, height);  // pixels, lower-left origin
// Only fragments within this rect are written
glDisable(GL_SCISSOR_TEST);

Multi-viewport / Layered Rendering (GL 4.1+)

// Set multiple viewports at once
float vps[8] = { 0,0,400,600,  400,0,400,600 };
glViewportArrayv(0, 2, vps);

float depths[4] = { 0,1,  0,1 };
glDepthRangeArrayv(0, 2, depths);

// Geometry shader selects viewport/layer
// gl_ViewportIndex = 0 or 1;
// gl_Layer = cubemap face index;

Sync Objects / Memory Barriers

// Insert a fence
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

// CPU waits for GPU
GLenum result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 5000000000ULL);
// result: GL_ALREADY_SIGNALED, GL_CONDITION_SATISFIED, GL_TIMEOUT_EXPIRED, GL_WAIT_FAILED

// GPU waits for fence (no CPU stall)
glWaitSync(fence, 0, GL_TIMEOUT_IGNORED);

glDeleteSync(fence);

// Memory barrier — order shader writes before subsequent reads
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT
              | GL_TEXTURE_FETCH_BARRIER_BIT);

glMemoryBarrier bits

BitProtects
GL_VERTEX_ATTRIB_ARRAY_BARRIER_BITVBO reads after shader writes
GL_ELEMENT_ARRAY_BARRIER_BITEBO reads
GL_UNIFORM_BARRIER_BITUniform reads after SSBO writes
GL_TEXTURE_FETCH_BARRIER_BITTexture sampling after image writes
GL_SHADER_IMAGE_ACCESS_BARRIER_BITImage load/store ordering
GL_COMMAND_BARRIER_BITIndirect draw commands
GL_PIXEL_BUFFER_BARRIER_BITPBO reads/writes
GL_TEXTURE_UPDATE_BARRIER_BITglTexImage calls
GL_BUFFER_UPDATE_BARRIER_BITglBufferData / map
GL_FRAMEBUFFER_BARRIER_BITFBO reads after rendering
GL_TRANSFORM_FEEDBACK_BARRIER_BITXFB buffer reads
GL_ATOMIC_COUNTER_BARRIER_BITAtomic counter reads
GL_SHADER_STORAGE_BARRIER_BITSSBO reads/writes
GL_ALL_BARRIER_BITSAll of the above