How to use ANGLE + GLFW?

Hi, I want to port a C application esshader using native OpenGL ES 3.0 + GLFW to ANGLE + GLFW.
ANGLE has been built from latest source, the ANGLE demos work fine.
GLFW is glfw-x11 3.3.8 from the Arch repos.
The native OpenGL ES 3.0 + GLFW application works as expexted, so GLFW must be fine.
My makefile includes the ANGLE include dir and links libGLESv2.so and libEGL.so in the ANGLE/out/Release dir.
ldd ./esshader shows that the linked angle libraries indeed are loaded (correct path).
The below initialization code succeeds and the window is created:

    if (!glfwInit())
        die("Unable to initialize GLFW.\n");

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, gles_major);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, gles_minor);

    GLFWmonitor *monitor = NULL;
    if (fullscreen) {
        monitor = glfwGetPrimaryMonitor();
        const GLFWvidmode *mode = glfwGetVideoMode(monitor);
        width = mode->width;
        height = mode->height;
    }

    if (!(window = glfwCreateWindow(width, height, "esshader", monitor, NULL))) {
        glfwTerminate();
        die("Unable to create GLFW window.\n");
    }

    glfwMakeContextCurrent(window);

    const char* description;
    int code = glfwGetError(&description);
    if (code != GLFW_NO_ERROR) {
        fprintf(stderr, "GLFW error %d: %s\n", code, description);
    }

    if (glfwGetCurrentContext() == NULL) {
        die("Failed to make GLFW context current.\n");
    }

    if (window_x >= 0 && window_y >= 0) {
        glfwSetWindowPos(window, window_x, window_y);
    }

But then directly after, glGetString always returns null:

    const GLubyte* renderer = glGetString(GL_RENDERER);
    const GLubyte* version = glGetString(GL_VERSION);
    const GLubyte* vendor = glGetString(GL_VENDOR);
    printf("Renderer: %s\n", renderer);
    printf("OpenGL version supported %s\n", version);
    printf("OpenGL vendor: %s\n", vendor);

And then shader compilation fails and the window closes, most of the time.
But sometimes the window stays open but black instead of rendering the shader.
What am I doing wrong?

Where from you are getting glGetString and other functions? Make sure they are loaded from the same libGLESv2.so file, and not from your regular OpenGL libGL.so or system’s libGLESv2.so. Otherwise calling GL function when you have GLES context from Angle will most likely return NULL. Or crash for other functions.

To make sure this happens the easiest way is to dynamically query entry points with glfwGetProcAddress because glfw is aware which context created (GL vs GLES) is and how it is created (EGL vs GLX vs WGL …) and will return correct function pointers.

So for each (Open)gl(ES) function I want to use, I need to manually retrieve a function ptr from glfw?
So far this has seemed to work:

typedef const GLubyte* (*PFNGLGETSTRINGPROC)(GLenum name);
PFNGLGETSTRINGPROC glGetString = (PFNGLGETSTRINGPROC) glfwGetProcAddress("glGetString");

With the output being:

Renderer: Mesa Intel(R) HD Graphics 5500 (BDW GT2)
OpenGL version supported OpenGL ES 3.2 Mesa 23.0.4
OpenGL vendor: Intel

Is that expected? Shouldn’t there be angle somewhere?
The output of ldd on the binary references angle/out/Release/libEGL.so and angle/out/Release/libGLESv2.so.

I have this in my Makefile:

# ANGLE library paths
ANGLE_DIR = /home/a/libraries/angle
ANGLE_INC_D = $(ANGLE_DIR)/include
ANGLE_INC = -I$(ANGLE_INC_D)
ANGLE_LNK_D = $(ANGLE_DIR)/out/Release
ANGLE_LNK = -L$(ANGLE_LNK_D)

export LD_LIBRARY_PATH := $(ANGLE_LNK_D):$(LD_LIBRARY_PATH) 
# at runtime, search this location first for ANGLEs libGLESv2.so and libEGL.so

# includes and libs
INC_DIRS = ${ANGLE_INC}
LIB_DIRS = ${ANGLE_LNK}
INCS = -I. ${INC_DIRS}
LIBS = -lc -lm -lEGL -lGLESv2 $(shell pkg-config --libs glfw3)

