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
| Dependency | Minimum version | Notes |
|---|---|---|
| C++ compiler | C++20 | GCC 10+, Clang 12+, MSVC 19.29+, Emscripten 3.1+ |
| CMake | 3.23 | Required for target_sources FILE_SET |
| OpenGL ES headers | GLES 3.2 | GLES3/gl32.h — provided by libgles2-mesa-dev on Debian/Ubuntu |
| GL context provider | any | SDL2, 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:
| Library | Loader function |
|---|---|
| SDL2 | SDL_GL_GetProcAddress |
| SDL3 | SDL_GL_GetProcAddress |
| GLFW | glfwGetProcAddress |
| EGL (raw) | eglGetProcAddress |
| Emscripten | emscripten_GetProcAddress |
| Custom | Any 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.