@gjsify/webgl 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,9 +113,11 @@ yarn build:prebuilds
113
113
 
114
114
  ## Prebuilt binaries
115
115
 
116
- Prebuilds for `linux-x64`, `linux-arm64`, `linux-ppc64`, `linux-s390x`, and `linux-riscv64` are built automatically by CI
116
+ Prebuilds for every target in `package.json#gjsify.platforms` are built automatically by CI
117
117
  (`.github/workflows/prebuilds.yml`) when the Vala source changes and committed back
118
- to the repository. They are included in the npm package via the `files` field.
118
+ to the repository. Since ADR 0017 each one ships in its own
119
+ `@gjsify/webgl-<os>-<arch>` package, referenced from here as an `optionalDependency`, so a
120
+ consumer installs only the artifact that fits its platform.
119
121
 
120
122
  ## Inspirations and credits
121
123
 
@@ -130,17 +132,44 @@ to the repository. They are included in the npm package via the `files` field.
130
132
  | `linux-x64` | ✅ `libgwebgl.so` + `Gwebgl-0.1.typelib` | native runner |
131
133
  | `linux-arm64` | ✅ | native runner |
132
134
  | `linux-ppc64`, `linux-s390x`, `linux-riscv64` | ✅ | QEMU emulation |
133
- | macOS (`darwin-arm64` / `darwin-x64`) | ❌ | **unverified, see below** |
135
+ | `darwin-arm64` / `darwin-x64` | ✅ `libgwebgl.dylib` + `Gwebgl-0.1.typelib` | native runner — **WebGL1 only, see below** |
134
136
  | Windows | ❌ | — no Vala/GI bridge in this repo targets Windows |
135
137
 
136
- **macOS is not blocked — it is unverified, and a green build would not be enough.** The
137
- build needs gtk4 + libepoxy + gdk-pixbuf from Homebrew (all available). The real question is
138
- runtime: GTK4's macOS (Quartz) backend goes through CGL, which tops out at desktop OpenGL 4.1
139
- Core and offers no GLES 3.2 — the profile this Vala bridge is written against. So compiling
140
- successfully on macOS would say nothing about whether `Gtk.GLArea` can actually render a
141
- WebGL2 context there. `meson.build` already emits the correct `.dylib` typelib leaf; the
142
- manual-dispatch `build-prebuilds-macos-experimental` job in the prebuilds workflow exists to
143
- establish the compile half of the answer.
138
+ **macOS renders — WebGL1 only.** Measured on macOS 15.7.8 / x86_64 with gtk 4.22 and
139
+ libepoxy 1.5.10: `Gtk.GLArea` realizes a `GdkMacosGLContext`, and this bridge draws real
140
+ pixels through it. That describes the GL stack once GTK is loaded — reaching it needed
141
+ `DYLD_LIBRARY_PATH=/usr/local/lib` exported by hand, because the `Gtk-4.0` typelib names a
142
+ bare `libgtk-4.1.dylib` leaf that a Homebrew prefix does not put on the loader's search path
143
+ for every host; that defect is tracked and fixed separately. Two further limits come from the
144
+ platform, not from the bridge:
145
+
146
+ - GTK4's macOS backend goes through **CGL, which offers desktop OpenGL only** — no GLES
147
+ profile at any version. A GLES-exclusive request (`gtk_gl_area_set_use_es(TRUE)`) fails
148
+ with *"Application does not support OpenGL API"*, so `WebGLBridge` declares
149
+ `set_allowed_apis(GL | GLES)` and lets GDK choose: GLES 3.2 where it exists, desktop
150
+ GL 4.1 on macOS.
151
+ - Desktop GL 4.1 has **no GLSL ES 3.00 compiler** (that needs `ARB_ES3_compatibility`, GL 4.3),
152
+ so `#version 300 es` shaders — i.e. all WebGL2 content, including three.js ≥ r163 and
153
+ Excalibur 0.32 — do not compile. `#version 100` (WebGL1) does, via `ARB_ES2_compatibility`.
154
+ Of the 219 GL entry points the library references, exactly two are absent from Apple's
155
+ `OpenGL.framework` (`glInvalidateFramebuffer`, `glInvalidateSubFramebuffer`, both GL 4.3);
156
+ they are gated on `epoxy_gl_version() >= 43` because libepoxy aborts rather than returning
157
+ null.
158
+
159
+ Remaining darwin gaps are tracked in [`status/open-todos.md`](../../../status/open-todos.md).
160
+
161
+ ## Diagnosing a host
162
+
163
+ ```bash
164
+ gjsify run packages/framework/webgl/scripts/probe-gl-host.js [--seconds N]
165
+ ```
166
+
167
+ Configures a bare `Gtk.GLArea` exactly as `WebGLBridge` does and reports the negotiated GL API
168
+ and version, the widget scale factor, the logical allocation versus the device-pixel drawing
169
+ buffer, the GL vendor/renderer/GLSL strings and which shader dialects compile — then draws a
170
+ recognisable pattern using only `clearColor` + `scissor` + `clear`, so a blank window can never
171
+ be mistaken for a failed shader. Exits non-zero when the GLArea does not realize. It needs a
172
+ real display, which is why it is a script rather than a spec.
144
173
 