# toolchain flags
CPPFLAGS = -DVERSION=\"${VERSION}\"
CFLAGS = -std=c99 -pedantic -Wall -O2 ${INCS} ${CPPFLAGS} \
	$(pkg-config --cflags glfw3)
LDFLAGS = -s ${LIB_DIRS} ${LIBS} -Wl,-rpath=$(ANGLE_LNK_D)

compiler is gcc

That probably means that libEGL and libGLESv2 shared libraries are loaded from your system, like /usr/lib - and those come from mesa package which provides native drivers for your Intel GPU.

How did you compile glfw? did it have same LDFLAGS set? Meaning rpath in glfw references angle’s should reference Angle’s .so files, not default system ones.

Try setting LD_LIBRARY_PATH env variable to folder where Angle’s .so files are located before running application, this way it will load libEGL/libGLESv2.so files by default from there instead from system:

LD_LIBRARY_PATH=angle/out/Release ./your_application

So glfw does need to be compiled against ANGLE, I was using pre-compiled from Arch repo!!
Will try that immediately.
LD_LIBRARY_PATH=/home/a/libraries/angle/out/Release ./esshader sadly didn’t bring any change … yet

Check the file names for .so libraries - glfw loads them with libEGL.so.1 and libGLESv2.so.2 names. You will may need to symlink to these names.

Or if you’re rebuilding glfw, then you can set what names it uses for EGL and GLESv2 libs by setting _GLFW_EGL_LIBRARY and _GLFW_GLESV2_LIBRARY defines to strings.

IT WERKS!!!

Thank you so much @mmozeiko , for me this is a great achievement! Been pulling my hair out all week!!
What you said was 100% spot-on, glfw was expecting ´libEGL.so.1´ and ´libGLESv2.so.2´ as dictated by ´egl_context.c´.
The built ANGLE libraries are named ´libEGL.so´ and ´libGLESv2.so´, so they were found at their specified paths by the linker, hence no errors during compilation BUT glfw couldn’t load them from that path so it looked in ´/usr/lib´ where all my other libraries are and loaded the native ones instead, but now function addresses couldn’t line up so the program didn’t work!
The solution is to go to the ´angle/out/Release/´ dir where the built libraries end up and simply create the symlinks yourself:

ln -s libEGL.so libEGL.so.1
ln -s libGLESv2.so libGLESv2.so.2

Then, during compilation, it will find and link against the files without the .1 and .2 suffix but when the program tries to load the libraries during execution, the symlinks redirect it to the real files.
Now glGetString FINALLY returns these instead of NULL:

Renderer: ANGLE (Intel, Mesa Intel(R) HD Graphics 5500 (BDW GT2), OpenGL 4.6 (Core Profile) Mesa 23.0.4)
OpenGL version supported OpenGL ES 3.0.0 (ANGLE 2.1.21545 git hash: f2e0f8a0b236)
OpenGL vendor: Google Inc. (Intel)

I had to remove glad again as that was causing a runtime error after glfw initialization despite not being actually used / called:

[65544] EGL: Failed to create window surface: A NativeWindowType argument does not refer to a valid native window

Your clever trick of manually retrieving the gl function addresses from glfw to determine the actually-loaded gl library can be very telling and I’ll save that one to disk in the hope I’ll never need that one again.
But now it’s not necessary to do that anymore, the gl functions “just work”, probably thanks to glfw but it’s beyond me how that sorcery works.

So again, thank you very much for your help!

Right, now it works because in your case all glfwGetProcAddress does it just lookups exported symbol in loaded libGLESv2.so file with dlsym. But if you’re linking to exactly same .so file, those are exactly same address, so everything works just fine.

But using glfwGetProcAddress is useful in cases when you don’t know which libGLESv2 file will be loaded. For example you may have application designed to work with GL and GLES contexts and allow user switch between them. In such cases you don’t know where are gl* functions, and that’s where glfwGetProcAddress helps.

hope this thread never gets taken down as good beginner resources on Google ANGLE are very rare, still
with macOS banishing openGL to the shadow realm you’d guess people would heavily use these alternative APIs but apparently devs willingly take the L and suck up to the big Corp in the sky, might as well learn their low-level graphics API (Metal).

Later on when my code is ready I’ll make a repo and be proud to share esshader-angle, especially for beginners to learn from.