Back to Home

OpenGL extensions in PHP FFI via GLFW

The article describes the implementation of OpenGL extensions in PHP FFI via GLFW without external DLLs. Bridge generation from C headers is automated, procedural stubs provide autocomplete. Full cross-platform compatibility with glfwGetProcAddress.

PHP FFI + GLFW: dynamic OpenGL extensions
Advertisement 728x90

Dynamic OpenGL Loading via GLFW in PHP FFI

In the PHP FFI approach to OpenGL, manually copying constants and typedefs from GLFW3 headers results in excessive boilerplate. We convert #define statements into PHP constants and insert API functions into the resource section unchanged.

Example transformation:

// glfw3.h
#define GLFW_VERSION_MAJOR 3

// glfw3.php
const GLFW_VERSION_MAJOR = 3;
// glfw3.h
GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window);

// glfw3.php (in __halt_compiler())
void glfwMakeContextCurrent(GLFWwindow* window);

FFI functions behave as both proxy object methods and pointers simultaneously:

Google AdInline article slot
$glfwMakeContextCurrent = $glfw->glfwMakeContextCurrent;
$glfwMakeContextCurrent($window);

Procedural Stubs for Autocomplete

Create PHP wrapper functions with static pointer caching for typing and IDE support:

const GLFW_INSTANCE = FFI::cdef(...);

function glfwMakeContextCurrent(mixed ...$args): void {
    static $function = GLFW_INSTANCE->glfwMakeContextCurrent;
    $function(...$args);
}

This approach minimizes FFI proxy method calls by replacing them with closures. Performance is maintained because function addresses are static.

Alternatives (not recommended):

Google AdInline article slot
  • Passing $glfw as the first argument.
  • Using global $glfw.

With the GLFW_INSTANCE constant, the main code simplifies:

require 'src/glfw3.php';

if (!glfwInit()) exit(-1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
$window = glfwCreateWindow(640, 480, 'Hello World', null, null);

Dynamic OpenGL Loading Without opengl32.dll

OpenGL functions are loaded dynamically via platform-dependent GetProcAddress functions:

  • Windows: wglGetProcAddress
  • Linux: glXGetProcAddressARB
  • Android: eglGetProcAddress

GLFW unifies this into the cross-platform glfwGetProcAddress:

Google AdInline article slot
function glClear(mixed ...$args): void {
    static $function = GLFW_INSTANCE->cast(
        'PFNGLCLEARPROC',
        GLFW_INSTANCE->glfwGetProcAddress('glClear')
    );
    $function(...$args);
}

Types like PFNGLCLEARPROC are taken from glcorearb.h:

typedef void (*PFNGLCLEARPROC)(GLbitfield mask);

Extension support (NV, AMD → ARB) is checked dynamically: hardware determines available functions, and drivers emulate missing ones.

Generating Bridges from Source Code

Automate the creation of glfw3.php (20k+ lines):

  • ffi/preprocessor: Converts C headers into PHP format, extracts defines.
  • ffi/ide-helper-generator: Generates stubs with argument types.

GLFW 3.3 was chosen for compatibility with Ubuntu 24.04. OpenGL constants and functions are integrated into a single file, and opengl.php is removed.

Advantages:

  • Full compatibility with ext-glfw.
  • Cross-platform without manual adjustments.
  • Autocomplete and typing without compilation.

Key Points

  • GLFW_INSTANCE as a global constant provides fast FFI access without globals.
  • *PFNGLPROC** types from glcorearb.h simplify casting without manual signature specification.
  • glfwGetProcAddress replaces the platform zoo (WGL/GLX/EGL).
  • Generation from source guarantees the relevance of 20k+ lines.
  • Performance: static cache minimizes FFI method overhead.

The code is ready for drawing triangles with buffers and shaders in the next part.

— Editorial Team

Advertisement 728x90

Read Next