Psellos
Contemporary Development With Functional Programming

LablGLES: OpenGL ES from OCaml

August 16, 2011

Posted by Jeffrey

OpenGL ES is a reduced version of the OpenGL graphics standard, suitable for use on small devices like mobile phones. My OCaml interface is provided as a set of patches to LablGL, an OCaml OpenGL interface by Jacques Garrigue and others.

You can use LablGLES to build OpenGL applications in OCaml for the iPhone and other iOS devices using OCamlXARM, a cross compiler for iOS. Starting with LablGLES 1.1.9, you can also use LablGLES to build OpenGL applications for the iOS Simulator using OcamlXSim.

As of LablGLES 1.1.4, there is also support for compiling and linking under Android. (Android patches were contributed by Paul Snively, psnively@mac.com.)

The current version of LablGLES is 1.1.9, which is based on LablGL version 1.04. It supports OpenGL ES Version 1.1, including a few extensions necessary for iOS. If you want to use it to build iOS apps, you need an installation of Xcode, Apple’s development environment. See Apple’s developer website for details. The build shown on this page was tested with Xcode 4.0.2, but will work with any recent version of Xcode (version 3.2.5 or later, say).

The LablGLES library has been used in production on iOS. In fact the Psellos Cassino app currently available in the iTunes store is a LablGLES app.

Overview

The LablGL release contains three interfaces: to OpenGL, to Tcl/Tk, and to GLUT. Only OpenGL is readily applicable in an embedded environment; I do not build the other parts (and they are not affected by any of the patches).

To a fairly good approximation, OpenGL ES is a subset of OpenGL. Roughly speaking, then, the patches just remove the parts of LablGL that aren’t in OpenGL ES. In addition, I added support for framebuffer objects, an extension required for OpenGL ES applications on iOS devices. I also added support for PVRTC compressed textures, which again are very useful on iOS devices.

Note that a few of the command lines of the following discussion are too long to fit on a single line of a typical browser window. In a lot of cases there is no good place to split them into smaller lines, usually because of a long filename or URL. Take care that you enter them as a single line if you are typing them in by hand.

Get LablGL Sources and Patches

Choose a place to work (an empty directory would be good).

    $ cd <good place to work>

Download the sources for the LablGL 1.04 release:

    $ curl -O -s http://wwwfun.kurims.kyoto-u.ac.jp/soft/olabl/dist/lablgl-1.04.tar.gz
    $ ls -l lablgl-1.04.tar.gz
    -rw-r--r--  1 psellos  staff   410525 Aug 16 15:41 lablgl-1.04.tar.gz

Download LablGLES patches from Psellos:

    $ curl -O -s http://psellos.com/pub/lablgles/lablgles-1.1.9.diff
    $ ls -l lablgles-1.1.9.diff
    -rw-r--r--  1 psellos  staff   145726 Aug 16 15:42 lablgles-1.1.9.diff

To save typing, you can access these two links directly from your browser:

Unpack and Apply Patches

Unpack the LablGL sources.

    $ tar --strip-components=1 -xzf lablgl-1.04.tar.gz

Apply the LablGLES patch.

    $ patch -p0 -E < lablgles-1.1.9.diff
    patching file Makefile.config.iossim
    patching file ESVERSION
    patching file src/glMap.mli
    patching file src/gluQuadric.mli
    patching file src/gluMisc.mli
    patching file src/glu_tags.var
    patching file src/gluTess.ml
    patching file src/ml_glu.c
    patching file src/ml_glu.h
    patching file src/gluQuadric.ml
        . . .
    patching file Makefile.config.ex
    patching file Makefile.config.android
    patching file Makefile.config.mingw
    patching file Makefile.common
    patching file Makefile.config.ios
    patching file Makefile

The patch command lists 59 patched files. Because OpenGLES is a subset of OpenGL, around 20 of the LablGL source files are not applicable to LablGLES. The patch causes all their content to disappear, and the -E option asks for these empty files to be deleted.

Build the Library

Specific details for building LablGLES will depend a great deal on your host and target systems. Here I show how to build for iOS (iPhone and iPad) and for the iOS Simulator, using Apple’s Xcode and the two previously mentioned OCaml cross compilers, OCamlXARM and OCamlXSim.

If you’re using OCamlXARM or OCamlXSim, please make sure you have a recent version. The LablGLES compilation requires the camlp4 preprocessor, which didn’t work in earlier versions of the cross compilers. For OCamlXARM you want ocaml-3.10.2+xarm12 or later. For OCamlXSim you want ocaml-3.12.1+xsim1 or later. If you built the compilers yourself from source, you should have built OCamlXARM from patches with version 1.0.12 or later. You should have built OCamlXSim from the directions posted on July 15, 2011 or later.

The build of LablGL (and thus LablGLES) is controlled by a main Makefile that is the same for all environments. Details for a particular environment are defined in a file named Makefile.config, which is included by the main Makefile. The LablGL release includes several examples for different environments, named Makefile.config.ex, Makefile.config.osx, and so on. The LablGLES patches create three new files for different OpenGLES environments:

Makefile.config.ios  for iPhone and iPad
Makefile.config.iossim  for iOS Simulator
Makefile.config.android  for Android

The first step in building LablGLES, then, is to create a Makefile.config file that defines the details of your environment. Here I will show how to use use the Makefile.config.ios and Makefile.config.iossim that come with LablGLES.

You will generally need to modify the Makefile to fit your local development layout. If you are working with iOS or the iOS Simulator, you may need to specify the location of your cross compiler or adjust the iOS SDK version. The lines in Makefile.config.ios looks as follows:

    OCAMLBINDIR=/usr/local/ocamlxarm/bin
    SDK=/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk

The lines in Makefile.config.iossim looks as follows:

    OCAMLBINDIR=/usr/local/ocamlxsim/bin
    SDK=/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk

Make sure the lines specify the location of your OCaml cross compiler (and other utilities), and the base SDK you want to use. You probably want to set SDK to the most recent SDK version you have installed on your system.

Once you have your configuration file, copy it into place and compile the library. For iOS, this looks as follows:

    $ cp Makefile.config.ios Makefile.config
    $ make lib libopt
    cd src && make all LIBDIR="`ocamlc -where`"
    /usr/local/ocamlxarm/bin/ocamlc -pp /usr/local/ocamlxarm/bin/camlp4o var2def.ml -o var2def
    /usr/local/ocamlxarm/bin/ocamlc -pp /usr/local/ocamlxarm/bin/camlp4o var2switch.ml -o var2switch
    /usr/local/ocamlxarm/bin/ocamlrun ../src/var2def < gl_tags.var > gl_tags.h
    /usr/local/ocamlxarm/bin/ocamlrun ../src/var2switch -table GL_ < gl_tags.var > gl_tags.c
    /usr/local/ocamlxarm/bin/ocamlc -c -w s -ccopt "-c -O -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.2.sdk" ml_gl.c
        . . .
    /usr/local/ocamlxarm/bin/ocamlc -c -w s -I +labltk raw.mli
    /usr/local/ocamlxarm/bin/ocamlc -c -w s -I +labltk raw.ml
    /usr/local/ocamlxarm/bin/ocamlc -c -w s -I +labltk gl.mli
    /usr/local/ocamlxarm/bin/ocamlc -c -w s -I +labltk gl.ml
        . . .
    /usr/local/ocamlxarm/bin/ocamlopt -c -ccopt -isysroot -ccopt /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk -cclib -Wl,-syslibroot,/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk -I +labltk glFramebuffer.ml
    /usr/local/ocamlxarm/bin/ocamlmklib -ldopt -Wl,-syslibroot,/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk -o lablgles ml_gl.o    ml_raw.o ml_glarray.o ml_glframe.o raw.cmx gl.cmx glLight.cmx glMat.cmx glMisc.cmx glPix.cmx glClear.cmx glTex.cmx glDraw.cmx glFunc.cmx glArray.cmx glFramebuffer.cmx -framework OpenGLES

For the iOS Simulator it looks as follows:

    $ cp Makefile.config.iossim Makefile.config
    $ make lib libopt
    cd src && make all LIBDIR="`ocamlc -where`"
    /usr/local/ocamlxsim/bin/ocamlc -pp /usr/local/ocamlxsim/bin/camlp4o var2def.ml -o var2def
        . . .
    /usr/local/ocamlxsim/bin/ocamlmklib -ldopt -Wl,-syslibroot,/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.3.sdk -o lablgles ml_gl.o      ml_raw.o ml_glarray.o ml_glframe.o raw.cmx gl.cmx glLight.cmx glMat.cmx glMisc.cmx glPix.cmx glClear.cmx glTex.cmx glDraw.cmx glFunc.cmx glArray.cmx glFramebuffer.cmx -framework OpenGLES

The compilation produces around 50 lines of output, some of them quite long.

The supplied Makefile.config.ios and Makefile.config.iossim specify that LablGLES should be installed in a subdirectory named release. To create this local installation, type make install:

    $ make install
    cd src && make install
        . . .

The installation process produces around 15 lines of output. The contents of the release directory should look something like this:

    $ ls -l release
    total 712
    -rw-r--r--  1 psellos  staff   4492 Aug 16 15:47 build.ml
    -rwxr-xr-x  1 psellos  staff  33028 Aug 16 15:47 dlllablgles.so
    -rw-r--r--  1 psellos  staff   3065 Aug 16 15:47 gl.cmi
    -rw-r--r--  1 psellos  staff    707 Aug 16 15:47 gl.cmx
    -rw-r--r--  1 psellos  staff   2719 Aug 16 15:47 gl.ml
    -rw-r--r--  1 psellos  staff   1874 Aug 16 15:47 gl.mli
        . . .
    -rw-r--r--  1 psellos  staff  53872 Aug 16 15:47 lablgles.a
    -rw-r--r--  1 psellos  staff  18325 Aug 16 15:47 lablgles.cma
    -rw-r--r--  1 psellos  staff   2564 Aug 16 15:47 lablgles.cmxa
    -rw-r--r--  1 psellos  staff  36384 Aug 16 15:47 liblablgles.a
    -rw-r--r--  1 psellos  staff   6258 Aug 16 15:47 raw.cmi
    -rw-r--r--  1 psellos  staff   1313 Aug 16 15:47 raw.cmx
    -rw-r--r--  1 psellos  staff   3179 Aug 16 15:47 raw.ml
    -rw-r--r--  1 psellos  staff   3716 Aug 16 15:47 raw.mli

