Skip to content

About

OpenGL 4.6 binding for the BEAM.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

OpenGL 4.6 binding for the BEAM

Erlangsters Repository Supported Erlang/OTP Versions Current Version License Build Status Documentation Link

Warning

This repository is still in development. Until release tags are cut, the master branch may be rewound.

This repository hosts the generated OpenGL 4.6 binding for Erlang and Elixir. Only the core profile functions selected for this target are exposed through the gl module. Loading custom OpenGL functions is not supported.

After an EGL context is current, calling OpenGL from Erlang looks like this:

{ok, Version} = gl:get_string(version),
io:format("OpenGL version: ~p~n", [Version]).

From Elixir, the same module is available as :gl:

{:ok, version} = :gl.get_string(:version)
IO.puts("OpenGL version: #{version}")

It works with the EGL binding, which provides OpenGL contexts and surfaces. The GLFW binding can be used when an application needs a window.

Bindings for other versions of OpenGL and OpenGL ES are available from the Erlangsters repository list.

Generated by the Erlangsters community and released under the MIT license.

Quick preview

All the pieces together, with EGL, OpenGL and optional GLFW for a window, look roughly like this:

glfw:init(),

Display = egl:get_display(default_display),
{ok, _} = egl:initialize(Display),

ConfigAttribs = [
    {surface_type, [window_bit]},
    {renderable_type, [opengl_bit]}
],
{ok, Configs} = egl:choose_config(Display, ConfigAttribs),
Config = hd(Configs),

ContextAttribs = [
    {context_major_version, 4},
    {context_minor_version, 6}
],
egl:bind_api(opengl_api),
{ok, Context} = egl:create_context(Display, Config, no_context, ContextAttribs),

{ok, Window} = glfw:create_window(640, 480, "Hello World!"),
WindowHandle = glfw:window_egl_handle(Window),
{ok, Surface} = egl:create_window_surface(Display, Config, WindowHandle, []),

ok = egl:make_current(Display, Surface, Surface, Context),
ok = gl:glad_load_gl(),

% Here goes your OpenGL initialization code.
Vertices = [
     0.0,  0.5, 0.0, 1.0, 0.0, 0.0,
    -0.5, -0.5, 0.0, 0.0, 1.0, 0.0,
     0.5, -0.5, 0.0, 0.0, 0.0, 1.0
],

{ok, [Buffer]} = gl:gen_buffers(1),
gl:bind_buffer(array_buffer, Buffer),
gl:buffer_data(
    array_buffer,
    length(Vertices) * 4,
    <<<<Vertex:32/float-little>> || Vertex <- Vertices>>,
    static_draw
),

loop_window(Display, Surface, Window).

Here is how the loop function can look:

loop_window(Display, Surface, Window) ->
    case glfw:window_should_close(Window) of
        true ->
            glfw:terminate();
        false ->
            % Here goes your OpenGL rendering code.
            gl:clear([color_buffer_bit]),

            egl:swap_buffers(Display, Surface),
            glfw:poll_events(),
            handle_events(Window),

            loop_window(Display, Surface, Window)
    end.

And here is a small event handler:

handle_events(Window) ->
    receive
        #glfw_window_size{size = {Width, Height}} ->
            gl:viewport(0, 0, Width, Height),
            handle_events(Window)
    after 0 ->
        ok
    end.

For more examples, consult the OpenGL samples repository, which contains mini-programs written in C, Erlang and Elixir.

Working with the binding

Before writing larger programs, it is useful to understand the thread-safety model and the API mapping rules.

Thread safety

OpenGL is a C API with thread-affine state: a context is current on one OS thread at a time. The BEAM schedules Erlang processes across OS threads, so the binding relies on EGL to preserve a process-oriented context model for OpenGL calls.

In practice, think of the BEAM process that owns the current EGL context as the place where the matching OpenGL calls should happen. This mirrors how OpenGL is usually structured in C while keeping the Erlang process model intact.

The target documentation explains this in more detail: Context and testing.

API mapping

The generated API follows deterministic mapping rules from the C OpenGL API to Erlang and Elixir. Function names become lower snake case, enums are atoms, and OpenGL output parameters become return values.

Most OpenGL code can be translated directly once those rules are familiar. If a specific function shape is unclear, use the local API mapping, the generated API reference, and the binding generator documentation.

Using the binding in your project

With Rebar3, add the following to your project's rebar.config:

{deps, [
    {egl, {git, "https://github.com/erlangsters/egl-1.5.git", {tag, "master"}}},
    {gl, {git, "https://github.com/erlangsters/opengl-4.6.git", {tag, "master"}}}
]}.

During early development, master is shown for convenience. Pin a release tag or commit in applications that need reproducible builds.

If you use Erlang.mk, add the Git dependencies to your Makefile:

BUILD_DEPS = egl gl
dep_egl = git https://github.com/erlangsters/egl-1.5 master
dep_gl = git https://github.com/erlangsters/opengl-4.6 master

Generating the binding yourself

This repository is generated and hosted for convenience. To reproduce the generated files, clone the OpenGL binding generator, compile it, and generate this target:

rebar3 escriptize
./_build/default/bin/opengl_gen gl 4.6

The generator writes gl.erl, gl.hrl, and gl.c into its working directory. Those files are then copied into this target repository and built as priv/beam-gl.so by CMake through Rebar3 hooks.

Do not hand-edit generated gl.erl, gl.hrl, or gl.c files in this target repository.

About

OpenGL 4.6 binding for the BEAM.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Contributors

Languages