GL
OpenGL state operations. Every method needs a current context (Window->GLCreateContext), and GL state is per-context, so these all act on whichever context is current on this thread. ## Shutting down, in this order 1. Cursor->SetRelativeMouseMode(false), if it was ever set -- while the window still exists. 2. **Every Shader, Mesh and Texture2D, while the context is still current.** Their Free methods issue glDeleteProgram/glDeleteBuffers/glDeleteTextures, and those need a current context; after the context is gone they are a no-op at best and a driver crash at worst. 3. Any Surface. Order-independent -- Texture2D->New copies the pixels, so a surface can in fact be freed as soon as the texture is built. 4. GLContext->Delete() 5. Window->Destroy() 6. Core->Quit() Getting step 2 wrong leaks GPU memory silently, and process exit reclaims it, so a program that frees nothing at all looks and behaves identically to a correct one. It only becomes a bug in something long-running that rebuilds meshes -- at which point it is an unbounded VRAM leak with no symptom pointing back here.
Operations
- BeginTransparency
- Clear
- ClearColor
- ClearColorBuffer
- Disable
- DrainErrors
- Enable
- EndTransparency
- GetError
- GetExtensions
- GetLastError
- GetMaxAnisotropy
- GetOptionalMissing
- HasExtension
- IsEnabled
- LoadFunctions
- ReadPixel
- ReportErrors
- Scissor
- SetBlend
- SetCullMode
- SetDepthFunc
- SetDepthWrite
- SetLineWidth
- SetPointSize
- SetPolygonOffset
- SetStencilFunc
- SetStencilMask
- SetStencilOp
- Viewport
BeginTransparency # function
Enable ordinary alpha transparency: blending on, factors set, depth writing off. The three calls that go together, because getting one of the three wrong is what makes transparency look broken rather than absent. Call EndTransparency when the transparent pass is done. CLEAR THE FRAME BEFORE CALLING THIS. Turning depth writing off also disables clearing the depth buffer -- see SetDepthWrite -- so a clear afterwards leaves stale depth behind.
function : BeginTransparency() ~ NilClear # function
Clear the given buffers.
function : Clear(mask:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| mask | Int | ClearBit values combined with 'or' |
ClearColor # function
Set the color the next Clear fills the color buffer with. Components are clamped to 0.0-1.0.
function : ClearColor(r:Float, g:Float, b:Float, a:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| r | Float | red |
| g | Float | green |
| b | Float | blue |
| a | Float | alpha |
ClearColorBuffer # function
Clear the color buffer only. Convenience for the common 2D case.
function : ClearColorBuffer() ~ NilDisable # function
Disable a capability.
function : Disable(capability:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| capability | Int | the capability to disable |
DrainErrors # function
Empty the GL error queue, returning everything that was in it.
function : DrainErrors() ~ Int[]Return
| Type | Description |
|---|---|
| Int | the queued error codes, empty when there were none |
Enable # function
Enable a capability, e.g. Capability->GL_DEPTH_TEST.
function : Enable(capability:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| capability | Int | the capability to enable |
GetError # function
Take one error off the GL error queue. GL queues errors and each call removes only one, so a single check can hide others -- use DrainErrors to empty the queue.
function : GetError() ~ IntReturn
| Type | Description |
|---|---|
| Int | an ErrorCode; GL_NO_ERROR when the queue is empty |
GetExtensions # function
Every extension this context reports. Needed because a non-null proc address does NOT mean a feature is usable. SDL falls back to GetProcAddress on opengl32.dll on Windows, and on macOS the lookup goes into OpenGL.framework regardless of what the current context actually supports -- so a pointer can come back for something the driver will refuse. The extension string is the authority. In a core profile this is the only way to ask: glGetString(GL_EXTENSIONS) returns nothing and raises GL_INVALID_ENUM there.
function : GetExtensions() ~ String[]Return
| Type | Description |
|---|---|
| String | the extension names, empty when there is no context |
GetLastError # function
Why the last call silently did nothing. GetError only reports what GL itself considers an error, and the most confusing failures in this library are not GL errors at all: the entry points were never loaded, a mat4 was the wrong length, a uniform name does not exist in the program. Each of those leaves the error queue clean and renders a black window, so DrainErrors finds nothing and this is the only thing that can tell you why. Reading clears it, so the answer belongs to the call you just made.
function : GetLastError() ~ StringReturn
| Type | Description |
|---|---|
| String | the reason, or an empty string when nothing has silently failed |
Example
shader->SetMatrix4("mpv", mvp); # typo
reason := GL->GetLastError();
if(reason->Size() > 0) {
reason->ErrorLine(); # 'no uniform named "mpv" ...'
};GetMaxAnisotropy # function
The largest anisotropy level this driver offers. 0 when GL_EXT_texture_filter_anisotropic is absent, in which case asking for anisotropy is silently ignored rather than an error. Typically 16 where it is supported. This bundle has had HasExtension since the beginning, with this very extension as its documentation example, and nothing to set once the answer came back yes. This is the setter half.
function : GetMaxAnisotropy() ~ FloatReturn
| Type | Description |
|---|---|
| Float | the maximum, or 0.0 if unsupported |
GetOptionalMissing # function
Optional entry points that could not be resolved. Separate from LoadFunctions, which reports the ones that are REQUIRED: a missing required function disables the whole layer, because nothing here can work without it. A missing optional one disables only itself. Keeping the two apart is what stops one absent extension from taking the renderer down with it.
function : GetOptionalMissing() ~ StringReturn
| Type | Description |
|---|---|
| String | a comma-separated list, empty when everything resolved |
HasExtension # function
Whether this context reports an extension.
function : HasExtension(name:String) ~ BoolParameters
| Name | Type | Description |
|---|---|---|
| name | String | the full name, e.g. "GL_EXT_texture_filter_anisotropic" |
Return
| Type | Description |
|---|---|
| Bool | true when it is present |
IsEnabled # function
Is a capability currently on? Asked rather than assumed by anything that turns a capability off and has to put it back -- Overlay->Begin/End do exactly this with culling, and a program may legitimately have had it off already.
function : IsEnabled(capability:Int) ~ BoolParameters
| Name | Type | Description |
|---|---|---|
| capability | Int | a Capability value |
Return
| Type | Description |
|---|---|
| Bool | true if enabled |
LoadFunctions # function
Resolve the OpenGL 2.0+ entry points. Call once, after the context is current and before creating any Shader, Mesh or Texture2D. Everything above GL 1.1 has to be looked up at runtime; that happens in native code and is cached, so this is a one-off cost rather than per call.
function : LoadFunctions() ~ StringReturn
| Type | Description |
|---|---|
| String | an empty string on success, or a comma-separated list of the functions that could not be resolved -- which is what a context older than 3.3 looks like from here |
ReadPixel # function
Read one pixel from the current framebuffer. The point of this is assertion: it lets a program (or a test) check that something was actually DRAWN, rather than only that nothing crashed. Read before swapping buffers, since the swap may discard the back buffer. GL's origin is bottom-left, so y counts up from the bottom of the drawable.
function : ReadPixel(x:Int, y:Int) ~ IntParameters
| Name | Type | Description |
|---|---|---|
| x | Int | pixel column |
| y | Int | pixel row, counted from the bottom |
Return
| Type | Description |
|---|---|
| Int | the pixel packed as 0xAARRGGBB |
ReportErrors # function
Drain the error queue and print whatever was in it. Exists because every program was writing the same four lines: drain, count, test the count, print. Errors are worth checking at least once per run -- they are queued, so one raised in frame 2 is still there at exit.
function : ReportErrors(during:String) ~ IntParameters
| Name | Type | Description |
|---|---|---|
| during | String | what was happening, included in the message |
Return
| Type | Description |
|---|---|
| Int | how many errors there were, so a caller can also act on it |
Scissor # function
Restrict drawing to a rectangle. Only has an effect while Capability->GL_SCISSOR_TEST is enabled. That constant has been in this bundle since the beginning with nothing to bind it to, so enabling it clipped to whatever rectangle the driver happened to start with -- usually the whole window, which made it look like it worked. Pixels, not points, and measured from the BOTTOM-left like the viewport -- not the top-left like Overlay. A split-screen viewport or a UI panel that must not be drawn over is what this is for.
function : Scissor(x:Int, y:Int, width:Int, height:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| x | Int | left edge in pixels from the left |
| y | Int | bottom edge in pixels from the bottom |
| width | Int | width in pixels |
| height | Int | height in pixels |
SetBlend # function
How a fragment's colour combines with what is already in the buffer. Enabling Capability->GL_BLEND is not enough on its own, and that is the trap: GL's default factors are ONE and ZERO, which means "replace", so blending appears to be on and nothing changes. This is the other half. The two worth knowing: * **Straight alpha** -- SRC_ALPHA, ONE_MINUS_SRC_ALPHA. Ordinary transparency: glass, foliage, a fade. Needs the transparent things drawn AFTER the opaque ones and back-to-front among themselves, because a fragment can only blend with what is already there. * **Additive** -- ONE, ONE. Light: sparks, glows, fire. Order-independent, since addition commutes, which is why particle systems reach for it. With straight alpha, also turn depth WRITING off for the transparent pass while leaving the test on -- see SetDepthWrite. A transparent surface that writes depth hides whatever transparent thing is behind it.
function : SetBlend(source:Int, destination:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| source | Int | how much of the incoming fragment to use |
| destination | Int | how much of what is already there to keep |
SetCullMode # function
Which side of a triangle GL_CULL_FACE discards. GL_BACK by default, which is what every mesh in this bundle is wound for.
function : SetCullMode(mode:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| mode | Int | a CullMode |
SetDepthFunc # function
Which depth comparison counts as passing. GL_LESS is the default and is what a normal scene wants. GL_LEQUAL is what lets a skybox draw at the far plane and a decal draw exactly on the surface it decorates -- both need to pass at a depth that is already written.
function : SetDepthFunc(func:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| func | Int | a CompareFunc |
SetDepthWrite # function
Whether drawing writes depth. The test is unaffected. Off for a transparent pass: those fragments should be hidden BY opaque geometry but should not hide each other, and the only way to have both is to test depth without writing it. **Clearing the depth buffer while this is off does nothing.** glClear of the depth buffer is subject to the same mask, so a frame that turns depth writing off and then clears keeps the previous frame's depth values -- and everything at the same distance as something drawn last time silently fails the test. Clear first, then turn writing off. This is not hypothetical: it is how the check for this in the regression suite first came out reading zero instead of a blend.
function : SetDepthWrite(enabled:Bool) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| enabled | Bool | false to stop writing depth |
SetLineWidth # function
How thick GL_LINES draws, in pixels. Treat anything above 1.0 as a REQUEST, not a setting. A core profile is allowed to refuse it outright with GL_INVALID_VALUE, and the two renderers this bundle is tested on disagree: Mesa refuses 3.0, desktop NVIDIA accepts it silently. A program that needs thick lines reliably has to draw them as quads. A refusal is REPORTED through GL->GetLastError and the GL error is consumed here rather than left standing. That second part matters more than it looks: GL errors persist until something reads them, so without it a single wide-line request at startup would surface as a failure in the next unrelated GetError call, anywhere in the program. This exact thing turned one CI leg red while the check that failed was three blocks away from the call that caused it.
function : SetLineWidth(width:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| width | Float | thickness in pixels; only 1.0 is guaranteed |
SetPointSize # function
How large a GL_POINTS vertex draws, in pixels. Without this every point is one pixel, which is most of the reason DrawMode->GL_POINTS was not worth reaching even once it drew points at all.
function : SetPointSize(size:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| size | Float | diameter in pixels |
SetPolygonOffset # function
Shift filled polygons in depth, without moving them on screen. The fix for two surfaces at the same depth flickering against each other -- a decal on a wall, a shadow on a floor, a highlight over a tile. Both are at the same z, the winner is decided by floating-point noise, and it changes as the camera moves, which is what makes it read as flickering rather than as a mistake. Enable Capability->GL_POLYGON_OFFSET_FILL first; this call on its own does nothing. GL->Enable(Capability->GL_POLYGON_OFFSET_FILL); GL->SetPolygonOffset(-1.0, -1.0); ... draw the decal ... GL->Disable(Capability->GL_POLYGON_OFFSET_FILL); NEGATIVE values pull towards the viewer, which is what a decal wants. Positive values push away, which is the other use: offsetting shadow casters to trade one artefact for another. Both terms matter and they are not interchangeable. `units` is a constant nudge in depth-buffer steps; `factor` scales with how steeply the polygon is sloped, which is the term that fixes a surface seen at a grazing angle where a constant offset is not enough. -1.0, -1.0 is the usual starting point. Before this existed the only way to lift a decal off a surface was to move it geometrically along the normal -- which works, and then breaks at distance where the offset is smaller than a depth step, and looks like a floating sticker up close.
function : SetPolygonOffset(factor:Float, units:Float) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| factor | Float | scaled by the polygon's depth slope |
| units | Float | constant offset in depth-buffer units |
SetStencilFunc # function
Which stencil values pass the test. Only has an effect while Capability->GL_STENCIL_TEST is enabled. The window has requested 8 stencil bits since it learned to; this is the half that was missing, and without it the buffer could be cleared and nothing more -- no portal, mirror, outline or decal mask. The usual two-pass shape: write a mask, then draw only where it is. GL->Enable(Capability->GL_STENCIL_TEST); GL->SetStencilFunc(CompareFunc->GL_ALWAYS, 1, 0xFF); GL->SetStencilOp(StencilOp->GL_KEEP, StencilOp->GL_KEEP, StencilOp->GL_REPLACE); ... draw the mask ... GL->SetStencilFunc(CompareFunc->GL_EQUAL, 1, 0xFF); GL->SetStencilOp(StencilOp->GL_KEEP, StencilOp->GL_KEEP, StencilOp->GL_KEEP); ... draw what the mask reveals ...
function : SetStencilFunc(func:Int, reference:Int, mask:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| func | Int | a CompareFunc |
| reference | Int | the value to compare against |
| mask | Int | ANDed with both values before comparing; 0xFF for all bits |
SetStencilMask # function
Which stencil bits are writable.
function : SetStencilMask(mask:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| mask | Int | 0xFF for all of them, 0x00 to make the buffer read-only |
SetStencilOp # function
What to write to the stencil buffer in each of the three outcomes.
function : SetStencilOp(on_fail:Int, on_depth_fail:Int, on_pass:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| on_fail | Int | when the stencil test fails |
| on_depth_fail | Int | when stencil passes but depth fails |
| on_pass | Int | when both pass -- usually GL_REPLACE for a mask |
Viewport # function
Set the drawable region, in pixels. Use Window->GLGetDrawableSize rather than the size passed to Window->New: on a high-DPI display the drawable is larger than the window, and using the window size renders to a corner.
function : Viewport(x:Int, y:Int, width:Int, height:Int) ~ NilParameters
| Name | Type | Description |
|---|---|---|
| x | Int | left |
| y | Int | bottom (GL's origin is bottom-left, not top-left) |
| width | Int | width in pixels |
| height | Int | height in pixels |