Getting Started

This guide walks you through adding meta-gl to your project, initializing it, and making your first type-safe OpenGL ES calls.

Requirements

DependencyMinimum versionNotes
C++ compilerC++20GCC 10+, Clang 12+, MSVC 19.29+, Emscripten 3.1+
CMake3.23Required for target_sources FILE_SET
OpenGL ES headersGLES 3.2GLES3/gl32.h — provided by libgles2-mesa-dev on Debian/Ubuntu
GL context provideranySDL2, GLFW, EGL, Emscripten — meta-gl does not create windows

CMake Integration (recommended)

Add meta-gl as a subdirectory in your project:

# In your top-level CMakeLists.txt:
cmake_minimum_required(VERSION 3.23)
project(my-app)

add_subdirectory(path/to/meta-gl meta-gl)

add_executable(my-app main.cpp)
target_link_libraries(my-app PRIVATE meta-gl::meta-gl)
target_compile_features(my-app PRIVATE cxx_std_20)

The meta-gl::meta-gl target propagates the include path automatically. You only need the one target_link_libraries call.

Standalone Build (for development)

# Configure
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

# Build
cmake --build build -j$(nproc)

# The static library is at:
# build/libmeta-gl.a  (Linux) or build/meta-gl.lib (Windows)

Initialization

Before calling any metagl::gl* function you must initialize the function pointer table. Call metagl::Initialize() exactly once after your GL context has been created. On context loss/restore, call it again.

#include <metagl/metagl.hpp>

// After SDL_GL_CreateContext / glfwMakeContextCurrent / eglMakeCurrent:
bool ok = metagl::Initialize(SDL_GL_GetProcAddress);
if (!ok) {
    std::cerr << "meta-gl: failed to load core GL functions\n";
    return 1;
}

// Optionally verify individual functions:
if (!metagl::IsFunctionAvailable("glDrawElementsBaseVertex")) {
    // ES 3.2 feature not present — disable that code path
}

Thread safety: Initialize() is not thread-safe. Call it from the thread that owns the GL context, before spawning render threads.

Compatible Loader Functions

Any function with the signature void* (const char* name) works as a loader. Common choices:

LibraryLoader function
SDL2SDL_GL_GetProcAddress
SDL3SDL_GL_GetProcAddress
GLFWglfwGetProcAddress
EGL (raw)eglGetProcAddress
Emscriptenemscripten_GetProcAddress
CustomAny void*(const char*) callable

First Triangle (complete example)

#include <metagl/metagl.hpp>
#include <SDL2/SDL.h>
#include <array>
#include <cstdio>

static const char* vert_src = R"(#version 300 es
layout(location=0) in vec2 aPos;
void main() { gl_Position = vec4(aPos, 0.0, 1.0); })";

static const char* frag_src = R"(#version 300 es
precision mediump float;
out vec4 fragColor;
void main() { fragColor = vec4(0.2, 0.7, 1.0, 1.0); })";

int main() {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
    SDL_Window* win = SDL_CreateWindow("meta-gl demo",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600,
        SDL_WINDOW_OPENGL);
    SDL_GLContext ctx = SDL_GL_CreateContext(win);

    // Initialize meta-gl
    metagl::Initialize(SDL_GL_GetProcAddress);

    // Build shader program
    GLuint vs = metagl::glCreateShader(metagl::ShaderType::Vertex);
    metagl::glShaderSource(vs, 1, &vert_src, nullptr);
    metagl::glCompileShader(vs);

    GLuint fs = metagl::glCreateShader(metagl::ShaderType::Fragment);
    metagl::glShaderSource(fs, 1, &frag_src, nullptr);
    metagl::glCompileShader(fs);

    GLuint prog = metagl::glCreateProgram();
    metagl::glAttachShader(prog, vs);
    metagl::glAttachShader(prog, fs);
    metagl::glLinkProgram(prog);
    metagl::glUseProgram(prog);

    // Upload triangle vertices
    std::array<float, 6> verts = { -0.5f, -0.5f,  0.5f, -0.5f,  0.0f, 0.5f };
    GLuint vao, vbo;
    metagl::glGenVertexArrays(1, &vao);
    metagl::glBindVertexArray(vao);
    metagl::glGenBuffers(1, &vbo);
    metagl::glBindBuffer(metagl::BufferTarget::Array, vbo);
    metagl::glBufferData(metagl::BufferTarget::Array,
        (GLsizeiptr)(verts.size() * sizeof(float)), verts.data(),
        metagl::BufferUsage::StaticDraw);
    metagl::glEnableVertexAttribArray(0);
    metagl::glVertexAttribPointer(0, 2, metagl::DataType::Float,
        GL_FALSE, 0, nullptr);

    // Render loop
    bool running = true;
    while (running) {
        SDL_Event e;
        while (SDL_PollEvent(&e))
            if (e.type == SDL_QUIT) running = false;

        metagl::glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        metagl::glClear(metagl::ClearBufferBit::Color);
        metagl::glDrawArrays(metagl::PrimitiveType::Triangles, 0, 3);
        SDL_GL_SwapWindow(win);
    }
    return 0;
}

Handling Context Loss

On Android and WebGL contexts can be lost at any time. meta-gl provides a generation counter to detect stale handles:

// Platform callback (e.g. Emscripten HTMLELEMENT_EVENT_WEBGLCONTEXTLOST):
metagl::MarkContextLost();

// In your render loop guard:
if (metagl::IsContextLost()) {
    return; // skip GL calls
}

// On restore:
metagl::Initialize(getProcAddress); // reloads function pointers, bumps generation
uint64_t gen = metagl::GetContextGeneration();
// Recreate all textures, buffers, programs ...

Emscripten / WebGL

# Configure for Emscripten:
emcmake cmake -S . -B build-web \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_EXE_LINKER_FLAGS="-s USE_WEBGL2=1 -s MIN_WEBGL_VERSION=2"

emmake cmake --build build-web

When compiled with Emscripten, metagl::GetCapabilities().webgl2 returns true and the context info reflects the WebGL API kind. Use metagl/Emscripten.hpp for WebGL-specific canvas and event helpers.