Skip to content
Dav1dde edited this page May 1, 2019 · 10 revisions

Contents

  1. Quickstart
    1. GLFW
    2. SDL
    3. Built-In Loader
    4. CMake
  2. Basic API
  3. Header Only
  4. Loader
  5. Debugging
  6. Aliasing
  7. Multi Context

Quick Start

Generate the bindings you need with the online generator, most commonly you just want to select an OpenGL version, add the OpenGL extensions you want and click generate.

If you're not using GLFW, SDL, Qt or a windowing library that provides some kind of GetProcAddress function, you also need to tick the loader option.

Add the generated files to your project and compile with them:

src
└── main.c
glad
├── src
│  └── gl.c
├── include
│  ├── glad
│  │  └── gl.h
│  └── KHR
│     └── khrplatform.h

# gcc src/main.c glad/src/gl.c -Iglad/include -lglfw -ldl

GLFW

Include glad before GLFW:

#include <glad/gl.h>
#include <GLFW/glfw3.h>

Initialize glad after creating a context:

GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "[glad] GL with GLFW", NULL, NULL);
glfwMakeContextCurrent(window);

int version = gladLoadGL(glfwGetProcAddress);
printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));

The full example: gl_glfw.c.

SDL

Include glad before SDL:

#include <glad/gl.h>
#include <SDL.h>
#include <SDL_opengl.h>

Initialize glad after creating a context:

SDL_GLContext context = SDL_GL_CreateContext(window);

int version = gladLoadGL((GLADloadfunc) SDL_GL_GetProcAddress);
printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));

The full example: gl_sdl2.c.

Built-In Loader

In the previous glfw and SDL examples the windowing library is used to load OpenGL through glfwGetProcAddress or SDL_GL_GetProcAddress.

Glad can be configured to include a loader (by ticking the loader option) and makes the loading independent from the windowing library (you should use the one from the windowing library if you use one!).

// Create OpenGL context first

// Load glad
int version = gladLoaderLoadGL();
printf("GL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));

// .. Render

// Unload glad before exiting
gladLoaderUnloadGL();

CMake

Including glad sources

You can simply download the glad sources from the online generator and compile with them:

glad
├── src
│  └── gl.c
├── include
│  ├── glad
│  │  └── gl.h
│  └── KHR
│     └── khrplatform.h

And in your CMakesLists.txt:

include_directories(${CMAKE_CURRENT_SOURCE_DIR}/glad/include)
file(GLOB BUTTERFLIES_SOURCES_C ${CMAKE_CURRENT_SOURCE_DIR} *.c glad/src/gl.c)

Generating during build process

Using glad with CMake requires a slight modification of your CMakeLists.txt, a Python interpreter and the glad sources (e.g. embedded as a git submodule).

cmake_minimum_required(VERSION 3.1)
project(my_project C CXX)

find_package(glfw3 REQUIRED)

# Path to glad directory
set(GLAD_SOURCES_DIR "${PROJECT_SOURCE_DIR}/external/glad/")
# Path to glad cmake files
add_subdirectory("${GLAD_SOURCES_DIR}/cmake" glad_cmake)

# Specify glad settings
glad_add_library(glad_gl_core_33 REPRODUCIBLE API gl:core=3.3)

add_executable(multiwin_mx
    multiwin_mx.cpp
    )

# Add glad to your project
target_link_libraries(multiwin_mx
    PUBLIC
        glad_gl_core_33
        glfw
    )

The full example: multiwin_mx.

Basic API

All public and glad specific symbols follow certain rules:

  1. All symbols contain the API (GL, GLES2, WGL, etc.), in the following this will be marked as {API}
  2. All symbols are equally named across all APIs (e.g. gladLoad{API})
  3. Functions are prefixed with glad
  4. Structs are prefixed with Glad
  5. Macros are prefixed with GLAD_
  6. Constants/Globals are prefixed with GLAD_
  7. Typedefs are prefixed with GLAD

Initializing Glad

Everything generated by glad needs to be initialized through one loader function. Glad exposes two with similiar APIs by default:

typedef void (*GLADapiproc)(void);
typedef GLADapiproc (*GLADloadfunc)(const char *name);
typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name);

int gladLoad{API}(GLADloadfunc load);
int gladLoad{API}UserPtr(GLADuserptrloadfunc load, void *userptr);

gladLoad{API}UserPtr allows you to pass a userpointer which will be forwarded to your loader function, this allows you to write a loader function which is independent of global state (useful for GUI frameworks like Qt).

In most cases you will be using gladLoad{API}, it integrates seamlessly with popular windowing frameworks like GLFW or SDL:

// GLFW + GL
int version = gladLoadGL(glfwGetProcAddress);

// SDL + GL
int version = gladLoadGL((GLADloadfunc) SDL_GL_GetProcAddress);

int major = GLAD_VERSION_MAJOR(version);
int minor = GLAD_VERSION_MINOR(version);

printf("Loaded OpenGL version %d.%d\n", major, minor);

The return value of gladLoad{API}UserPtr and gladLoad{API} is the actual loaded version of the API. To extract the major and minor version out of the return value you can use the GLAD_VERSION_MAJOR(version) and GLAD_VERSION_MINOR(version) macros.

