oxgl
符合标准的WebGL 1和WebGL 2的Node.js,通过 角度,与SwiftShader软件回退。完整的WebGL 1和WebGL 2 api 的实现,带有扩展和上下文丢失,并根据 Khronos一致性套件;js不加修改地渲染它。
安装
npm install @oxgl-js/oxgl
预构建Linux x64和arm64 (glibc 2.34或更新版本,如Ubuntu 22.04或
Debian 12), macOS arm64, Windows x64和arm64,在Node.js 22.22.2或
更新。安装不需要Rust工具链、编译器或安装脚本:npm
只下载主机的平台包(@oxgl-js/<platform>),其中包含
本地插件及其ANGLE库。Linux与肌肉(如Alpine)有
没有建立;在那里导入oxgl会抛出一个错误
code === 'OXGL_NATIVE_MISSING'。要从源代码构建,请参见下面的先决条件
和 CONTRIBUTING.md.
平台
支持和测试:Linux x64和Linux arm64 (glibc), macOS arm64, Windows
x64和Windows arm64。每个都有一个固定的ANGLE包,CI运行Rust和
节点测试和两个Khronos一致性套件在每个软件上
后端。这个 结果页面 显示每一个
的一致性页面和js示例在每个平台上的结果
最新运行的CI main,为每一个不及格的理由。
一致性结果与Linux arm64仅在这些页面中有所不同
conformance/expectations.txt 它们适用的平台列表:
- 在x64 (Linux和Windows)上,18个deqp页面呈现为32位整数
彩色缓冲器(
rgba32i,r32ui与之相似的是,他们的图像比较失败了, 因为WebGL一致性测试中的一个bug:他们的着色器转换浮点数 刚刚超过32位整数范围,GLSL未定义 参考图像假设结果是固定的。Arm64箝位和x86没有。 原生dEQP测试在2018年修复了这个问题;WebGL端口没有。
Mac上的Docker没有向Linux容器公开GPU,因此Linux arm64运行使用
软件后端。在macOS arm64上已经检查了GPU后端路径
(苹果GPU上的ANGLE Metal):所有Node测试都通过,并且Khronos结果匹配
Linux arm64除在图像解码上的差异外,由一致性决定
Runner的canvas包(当时是node-canvas;现在 @napi-rs/canvas)。
已知的问题
- SwiftShader初始化简单地改变进程全局状态。 这个
进程中的第一个SwiftShader上下文(
backend: 'software',OXGL_BACKEND=software,或'auto'GPU无法提供的上下文, 是否没有可用的GPU或一个GPU上下文创建失败,为 例如当GPU内存耗尽时)使角度设置VK_ICD_FILENAMES在Linux和Windows上,更改工作目录 几毫秒,然后恢复两者。的线程 环境或使用的相对路径在那一刻可能会出错或崩溃。 GPU后端不受影响。控制这种情况的发生,创造和毁灭 在启动其他线程之前,启动时的一个软件上下文。原因是 角的ScopedVkLoaderEnvironment;这个条目一旦被固定就会消失 角度停止这样做(跟踪)angle/README.md“已知的上游问题”)。 - SwiftShader可以得到一个近剪切三角形的深度错误。 与
软件后端,一个大的三角形表示可以在近平面剪辑中绘制
深度太近,隐藏了前面的几何形状。一个典型的
Case是一个由两个三角形组成的地平面,通过相机下方,其中
然后隐藏站在上面的物体。GPU后端不受影响。细分
大的几何图形(例如
new THREE.PlaneGeometry(6, 6, 32, 32)而不是PlaneGeometry(6, 6))避免它。在跟踪angle/README.md, “已知上游问题”。 - 每个使用oxgl的线程都会留下大约72字节。 角分配
在线程的第一个EGL调用时的每个线程状态,并且故意不释放
因此,创建上下文的工作线程在创建上下文时泄漏了这些字节
退出。这是每个线程,而不是每个上下文。应用程序
很多短期的工人应该重用工人。在跟踪
angle/README.md“已知的上游问题”。
使用
import { createContext } from '@oxgl-js/oxgl';
const gl = createContext({ width: 64, height: 32 });
gl.clearColor(0, 1, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
const pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
gl.readPixels(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
console.log(pixels.subarray(0, 4)); // Uint8Array(4) [ 0, 255, 0, 255 ]
gl.destroy();
createContext({ width, height, backend, webgl2, attributes, canvas }) 返回
一个 WebGLRenderingContext,或者 WebGL2RenderingContext 当 webgl2 是
true。不需要窗口系统、显示服务器或画布。 width 和
height 必须是0到2147483647之间的整数(0等于1);大一号
实现的限制提供了一个较小的绘图缓冲区与相同的方面
比率,检查一下 drawingBufferWidth 和 drawingBufferHeight.
attributes 以 WebGLContextAttributes 浏览器的 getContext
需要(alpha, depth, stencil, antialias, premultipliedAlpha,
preserveDrawingBuffer, powerPreference, failIfMajorPerformanceCaveat),
使用相同的默认值; getContextAttributes() 报告分配了什么。
在浏览器中, antialias 默认为 true,它给出了一个4个样本缓冲
加上一个解析缓冲区,大约是单采样RGBA8内存的9倍
缓冲区;通过 antialias: false 当你不需要的时候。无头缓冲器是
从来没有出现过,所以它的内容一直存在 preserveDrawingBuffer 是多少。
failIfMajorPerformanceCaveat: true 拒绝软件后端:with
'auto' 它表现为 'gpu',与 'software' 它把
OXGL_CONTEXT_CREATION_FAILED.
画布和调整大小
gl.canvas 是 canvas 选项的对象,或者 HeadlessCanvas (一个
EventTarget 与 width 和 height)。设置
gl.canvas.width 或者 height 调整绘图缓冲区的大小,就像HTML一样
画布。 gl.resize(width, height) (不是WebGL的一部分)做同样的任何
上下文,也在更新 HeadlessCanvas. 调整大小将清除缓冲区和
保持所有其他状态,包括视窗。
接受画布和上下文的库,比如three.js,可以同时使用两者:
import * as THREE from 'three';
import { createContext } from '@oxgl-js/oxgl';
const gl = createContext({ width: 256, height: 256, webgl2: true });
const renderer = new THREE.WebGLRenderer({ canvas: gl.canvas, context: gl });
renderer.setSize(256, 256, false); // false: a HeadlessCanvas has no style
npm run threejs 渲染three.js自己的WebGL示例(release r186)
并将每一帧与版本保存的参考截图进行比较
有关它(见 CONTRIBUTING.md). 在带有软件后端的Linux arm64上,为
它运行的354个示例(three.js自己的测试也跳过了377个示例):
- 228在three.js的阈值内匹配它们的引用(0.1%的像素);
- 67个像素的差异高达5%:63个像素在线,多边形和文本边缘,其中 SwiftShader栅格化不同于制作参考的GPU, 在画布上绘制的HTML,运行程序不渲染;
- 59 .不匹配。其中51个达到极限的测试跑者而不是
oxgl:它拒绝的远程资产,用CSS、SVG或2D画布绘制的页面;
视图放置由CSS流布局,HTML内容围绕画布,模块
工人,或没有
AudioContext. 8个不同的已知原因:SwiftShader (3; 包括在“已知问题”下几近被剪断的三角形),随机噪声 (3), SwiftShader中更多的压缩纹理格式,和一个挂钟 计时器。没有一个是无法解释的。
后端
backend | 呈现与 |
|---|---|
'auto' (默认) | 平台GPU后端(macOS上的Metal, Linux上的Vulkan, Windows上的D3D11),或者SwiftShader(如果失败) |
'gpu' | 仅支持GPU后端;抛出 OXGL_GPU_UNAVAILABLE 如果失败了 |
'software' | SwiftShader (CPU),无需GPU或GPU驱动即可工作 |
CPU驱动程序(SwiftShader, llvmpipe, lavapipe, WARP)永远不会算作GPU。如果 GPU后端初始化失败,它不会重试,直到进程 将重新启动。创建一个GPU上下文失败,例如GPU耗尽 记忆,只影响那一次呼唤。
这个 OXGL_BACKEND 环境变量(auto, gpu,或者 software)
覆盖了 backend 选择。它从调用线程中读取
process.env 在每一个 createContext 调用,这样worker就可以设置自己的。一个
空值将被忽略。
扩展
getSupportedExtensions() 列表中列出了angular可以启用的WebGL扩展
上下文的后端,和 getExtension(name) 启用一个并返回它的对象
(每次调用都是同一个对象)。在软件后端,包括
ANGLE_instanced_arrays, OES_vertex_array_object, WEBGL_draw_buffers,
OES_texture_float, OES_texture_half_float, EXT_color_buffer_float,
EXT_color_buffer_half_float, EXT_texture_filter_anisotropic,
WEBGL_depth_texture, WEBGL_multi_draw, S3TC, ETC, ASTC, BPTC和RGTC
compressed-texture扩展, WEBGL_lose_context 还有调试扩展。
WEBGL_color_buffer_float 没有提供(SwiftShader不能渲染到
RGBA/FLOAT 纹理),也不是定时器查询扩展。
环境损失
丢失的背景 WEBGL_lose_context.loseContext() 或者在后端
火灾 webglcontextlost 在以后的任务中;调用
preventDefault() 它允许恢复,之后 webglcontextrestored
火灾和上下文以新的状态再次呈现。之前创建的对象
丢失之后无效,就像在浏览器中一样。
发布环境
destroy() (不是WebGL的一部分)释放上下文的本地资源
并将其置于lost状态:之后的调用什么都不做, getError()
返回 CONTEXT_LOST_WEBGL 偶尔地 NO_ERROR,以及绘图缓冲器
大小读作 0. 一个上下文变得无法访问,没有 destroy() 是
垃圾回收后释放。
错误
无效选项引发 TypeError 或者 RangeError. 其他的失败
Error 与一个 code:
code | 导致 |
|---|---|
OXGL_GPU_UNAVAILABLE | backend: 'gpu' (选项或 OXGL_BACKEND)无法创建上下文 |
OXGL_INVALID_BACKEND | OXGL_BACKEND 有别的值吗 auto, gpu, software,或空的 |
OXGL_CONTEXT_CREATION_FAILED | 没有后端可以创建上下文,例如WebGL 2在没有OpenGL ES 3.0的后端,或者 failIfMajorPerformanceCaveat 通过软件后端 |
OXGL_NATIVE_MISSING | 本机插件或其ANGLE库丢失或加载失败 |
WebGL错误通过 getError(),如在浏览器中。
表演
npm run bench 测量每个呼叫开销,抽取,转移和两个3 .js
场景。在Linux arm64 Docker(16核,软件后端,每个案例2秒):
| 情况下 | 结果 |
|---|---|
enable (一个状态调用:WebIDL转换和本机调用) | 11.6 M呼叫/s |
uniform4f | 7.3 M呼叫/s |
drawArrays一个小三角形 | 53k /s |
bufferSubData, 1 MiB | 32 GB / s |
texSubImage2D, 512×512 rgba8 | 26 GB / s |
readPixels, 256×256 rgba8 | 3.3 GB / s |
| Three.js, 1000个点亮的网格 | 52帧/秒 |
| 三个。js,一个128×64-segment点亮球 | 1230帧/秒 |
draw的开销与直接调用ANGLE相同(51.9 k draw /s)
cargo test -p oxgl-sys --test angle --release raw_draw_rate -- --ignored):
时间是SwiftShader的,GPU后端绘制得更快。oxgl补充道
每次呼叫约100纳秒。
在GPU后端(macOS arm64, ANGLE Metal, Apple M4 Max)上也是如此 基准测试给出了715 k的小帧/秒和830帧/秒的1000网格 three.js场景。
先决条件
从源构建:
- rustup. 固定工具链
rust-toolchain.toml是 第一次使用时自动安装。 - Node.js 22.22.2或更新版本(
engines在package.json)。 - libclang,它在构建时生成EGL/GLES绑定
macOS的命令行工具,
libclang-dev(或任何LLVM安装), Windows上的LLVM。集LIBCLANG_PATH如果没有找到。 - 为
npm run conformance: ffmpeg(带ffprobe),用于解码 套房的视频。 - 对于许可证检查:在版本CI引脚处拒绝货和约货
(
CARGO_DENY_VERSION和CARGO_ABOUT_VERSION在.github/workflows/ci.yml),安装有cargo install --locked cargo-deny@<version>和cargo install --locked cargo-about@<version> --features cli.
命令
npm install # install JS tooling
npm run build # build the native addon (release) and the TypeScript package into dist/
npm run lint # rustfmt, clippy (pedantic, warnings as errors), ESLint, tsc, license check
npm test # node:test suites in test/ (run after build)
npm run conformance # Khronos WebGL conformance suites, judged against conformance/expectations.txt
npm run threejs # three.js's WebGL examples, judged against threejs/expectations.txt
npm run notices # regenerate THIRD_PARTY_NOTICES after changing Rust dependencies
cargo test --workspace --exclude oxgl-node # Rust tests (oxgl-node is tested from Node)
规范和变更建议存在于 openspec/;运行OpenSpec npx openspec.
CONTRIBUTING.md 描述CI检查以及如何在本地运行每个检查。
许可证
BSD-3-Clause;看到 LICENSE. 捆绑第三方组件及其许可证
都列在 THIRD_PARTY_NOTICES.