145
174
  ## License
146
175
 
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{WebGLRenderingContext as e}from"./webgl-rendering-context.js";import{WebGL2RenderingContext as t}from"./webgl2-rendering-context.js";import{HTMLCanvasElement as n}from"./html-canvas-element.js";import{notifyElementResize as r}from"@gjsify/dom-elements";import i from"gi://GObject";import a from"gi://GLib?version=2.0";import o from"gi://Gtk?version=4.0";import{attachEventControllers as s}from"@gjsify/event-bridge";import{Event as c}from"@gjsify/dom-events";const l=i.registerClass({GTypeName:`GjsifyWebGLBridge`},class WebGLBridge extends o.GLArea{constructor(e){super(e),this._canvas=null,this._readyCallbacks=[],this._resizeCallbacks=[],this._renderTag=null,this._tickCallbackId=null,this._frameCallback=null,this._lastFrameCallback=null,this._lastFrameTime=0,this._replaying=!1,this._frameCallbackId=0,this._nextFrameId=1,this._timeOrigin=a.get_monotonic_time(),this.set_use_es(!0),this.set_required_version(3,2),this.set_has_depth_buffer(!0),this.set_has_stencil_buffer(!0),s(this,()=>this._canvas,{captureKeys:!0}),this._tickCallbackId=this.add_tick_callback((e,t)=>(this._frameCallback!==null&&this.queue_render(),a.SOURCE_CONTINUE));let t=this.connect(`render`,()=>{this.disconnect(t),this.make_current(),this._canvas=new n(this);{let e=globalThis;e.document?.body&&e.document.body.appendChild(this._canvas)}this._canvas.getContext(`webgl2`);let e=this._canvas.getContext(`webgl`);if(e){for(let t of this._readyCallbacks)t(this._canvas,e);this._readyCallbacks=[]}return this._renderTag=this.connect(`render`,e=>{let t=(a.get_monotonic_time()-this._timeOrigin)/1e3;if(this._frameCallback!==null){globalThis.__GJSIFY_DEBUG_RAF===!0&&console.log(`[rAF] frame callback fires t=${t.toFixed(1)}`);let e=this._frameCallback;this._frameCallback=null,this._frameCallbackId=0,this._lastFrameCallback=e,this._lastFrameTime=t,e(t)}else if(this._lastFrameCallback!==null){this._replaying=!0;try{this._lastFrameCallback(this._lastFrameTime)}finally{this._replaying=!1}}return!0}),!0});this.connect(`resize`,()=>{let e=this.get_allocated_width(),t=this.get_allocated_height();this._canvas&&(this._canvas.dispatchEvent(new c(`resize`)),r(this._canvas,e,t));for(let n of this._resizeCallbacks)n(e,t);this._frameCallback&&this.requestAnimationFrame(this._frameCallback)}),this.connect(`unrealize`,()=>{this._renderTag!==null&&(this.disconnect(this._renderTag),this._renderTag=null),this._tickCallbackId!==null&&(this.remove_tick_callback(this._tickCallbackId),this._tickCallbackId=null),this._frameCallback=null,this._lastFrameCallback=null,this._lastFrameTime=0,this._replaying=!1,this._canvas=null})}get canvas(){return this._canvas}onReady(e){if(this._canvas){let t=this._canvas.getContext(`webgl`);if(t){e(this._canvas,t);return}}this._readyCallbacks.push(e)}onWebGLReady(e){this.onReady(e)}onResize(e){this._resizeCallbacks.push(e)}requestAnimationFrame(e){let t=this._nextFrameId++;return this._replaying?t:(this._frameCallback=e,this._frameCallbackId=t,this.queue_render(),t)}cancelAnimationFrame(e){e!==0&&e===this._frameCallbackId&&(this._frameCallback=null,this._frameCallbackId=0,this._lastFrameCallback=null,this._lastFrameTime=0)}installGlobals(){let n=globalThis;n.requestAnimationFrame=e=>this.requestAnimationFrame(e),n.cancelAnimationFrame=e=>this.cancelAnimationFrame(e),n.WebGLRenderingContext=e,n.WebGL2RenderingContext=t,Object.defineProperty(globalThis,"devicePixelRatio",{get:()=>this.get_scale_factor()||1,configurable:!0});let r=this._timeOrigin;n.performance={now:()=>(a.get_monotonic_time()-r)/1e3,timeOrigin:Date.now()}}});export{l as WebGLBridge};
1
+ import"./_virtual/_rolldown/runtime.js";import{WebGLRenderingContext as e}from"./webgl-rendering-context.js";import{WebGL2RenderingContext as t}from"./webgl2-rendering-context.js";import{HTMLCanvasElement as n}from"./html-canvas-element.js";import{notifyElementResize as r}from"@gjsify/dom-elements";import i from"gi://GObject";import a from"gi://Gdk?version=4.0";import o from"gi://GLib?version=2.0";import s from"gi://Gtk?version=4.0";import{attachEventControllers as c}from"@gjsify/event-bridge";import{Event as l}from"@gjsify/dom-events";const u=i.registerClass({GTypeName:`GjsifyWebGLBridge`},class WebGLBridge extends s.GLArea{constructor(e){super(e),this._canvas=null,this._readyCallbacks=[],this._resizeCallbacks=[],this._renderTag=null,this._tickCallbackId=null,this._frameCallback=null,this._lastFrameCallback=null,this._lastFrameTime=0,this._replaying=!1,this._frameCallbackId=0,this._nextFrameId=1,this._timeOrigin=o.get_monotonic_time(),this.set_allowed_apis(a.GLAPI.GL|a.GLAPI.GLES),this.set_required_version(3,2),this.set_has_depth_buffer(!0),this.set_has_stencil_buffer(!0),c(this,()=>this._canvas,{captureKeys:!0}),this._tickCallbackId=this.add_tick_callback((e,t)=>(this._frameCallback!==null&&this.queue_render(),o.SOURCE_CONTINUE));let t=this.connect(`render`,()=>{this.disconnect(t),this.make_current(),this._canvas=new n(this);{let e=globalThis;e.document?.body&&e.document.body.appendChild(this._canvas)}this._canvas.getContext(`webgl2`);let e=this._canvas.getContext(`webgl`);if(e){for(let t of this._readyCallbacks)t(this._canvas,e);this._readyCallbacks=[]}return this._renderTag=this.connect(`render`,e=>{let t=(o.get_monotonic_time()-this._timeOrigin)/1e3;if(this._frameCallback!==null){globalThis.__GJSIFY_DEBUG_RAF===!0&&console.log(`[rAF] frame callback fires t=${t.toFixed(1)}`);let e=this._frameCallback;this._frameCallback=null,this._frameCallbackId=0,this._lastFrameCallback=e,this._lastFrameTime=t,e(t)}else if(this._lastFrameCallback!==null){this._replaying=!0;try{this._lastFrameCallback(this._lastFrameTime)}finally{this._replaying=!1}}return!0}),!0});this.connect(`resize`,()=>{let e=this.get_allocated_width(),t=this.get_allocated_height();this._canvas&&(this._canvas.dispatchEvent(new l(`resize`)),r(this._canvas,e,t));for(let n of this._resizeCallbacks)n(e,t);this._frameCallback&&this.requestAnimationFrame(this._frameCallback)}),this.connect(`unrealize`,()=>{this._renderTag!==null&&(this.disconnect(this._renderTag),this._renderTag=null),this._tickCallbackId!==null&&(this.remove_tick_callback(this._tickCallbackId),this._tickCallbackId=null),this._frameCallback=null,this._lastFrameCallback=null,this._lastFrameTime=0,this._replaying=!1,this._canvas=null})}get canvas(){return this._canvas}onReady(e){if(this._canvas){let t=this._canvas.getContext(`webgl`);if(t){e(this._canvas,t);return}}this._readyCallbacks.push(e)}onWebGLReady(e){this.onReady(e)}onResize(e){this._resizeCallbacks.push(e)}requestAnimationFrame(e){let t=this._nextFrameId++;return this._replaying?t:(this._frameCallback=e,this._frameCallbackId=t,this.queue_render(),t)}cancelAnimationFrame(e){e!==0&&e===this._frameCallbackId&&(this._frameCallback=null,this._frameCallbackId=0,this._lastFrameCallback=null,this._lastFrameTime=0)}installGlobals(){let n=globalThis;n.requestAnimationFrame=e=>this.requestAnimationFrame(e),n.cancelAnimationFrame=e=>this.cancelAnimationFrame(e),n.WebGLRenderingContext=e,n.WebGL2RenderingContext=t,Object.defineProperty(globalThis,"devicePixelRatio",{get:()=>this.get_scale_factor()||1,configurable:!0});let r=this._timeOrigin;n.performance={now:()=>(o.get_monotonic_time()-r)/1e3,timeOrigin:Date.now()}}});export{u as WebGLBridge};
@@ -1,5 +1,5 @@
1
1
  import GObject from 'gi://GObject';