NOTE: since some APIs require additional information in order to be initialized the loader signature may vary depending on the API (e.g. WGL and EGL have different signatures).

Runtime Checks

After glad was initialized a few variables will be populated. Glad creates a global boolean for every version and extension generated.

int GLAD_{API}_VERSION_3_0;
int GLAD_{API}_VERSION_3_1;
// ...

int GLAD_{API}_EXT_texture_buffer_object;
int GLAD_{API}_KHR_debug;
// ...

If a version/extension was successfully loaded the global will be set to true (1).

Compiletime Checks

Sometimes you want to write glad agnostic code, for this glad generates and defines a few macros dependent on the generation options used.

#define GLAD_{API}
#define GLAD_OPTION_{API}_LOADER
#define GLAD_OPTION_{API}_ALIAS
#define GLAD_OPTION_{API}_{OPTION} // {OPTION} is the enabled loader option

Examples

Initializing glad with glfwGetProcAddress and extracting the loaded major and minor versions:

    int version = gladLoadGL(glfwGetProcAddress);
    if (version == 0)
    {
        std::cout << "Failed to initialize OpenGL context" << std::endl;
        return -1;
    }

    // Successfully loaded OpenGL
    std::cout << "Loaded OpenGL " << GLAD_VERSION_MAJOR(version) << "." << GLAD_VERSION_MINOR(version) << std::endl;

More examples can be found in the glad repository

Header Only

The header only option combines source and header into a single header file.

The implementation will be guarded by GLAD_{API}_IMPLEMENTATION. In order to not end up with missing symbols you need to define this macro in ONE source file before including glad.

#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>

int main(void) {
    // ... setup context
    if (!gladLoadGL(glfwGetProcAddress)) {
        return -1;
    }
    // ...
}

Loader

The loader option adds an internal loader into the generated files. This is useful if you are using a windowing library that does not come with a loader or doing the windowing yourself.

The following functions will be added:

int gladLoaderLoad{API}(void);
void gladLoaderUnload{API}(void); // <- may not exist for certain APIs

If you are using the internal loader gladLoaderLoad{API} you don't have to call gladLoad{API}.

int main(void) {
    // ... setup context
    int version = gladLoaderLoadGL();
    if (!version) {
        printf("Unable to load OpenGL\n");
        return 1;
    }

    // ... render


    gladLoaderUnloadGL();

    return 0;
}

NOTE: Even after multiple calls to gladLoaderLoad{API} only one call to gladLoaderUnload{API} is required.

Debugging

The debugging option adds an additional layer of indirection allowing you to intercept and modify any call made. A non-debug glad build "forwards" your calls using a define to a function pointer prefixed with glad_, e.g. glClear() -> glad_glClear(). With the debug option an additional layer is inserted between glClear and glad_glClear: glClear() -> glad_debug_glClear() -> glad_glClear().

By default the additional layer (glad_debug_glClear) calls the "pre callback", then the actual function (glad_glClear) and afterwards the "post callback".

Callbacks can be set with the following functions:

typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...);
typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...);

void gladSet{API}PreCallback(GLADprecallback cb);
void gladSet{API}PostCallback(GLADpostcallback cb);

NOTE: the debug option cannot be used together with the multi context option.

Aliasing

Enables automatic alias resolution. A lot of functions and features are implemented in multiple extensions and version.

So you end up with a bunch of function which do exactly the same but are named differently and may be available or not based on the environment you are in.

Glad will automatically include all extensions which have an alias to a function you specified in the feature set and at runtime all functions will be initialized using their aliases.

An excerpt of the generated code:

    // executed after initial loading phase:
    if (glVertexAttrib2sv == NULL && glVertexAttrib2svARB != NULL) glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)glVertexAttrib2svARB;
    if (glVertexAttrib2sv == NULL && glVertexAttrib2svNV != NULL) glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)glVertexAttrib2svNV;

As you can see glVertexAttrib2sv will be useable if either glVertexAttrib2svARB or glVertexAttrib2svNV are available.

Multi Context

By default glad generates everything as a global variable, which works fine for most usecases. But when you have to support multiple contexts it becomes basically impossible to do so. If multi context is enabled all function pointers and variables are placed inside a struct.

Glad generates a struct called Glad{API}Context which can be passed to the appropriate loader function (loader functions are suffixed with Context).

The symbols within the struct have their prefix stripped, e.g. glClear turns into gl->Clear, this also applies to flags like GLAD_GL_EXT_framebuffer_multisample which turn into gl->EXT_framebuffer_multisample.

By default glad does not generate global aliases. This can be turned on using the "mx global" option. Next to the context struct a global instance of said struct will be generated, which can be populated through gladSet{API}Context(struct Glad{API}Context *context) and queried through struct Glad{API}Context* gladGet{API}Context().

    // ... setup GL context

    GladGLContext context;
    int version = gladLoadGLContext(&context, glfwGetProcAddress);
    // Alternatively use the internal loader
    // int version = gladLoaderLoadGLContext(&context);
    if (version == 0)
    {
        std::cout << "Failed to initialize OpenGL context" << std::endl;
        return -1;
    }

    gl->Viewport(0, 0, WIDTH, HEIGHT);

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        gl->ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        gl->Clear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
    }

NOTE: the mx option can not be used together with the debug option.