Examples

Practical code examples showing meta-gl's type-safe API side by side with the equivalent raw OpenGL calls.

Initialization

#include <metagl/metagl.hpp>

// Works with SDL2, GLFW, EGL, or any GetProcAddress-compatible function:
bool ok = metagl::Initialize(SDL_GL_GetProcAddress);
if (!ok) {
    fprintf(stderr, "Failed to load core GL functions\n");
    return EXIT_FAILURE;
}

const auto& caps = metagl::GetCapabilities();
printf("Renderer: %s\n", caps.renderer.c_str());
printf("ES3.1: %d  ES3.2: %d\n", caps.gles31, caps.gles32);

Clear Screen

Raw OpenGL

glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT |
        GL_DEPTH_BUFFER_BIT);

meta-gl

metagl::glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
metagl::glClear(
    metagl::ClearBufferBit::Color |
    metagl::ClearBufferBit::Depth);

Triangle (VAO + VBO + shader)

#include <metagl/metagl.hpp>
#include <array>

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

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

static auto compileShader(metagl::ShaderType type, const char* src) {
    GLuint id = metagl::glCreateShader(type);
    metagl::glShaderSource(id, 1, &src, nullptr);
    metagl::glCompileShader(id);
    GLint ok = 0;
    metagl::glGetShaderiv(id, metagl::ShaderParameter::CompileStatus, &ok);
    if (!ok) {
        char log[512];
        metagl::glGetShaderInfoLog(id, sizeof(log), nullptr, log);
        fprintf(stderr, "Shader error: %s\n", log);
    }
    return id;
}

GLuint buildProgram() {
    GLuint vs   = compileShader(metagl::ShaderType::Vertex,   vert);
    GLuint fs   = compileShader(metagl::ShaderType::Fragment, frag);
    GLuint prog = metagl::glCreateProgram();
    metagl::glAttachShader(prog, vs);
    metagl::glAttachShader(prog, fs);
    metagl::glLinkProgram(prog);
    metagl::glDeleteShader(vs);
    metagl::glDeleteShader(fs);
    return prog;
}

GLuint vao, vbo;

void setupTriangle() {
    std::array<float, 6> verts = { -0.5f, -0.5f,  0.5f, -0.5f,  0.0f, 0.5f };

    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, 2 * sizeof(float), nullptr);
}

void drawTriangle() {
    metagl::glBindVertexArray(vao);
    metagl::glDrawArrays(metagl::PrimitiveType::Triangles, 0, 3);
}

Texture Upload

void uploadRGBATexture(const uint8_t* pixels, int w, int h) {
    GLuint tex;
    metagl::glGenTextures(1, &tex);
    metagl::glBindTexture(metagl::TextureTarget::Texture2D, tex);

    // Set wrapping and filtering:
    metagl::glTexParameteri(metagl::TextureTarget::Texture2D,
        metagl::TextureParameter::WrapS,
        (GLint)metagl::TextureWrapMode::ClampToEdge);
    metagl::glTexParameteri(metagl::TextureTarget::Texture2D,
        metagl::TextureParameter::WrapT,
        (GLint)metagl::TextureWrapMode::ClampToEdge);
    metagl::glTexParameteri(metagl::TextureTarget::Texture2D,
        metagl::TextureParameter::MinFilter,
        (GLint)metagl::TextureMinFilter::LinearMipmapLinear);
    metagl::glTexParameteri(metagl::TextureTarget::Texture2D,
        metagl::TextureParameter::MagFilter,
        (GLint)metagl::TextureMagFilter::Linear);

    // Upload pixel data:
    metagl::glPixelStorei(metagl::PixelStoreParam::UnpackAlignment, 1);
    metagl::glTexImage2D(metagl::TextureTarget::Texture2D, 0,
        metagl::InternalFormat::Rgba8,
        w, h, 0,
        metagl::PixelFormat::Rgba,
        metagl::PixelType::UnsignedByte,
        pixels);

    metagl::glGenerateMipmap(metagl::TextureTarget::Texture2D);
}

Uniform Buffer Object (ES 3.0+)

// Example: per-frame camera matrices in a UBO
struct FrameData {
    float view[16];
    float proj[16];
};

GLuint ubo;
metagl::glGenBuffers(1, &ubo);
metagl::glBindBuffer(metagl::BufferTarget::Uniform, ubo);
metagl::glBufferData(metagl::BufferTarget::Uniform,
    sizeof(FrameData), nullptr, metagl::BufferUsage::DynamicDraw);

// Bind to binding point 0:
metagl::glBindBufferBase(metagl::BufferTarget::Uniform, 0, ubo);

// Per-frame update:
FrameData frame = { /* ... */ };
metagl::glBindBuffer(metagl::BufferTarget::Uniform, ubo);
metagl::glBufferSubData(metagl::BufferTarget::Uniform,
    0, sizeof(frame), &frame);

// In shader:  layout(binding=0, std140) uniform FrameData { mat4 view; mat4 proj; };
// In program: link binding index to UBO slot
GLuint blockIdx = metagl::glGetUniformBlockIndex(prog, "FrameData");
metagl::glUniformBlockBinding(prog, blockIdx, 0);

Offscreen Framebuffer (Render to Texture)

GLuint fbo, colorTex, depthRbo;