2
- import type Gdk from 'gi://Gdk?version=4.0';
2
+ import Gdk from 'gi://Gdk?version=4.0';
3
3
  import GLib from 'gi://GLib?version=2.0';
4
4
  import Gtk from 'gi://Gtk?version=4.0';
5
5
  import { HTMLCanvasElement as OurHTMLCanvasElement } from './html-canvas-element.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/webgl",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "WebGL module for Gjs",
5
5
  "module": "lib/esm/index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -60,8 +60,8 @@
60
60
  "WebGL"
61
61
  ],
62
62
  "devDependencies": {
63
- "@gjsify/cli": "^0.27.0",
64
- "@gjsify/unit": "^0.27.0",
63
+ "@gjsify/cli": "^0.28.0",
64
+ "@gjsify/unit": "^0.28.0",
65
65
  "@types/bit-twiddle": "^1.0.3",
66
66
  "@types/node": "^25.9.2",
67
67
  "typescript": "^6.0.3"
@@ -71,10 +71,10 @@
71
71
  "@girs/gjs": "^4.1.0",
72
72
  "@girs/gtk-4.0": "^4.1.0",
73
73
  "@girs/gwebgl-0.1": "0.1.0-4.0.0-rc.5",
74
- "@gjsify/dom-elements": "^0.27.0",
75
- "@gjsify/dom-events": "^0.27.0",
76
- "@gjsify/event-bridge": "^0.27.0",
77
- "@gjsify/utils": "^0.27.0",
74
+ "@gjsify/dom-elements": "^0.28.0",
75
+ "@gjsify/dom-events": "^0.28.0",
76
+ "@gjsify/event-bridge": "^0.28.0",
77
+ "@gjsify/utils": "^0.28.0",
78
78
  "bit-twiddle": "^1.0.2",
79
79
  "glsl-tokenizer": "^2.1.5"
80
80
  },