Test

To verify that the library works, try compiling and linking a small test program, such as the following:

    $ cat glestest.ml
    let main () =
        begin
        GlPix.store (`unpack_alignment 1);
        GlFunc.blend_func `one `one_minus_src_alpha;
        Gl.enable `blend;
        GlTex.env (`mode `modulate);
        GlDraw.viewport ~x: 0 ~y: 0 ~w: 320 ~h: 460;
        GlMat.mode `projection;
        GlMat.load_identity ();
        GlMat.ortho ~x: (0.0, 320.0) ~y: (0.0, 460.0)
                    ~z: (-100.0, 100.0);
        GlClear.color ~alpha: 1.0 (0.8, 0.333, 0.0);
        GlClear.clear [`color];
        end

    let () = main ()

Now compile and link with the cross compiler. This example is for compilation to iOS (with OCamlXARM). To test compilation to the iOS Simulator, replace ocamlxarm with ocamlxsim.

    $ /usr/local/ocamlxarm/bin/ocamlopt -I release -o glestest lablgles.cmxa glestest.ml
    $ file glestest
    glestest: Mach-O executable arm

If the compiler produces an executable, LablGLES very likely has been built correctly.

You may, instead, see an error like one of the following:

    $ /usr/local/ocamlxarm/bin/ocamlopt -I release -o glestest lablgles.cmxa glestest.ml
    ld: library not found for -lcrt1.o
    collect2: ld returned 1 exit status
    Error during linking

or

    $ /usr/local/ocamlxsim/bin/ocamlopt -I release -o glestest lablgles.cmxa glestest.ml
    ld: framework not found OpenGLES
    collect2: ld returned 1 exit status
    File "caml_startup", line 1, characters 0-1:
    Error: Error during linking

This means that your version of OCamlXARM or OCamlXSim has been built with an earlier version of the iOS SDK. As described on the OCamlXARM page, this is hard to avoid due to Apple’s steady release of new versions of iOS and SDKs for them.

You could rebuild OCamlXARM and OCamlXSim for the latest SDK, but the easiest solution is to specify explicit options to ocamlopt telling it which SDK version to use. To use the iOS 4.3 SDK with OCamlXARM:

    $ PLAT=/Developer/Platforms/iPhoneOS.platform
    $ PLATBIN=$PLAT/Developer/usr/bin
    $ SDK=$PLAT/Developer/SDKs/iPhoneOS4.3.sdk
    $ OCCC="$PLATBIN/gcc-4.2 -arch armv6"
    $ OCOPTS="-ccopt -isysroot -ccopt $SDK -cclib -Wl,-syslibroot,$SDK"
    $ /usr/local/ocamlxarm/bin/ocamlopt -cc "$OCCC" $OCOPTS -I release -o glestest lablgles.cmxa glestest.ml
    $ file glestest
    glestest: Mach-O executable arm

To use the iOS 4.3 SDK with OCamlXSim:

    $ PLAT=/Developer/Platforms/iPhoneSimulator.platform
    $ SDK=$PLAT/Developer/SDKs/iPhoneSimulator4.3.sdk
    $ OCOPTS="-ccopt -isysroot -ccopt $SDK -cclib -Wl,-syslibroot,$SDK"
    $ /usr/local/ocamlxarm/bin/ocamlopt $OCOPTS -I release -o glestest lablgles.cmxa glestest.ml
    $ file glestest
    glestest: Mach-O executable i386

Although the extra options are painful to specify, you’re generally not going to be typing them in by hand. You just need to set up your build system (Makefile, Xcode, etc.) once, and it will take care of it.

Note: These compiles of glestest.ml only test that the LablGLES library has been created correctly. A program that actually displays graphical content requires a little more setup. For a working example of a LablGLES app, see the accompanying note OCaml OpenGL ES iPhone App: IcosaBlue.

Further Information

As I said above, I’ve put together an example LablGLES app named IcosaBlue. The app itself is quite simple, but it demonstrates the extra Cocoa Touch classes and setup required to embed OpenGL ES graphics in an iPhone app. To make it as useful as possible, I packaged it both for the iPhone Simulator and for the iPhone device. See the IcosaBlue page for a description of how to build it and how it works.

I’m happy to get any comments, corrections, or questions at jeffsco@psellos.com.