返回首页

通过 GLFW 在 PHP FFI 中的 OpenGL 扩展

本文描述了通过 GLFW 在 PHP FFI 中实现 OpenGL 扩展,无需外部 DLL。桥从 C 头文件自动生成,过程存根提供自动补全。与 glfwGetProcAddress 完全跨平台兼容。

PHP FFI + GLFW:动态 OpenGL 扩展
Advertisement 728x90

通过PHP FFI实现GLFW动态加载OpenGL

在PHP FFI中使用OpenGL时,手动从GLFW3头文件复制常量和类型定义会产生大量样板代码。我们将#define语句转换为PHP常量,并将API函数原封不动地插入资源部分。

转换示例:

// glfw3.h
#define GLFW_VERSION_MAJOR 3

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

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

FFI函数同时作为代理对象方法和指针使用:

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

用于自动补全的过程化存根

创建带有static指针缓存的PHP包装函数,以支持类型提示和IDE辅助:

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

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

这种方法通过用闭包替换FFI代理方法调用,最小化了FFI开销。由于函数地址是静态的,性能得以保持。

不推荐的替代方案:

Google AdInline article slot
  • $glfw作为第一个参数传递。
  • 使用global $glfw

使用GLFW_INSTANCE常量后,主代码简化如下:

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);

无需opengl32.dll的动态OpenGL加载

OpenGL函数通过平台相关的GetProcAddress函数动态加载:

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

GLFW将其统一为跨平台的glfwGetProcAddress

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

PFNGLCLEARPROC等类型取自glcorearb.h

typedef void (*PFNGLCLEARPROC)(GLbitfield mask);

扩展支持(NV、AMD → ARB)是动态检查的:硬件决定可用函数,驱动程序模拟缺失的功能。

从源代码生成桥接代码

自动化生成glfw3.php(超过2万行):

  • ffi/preprocessor:将C头文件转换为PHP格式,提取定义。
  • ffi/ide-helper-generator:生成带有参数类型的存根。

选择GLFW 3.3是为了与Ubuntu 24.04兼容。OpenGL常量和函数被集成到单个文件中,opengl.php被移除。

优势:

  • ext-glfw完全兼容。
  • 跨平台,无需手动调整。
  • 无需编译即可实现自动补全和类型提示。

关键要点

  • GLFW_INSTANCE作为全局常量,无需全局变量即可快速访问FFI。
  • *PFNGLPROC**类型来自glcorearb.h,无需手动指定签名即可简化类型转换。
  • glfwGetProcAddress取代了平台特定的WGL/GLX/EGL。
  • 从源代码生成保证了超过2万行代码的时效性。
  • 性能:静态缓存最小化了FFI方法开销。

代码已准备就绪,下一部分将使用缓冲区和着色器绘制三角形。

— Editorial Team

Advertisement 728x90

继续阅读