@@ -89,12 +89,12 @@
89
89
  },
90
90
  "homepage": "https://github.com/gjsify/gjsify/tree/main/packages/framework/webgl#readme",
91
91
  "optionalDependencies": {
92
- "@gjsify/webgl-darwin-arm64": "0.27.0",
93
- "@gjsify/webgl-darwin-x64": "0.27.0",
94
- "@gjsify/webgl-linux-arm64": "0.27.0",
95
- "@gjsify/webgl-linux-ppc64": "0.27.0",
96
- "@gjsify/webgl-linux-riscv64": "0.27.0",
97
- "@gjsify/webgl-linux-s390x": "0.27.0",
98
- "@gjsify/webgl-linux-x64": "0.27.0"
92
+ "@gjsify/webgl-darwin-arm64": "0.28.0",
93
+ "@gjsify/webgl-darwin-x64": "0.28.0",
94
+ "@gjsify/webgl-linux-arm64": "0.28.0",
95
+ "@gjsify/webgl-linux-ppc64": "0.28.0",
96
+ "@gjsify/webgl-linux-riscv64": "0.28.0",
97
+ "@gjsify/webgl-linux-s390x": "0.28.0",
98
+ "@gjsify/webgl-linux-x64": "0.28.0"
99
99
  }
100
100
  }