GPU Programming · Reference

Glossary

The vocabulary every lesson in this workspace uses, and uses consistently.

Terms are added here only once a lesson has actually used them. Where GPU vendors disagree on a word, the Khronos/GLSL term is the one used throughout, with the CUDA equivalent noted — most performance writing on the internet is in CUDA dialect.

01The execution model

TermMeaning
Invocation
CUDA: thread
One run of main() in a compute shader. Thousands run at once. Each one knows only its ids and whatever it reads from memory.
Workgroup
CUDA: block
A fixed-size block of invocations, declared in the shader with layout(local_size_x = N) in;. The only scope in which invocations can share memory or synchronise with each other.
Local size Invocations per workgroup, up to three dimensions. Chosen by the shader author. Implementations must allow at least 128 invocations per group; 64 is the safe default for 1D work.
Dispatch The CPU-side "go". You give it a number of workgroups, never a number of invocations. Total invocations = groups × local size.
Global invocation id gl_GlobalInvocationID = gl_WorkGroupID * gl_WorkGroupSize + gl_LocalInvocationID. Your unique index over the whole dispatch, and therefore usually your array index.
Bounds guard if (id >= count) return; at the top of main(). Necessary because dispatches round up to whole workgroups, so surplus invocations are the norm.
Warp (NVIDIA) / wavefront (AMD) A hardware bundle of invocations executing in lockstep: 32 wide on NVIDIA, 64 on AMD. Invisible in GLSL, but it is why local sizes should be multiples of 64.
SIMT Single Instruction, Multiple Threads. Like SIMD, except individual lanes can be masked off — which is how a GPU runs an if at all.
Divergence When invocations in the same warp take different branches. The warp runs both sides, masking lanes, so the cost is the sum, not the max. The reason branchy shader code is slow.
Occupancy How many warps an execution unit has in flight relative to its maximum. High occupancy is how the GPU hides memory latency: while one warp waits on memory, another computes.

02The Godot / Vulkan plumbing

TermMeaning
RenderingDevice Godot's thin wrapper over Vulkan. Available only on the Forward+ and Mobile renderers. A local rendering device (create_local_rendering_device()) is your own private one, isolated from the one drawing the game.
RID An opaque handle to a GPU-side resource (buffer, shader, pipeline, uniform set). Not reference-counted: you free it yourself with rd.free_rid().
SPIR-V The compiled bytecode Vulkan actually consumes. Godot compiles your .glsl file to it at import time; RDShaderFile.get_spirv() hands it over.
Storage buffer (SSBO) A block of GPU memory the shader can read and write, and whose trailing array can be sized at runtime. The workhorse for simulation state.
std430 The memory layout rule set for storage buffers: how fields are packed and padded. Mismatched padding between GDScript and GLSL is the classic cause of "the numbers are garbage".
Set and binding The two-level address of a resource: layout(set = 0, binding = 0) in GLSL must match the set index and RDUniform.binding on the Godot side, or the shader reads nothing.
Uniform set A bundle of resources bound together at one set index. Vulkan calls it a descriptor set.
Compute pipeline The compiled shader plus fixed state, in the form the GPU can execute. Create once, reuse every frame.
Compute list The recorded sequence of bind/dispatch commands, between compute_list_begin() and compute_list_end(). Recording is not executing.
Submit / sync submit() hands recorded work to the GPU; sync() blocks the CPU until it is done. Calling them back to back throws away the parallelism.
Readback Copying GPU memory back to the CPU (buffer_get_data()). Expensive and synchronising. Good GPU design avoids it in the frame loop.
Barrier An ordering instruction: "everything before this must finish being written before anything after it reads". Needed whenever one dispatch consumes another's output.
Push constant A small block of parameters (delta time, counts, seeds) sent with the dispatch itself, with no buffer to create or update. The cheap way to pass per-frame values.
GLSL vs. Godot shading language Two different languages. Compute shaders are real GLSL 450 in a .glsl file starting with #[compute]. .gdshader files are Godot's own, GLSL-like but not GLSL, and cannot express compute.

03GLSL as a language

TermMeaning
Vector type vec2/vec3/vec4 (floats), and the same shapes for int (ivec), uint (uvec), bool (bvec). A position or velocity is one value of this type, not several scalars kept in sync by hand.
Componentwise How vector operators work by default: a + b on two vec2s adds each matching pair of components independently. No loop, no per-axis code.
Swizzle Naming a vector's components, in any order, with repeats allowed, via .xyzw/.rgba/.stpq — three spellings for the same slots. v.zyx reverses a vec3; v.xxxx is legal. Writable too: pos.xy = vec2(0) updates exactly those components.
No pointers The GLSL specification states it outright: "There are no pointer types." Nothing is passed by reference except a whole bound buffer block; there is no address-of, no linked structure built from them.
No printf GLSL has no console output. The only way to inspect what one invocation computed is to write it into a buffer and read that buffer back on the CPU — the readback step from lesson 1 doubles as the debugger.
Implicit int→float conversion Mixing an int/uint and a float in one expression compiles: GLSL picks the floating-point result and converts the integer operand automatically.

04GPU-resident body data

TermMeaning
GPU-resident State whose authoritative copy remains in GPU memory across simulation and rendering. The CPU may send additions or parameters and receive summaries, but does not copy every body's live state back each frame.
Schema The byte contract shared by CPU and shader: field order, machine type, byte offset, and record stride. Names are documentation only.
Alignment The byte boundary on which a value is allowed to begin. Under std430, a scalar aligns to 4 bytes, a vec2 to 8, and a vec3/vec4 to 16.
Padding Bytes inserted between or after meaningful fields to satisfy alignment and stride. They carry no gameplay meaning, but they are part of the schema.
Stride The byte distance from the start of array element i to element i+1. Record address = buffer start + index × stride.
Flag One behavior bit packed into an integer. Test with flags & BIT, add with flags |= BIT, and clear with flags &= ~BIT.
Array of structures (AoS) Each body record stores all of that body's fields together: [pos, vel, health][pos, vel, health]…. Straightforward to serialize and ideal for initialization records.
Structure of arrays (SoA) Each field or related field group has its own stream: all physics records, all simulation records, all temporary records. A pass can read only the streams it needs.