void createFBO(int w, int h) {
    // Color attachment — immutable texture (ES 3.0)
    metagl::glGenTextures(1, &colorTex);
    metagl::glBindTexture(metagl::TextureTarget::Texture2D, colorTex);
    metagl::glTexStorage2D(metagl::TextureTarget::Texture2D,
        1, metagl::InternalFormat::Rgba8, w, h);

    // Depth attachment — renderbuffer
    metagl::glGenRenderbuffers(1, &depthRbo);
    metagl::glBindRenderbuffer(metagl::RenderbufferTarget::Renderbuffer, depthRbo);
    metagl::glRenderbufferStorage(metagl::RenderbufferTarget::Renderbuffer,
        metagl::InternalFormat::DepthComponent24, w, h);

    // Assemble FBO
    metagl::glGenFramebuffers(1, &fbo);
    metagl::glBindFramebuffer(metagl::FramebufferTarget::Framebuffer, fbo);
    metagl::glFramebufferTexture2D(
        metagl::FramebufferTarget::Framebuffer,
        metagl::FramebufferAttachment::Color0,
        metagl::TextureTarget::Texture2D, colorTex, 0);
    metagl::glFramebufferRenderbuffer(
        metagl::FramebufferTarget::Framebuffer,
        metagl::FramebufferAttachment::Depth,
        metagl::RenderbufferTarget::Renderbuffer, depthRbo);

    // Verify completeness
    auto status = metagl::glCheckFramebufferStatus(
        metagl::FramebufferTarget::Framebuffer);
    if (status != metagl::FramebufferStatus::Complete) {
        fprintf(stderr, "FBO incomplete\n");
    }
}

void renderToFBO() {
    metagl::glBindFramebuffer(metagl::FramebufferTarget::Framebuffer, fbo);
    // ... render scene ...
    metagl::glBindFramebuffer(metagl::FramebufferTarget::Framebuffer, 0);
}

Instanced Drawing (ES 3.0+)

// Per-instance data: translation offsets
std::array<float, 200> offsets; // 100 instances × vec2
// ... fill offsets ...

GLuint instanceVbo;
metagl::glGenBuffers(1, &instanceVbo);
metagl::glBindBuffer(metagl::BufferTarget::Array, instanceVbo);
metagl::glBufferData(metagl::BufferTarget::Array,
    sizeof(offsets), offsets.data(), metagl::BufferUsage::StaticDraw);

// Attribute index 1 = per-instance offset
metagl::glEnableVertexAttribArray(1);
metagl::glVertexAttribPointer(1, 2, metagl::DataType::Float,
    GL_FALSE, 2 * sizeof(float), nullptr);
metagl::glVertexAttribDivisor(1, 1); // advance once per instance

// Draw 100 instances of the base mesh:
metagl::glDrawArraysInstanced(metagl::PrimitiveType::Triangles, 0, 3, 100);

Compute Shader (ES 3.1+)

static const char* computeSrc = R"(#version 310 es
layout(local_size_x=64) in;
layout(std430, binding=0) buffer Data {
    float values[];
};
void main() {
    uint idx = gl_GlobalInvocationID.x;
    values[idx] *= 2.0;
})";

void runCompute(GLuint ssbo, GLuint elementCount) {
    GLuint cs = metagl::glCreateShader(metagl::ShaderType::Compute);
    metagl::glShaderSource(cs, 1, &computeSrc, nullptr);
    metagl::glCompileShader(cs);

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

    // Bind SSBO to binding 0:
    metagl::glBindBufferBase(metagl::BufferTarget::ShaderStorage, 0, ssbo);

    // Dispatch: 64 threads per group, ceil(elementCount/64) groups
    GLuint groups = (elementCount + 63) / 64;
    metagl::glDispatchCompute(groups, 1, 1);

    // Ensure writes are visible to subsequent reads:
    metagl::glMemoryBarrier(metagl::MemoryBarrierMask::ShaderStorage);
}

Alpha Blending Setup

// Standard "over" blending for transparent objects:
metagl::glEnable(metagl::Capability::Blend);
metagl::glBlendFunc(metagl::BlendFactor::SrcAlpha,
                    metagl::BlendFactor::OneMinusSrcAlpha);
metagl::glBlendEquation(metagl::BlendEquation::FuncAdd);

// Premultiplied alpha (for ASTC/HDR textures):
metagl::glBlendFunc(metagl::BlendFactor::One,
                    metagl::BlendFactor::OneMinusSrcAlpha);

Stencil Masking

// Pass 1: write stencil mask
metagl::glEnable(metagl::Capability::StencilTest);
metagl::glStencilFunc(metagl::CompareFunc::Always, 1, 0xFF);
metagl::glStencilOp(metagl::StencilOp::Keep,
                    metagl::StencilOp::Keep,
                    metagl::StencilOp::Replace);
metagl::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
drawMaskShape();

// Pass 2: render only where stencil == 1
metagl::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
metagl::glStencilFunc(metagl::CompareFunc::Equal, 1, 0xFF);
metagl::glStencilOp(metagl::StencilOp::Keep,
                    metagl::StencilOp::Keep,
                    metagl::StencilOp::Keep);
drawScene();

Error Handling

// Inline error check helper:
inline void checkGLError(const char* context) {
    auto err = metagl::glGetError();
    if (err != metagl::ErrorCode::NoError) {
        fprintf(stderr, "GL error at %s: 0x%X\n",
                context,
                static_cast<unsigned>(err));
    }
}

// Usage:
metagl::glLinkProgram(prog);
checkGLError("glLinkProgram");

// Possible error codes:
//  ErrorCode::NoError                    — no error
//  ErrorCode::InvalidEnum                — bad enum value
//  ErrorCode::InvalidValue               — bad parameter value
//  ErrorCode::InvalidOperation           — invalid state
//  ErrorCode::OutOfMemory                — allocation failed
//  ErrorCode::InvalidFramebufferOperation — FBO not complete