glre 0.45.0 → 0.47.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.
Files changed (52) hide show
  1. package/README.md +4 -26
  2. package/dist/addons.d.ts +35 -50
  3. package/dist/index.cjs +6 -6
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +37 -90
  6. package/dist/index.js +6 -6
  7. package/dist/index.js.map +1 -1
  8. package/dist/native.cjs +1 -1
  9. package/dist/native.cjs.map +1 -1
  10. package/dist/native.d.ts +45 -93
  11. package/dist/native.js +1 -1
  12. package/dist/native.js.map +1 -1
  13. package/dist/node.cjs +15 -15
  14. package/dist/node.cjs.map +1 -1
  15. package/dist/node.d.ts +35 -50
  16. package/dist/node.js +15 -15
  17. package/dist/node.js.map +1 -1
  18. package/dist/react.cjs +1 -1
  19. package/dist/react.cjs.map +1 -1
  20. package/dist/react.d.ts +37 -91
  21. package/dist/react.js +1 -1
  22. package/dist/react.js.map +1 -1
  23. package/dist/solid.cjs +1 -1
  24. package/dist/solid.cjs.map +1 -1
  25. package/dist/solid.d.ts +37 -91
  26. package/dist/solid.js +1 -1
  27. package/dist/solid.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/{utils/helpers.ts → helpers.ts} +10 -32
  30. package/src/index.ts +45 -42
  31. package/src/native.ts +6 -7
  32. package/src/node/build.ts +3 -19
  33. package/src/node/create.ts +2 -4
  34. package/src/node/index.ts +8 -20
  35. package/src/node/types.ts +2 -0
  36. package/src/node/utils/index.ts +5 -2
  37. package/src/node/utils/infer.ts +5 -13
  38. package/src/node/utils/parse.ts +18 -34
  39. package/src/node/utils/utils.ts +15 -10
  40. package/src/react.ts +9 -12
  41. package/src/solid.ts +3 -10
  42. package/src/types.ts +30 -22
  43. package/src/webgl/compute.ts +56 -0
  44. package/src/webgl/graphic.ts +65 -0
  45. package/src/webgl/index.ts +21 -0
  46. package/src/{utils/program.ts → webgl/utils.ts} +30 -8
  47. package/src/webgpu/compute.ts +39 -0
  48. package/src/webgpu/graphic.ts +89 -0
  49. package/src/webgpu/index.ts +42 -0
  50. package/src/{utils/pipeline.ts → webgpu/utils.ts} +75 -78
  51. package/src/utils/webgl.ts +0 -135
  52. package/src/utils/webgpu.ts +0 -178
package/src/index.ts CHANGED
@@ -1,18 +1,12 @@
1
1
  import { durable, event } from 'reev'
2
2
  import { createFrame, createQueue } from 'refr'
3
- import { is } from './utils/helpers'
4
- import { webgl } from './utils/webgl'
5
- import { webgpu } from './utils/webgpu'
3
+ import { is } from './helpers'
4
+ import { webgl } from './webgl'
5
+ import { webgpu } from './webgpu'
6
6
  import type { EventState } from 'reev'
7
7
  import type { GL } from './types'
8
8
  export * from './types'
9
9
 
10
- export const isGL = (a: unknown): a is EventState<GL> => {
11
- if (!is.obj(a)) return false
12
- if ('isGL' in a) return true
13
- return false
14
- }
15
-
16
10
  export const isServer = () => {
17
11
  return typeof window === 'undefined'
18
12
  }
@@ -22,9 +16,11 @@ export const isWebGPUSupported = () => {
22
16
  return 'gpu' in navigator
23
17
  }
24
18
 
25
- let iTime = performance.now()
19
+ const findElement = (arg: Partial<GL>) => {
20
+ return arg.el || arg.elem || arg.element
21
+ }
26
22
 
27
- export const createGL = (props?: Partial<GL>) => {
23
+ export const createGL = (...args: Partial<GL>[]) => {
28
24
  const gl = event({
29
25
  isNative: false,
30
26
  isWebGL: true,
@@ -33,16 +29,9 @@ export const createGL = (props?: Partial<GL>) => {
33
29
  isDebug: false,
34
30
  isDepth: false,
35
31
  wireframe: false,
36
- isGL: true,
37
32
  size: [0, 0],
38
33
  mouse: [0, 0],
39
- count: 6,
40
- instanceCount: 1,
41
- particleCount: 1024,
42
34
  precision: 'highp',
43
- webgl: {},
44
- webgpu: {},
45
- loading: 0,
46
35
  error() {
47
36
  gl.isError = true
48
37
  gl.isLoop = false
@@ -51,67 +40,81 @@ export const createGL = (props?: Partial<GL>) => {
51
40
  },
52
41
  }) as EventState<GL>
53
42
 
43
+ let iTime = performance.now()
54
44
  gl.queue = createQueue()
55
45
  gl.frame = createFrame()
56
46
 
57
47
  gl.attribute = durable((k, v, i) => gl.queue(() => gl._attribute?.(k, v, i)), gl)
58
48
  gl.instance = durable((k, v, at) => gl.queue(() => gl._instance?.(k, v, at)), gl)
59
49
  gl.storage = durable((k, v) => gl.queue(() => gl._storage?.(k, v)), gl)
60
- gl.uniform = durable((k, v) => gl.queue(() => gl._uniform?.(k, v)), gl)
61
50
  gl.texture = durable((k, v) => gl.queue(() => gl._texture?.(k, v)), gl)
51
+ gl.uniform = durable((k, v) => gl.queue(() => gl._uniform?.(k, v)), gl)
62
52
  gl.uniform({ iResolution: gl.size, iMouse: [0, 0], iTime })
63
53
 
64
- gl('mount', async () => {
65
- if (!isWebGPUSupported()) gl.isWebGL = true
66
- gl.vs = gl.vs || gl.vert || gl.vertex
67
- gl.fs = gl.fs || gl.frag || gl.fragment
68
- gl.cs = gl.cs || gl.comp || gl.compute
69
- if (gl.isWebGL) {
70
- gl((await webgl(gl)) as GL)
71
- } else gl((await webgpu(gl)) as GL)
72
- if (gl.isError) return // stop if error
54
+ gl('mount', async (el: HTMLCanvasElement) => {
55
+ gl.el = findElement(gl) || el || args.map(findElement).find(Boolean)
56
+ const isAppend = !gl.el // Check first: canvas may unmount during WebGPU async processing
57
+ if (isAppend && !gl.isNative) gl.el = document.createElement('canvas')
58
+ for (const arg of args) {
59
+ gl.fs = arg.fs || arg.frag || arg.fragment || undefined
60
+ gl.cs = arg.cs || arg.comp || arg.compute || undefined
61
+ gl.vs = arg.vs || arg.vert || arg.vertex || undefined
62
+ gl.triangleCount = arg.triangleCount || arg.count || 6
63
+ gl.instanceCount = arg.instanceCount || 1
64
+ gl.particleCount = arg.particleCount || 1024
65
+ gl(arg)
66
+ if (is.bol(arg.isWebGL)) gl.isWebGL = arg.isWebGL || !isWebGPUSupported()
67
+ if (gl.isWebGL) webgl(gl)
68
+ else await webgpu(gl)
69
+ if (arg.mount) arg.mount() // events added in mount phase need explicit call to execute
70
+ }
71
+ if (!gl.el || gl.isError) return // stop if error or canvas was unmounted during async
73
72
  gl.resize()
74
73
  gl.frame(() => {
75
- gl.loop()
76
- gl.queue.flush()
77
- if (gl.loading) return true // wait for textures @TODO FIX
78
74
  gl.render()
79
75
  return gl.isLoop
80
76
  })
81
77
  if (gl.isNative) return
78
+ if (isAppend) document.body.appendChild(gl.el)
82
79
  window.addEventListener('resize', gl.resize)
83
80
  gl.el.addEventListener('mousemove', gl.mousemove)
84
81
  })
85
82
 
86
83
  gl('clean', () => {
87
84
  gl.frame.stop()
88
- if (gl.isNative) return
85
+ if (!gl.el || gl.isNative) return
89
86
  window.removeEventListener('resize', gl.resize)
90
87
  gl.el.removeEventListener('mousemove', gl.mousemove)
91
88
  })
92
89
 
90
+ gl('ref', (el: HTMLCanvasElement | null) => {
91
+ if (el) {
92
+ gl.el = el
93
+ gl.mount()
94
+ } else gl.clean()
95
+ })
96
+
93
97
  gl('resize', () => {
94
- const w = gl.width || window.innerWidth
95
- const h = gl.height || window.innerHeight
96
- gl.size[0] = gl.el.width = w
97
- gl.size[1] = gl.el.height = h
98
+ const rect = gl.el.parentElement?.getBoundingClientRect()
99
+ gl.size[0] = gl.el.width = gl.width || rect?.width || window.innerWidth
100
+ gl.size[1] = gl.el.height = gl.height || rect?.height || window.innerHeight
98
101
  gl.uniform('iResolution', gl.size)
99
102
  })
100
103
 
101
104
  gl('mousemove', (_e: any, x = _e.clientX, y = _e.clientY) => {
102
- const [w, h] = gl.size
103
- const { top, left } = gl.el.getBoundingClientRect()
104
- gl.mouse[0] = (x - top - w / 2) / (w / 2)
105
- gl.mouse[1] = -(y - left - h / 2) / (h / 2)
105
+ const rect = gl.el.getBoundingClientRect()
106
+ gl.mouse[0] = (x - rect.left) / rect.width
107
+ gl.mouse[1] = -(y - rect.top) / rect.height + 1
106
108
  gl.uniform('iMouse', gl.mouse)
107
109
  })
108
110
 
109
- gl('loop', () => {
111
+ gl('render', () => {
110
112
  iTime = performance.now() / 1000
111
113
  gl.uniform('iTime', iTime)
114
+ gl.queue.flush()
112
115
  })
113
116
 
114
- return gl(props)
117
+ return gl
115
118
  }
116
119
 
117
120
  export default createGL
package/src/native.ts CHANGED
@@ -1,14 +1,13 @@
1
- import { useState } from 'react' // @ts-ignore
2
- // import { Dimensions } from 'react-native'
3
- import { createGL, isGL } from './index'
1
+ import { useState } from 'react'
2
+ import { createGL } from './index'
4
3
  import type { GL } from './types'
5
4
  export * from './index'
6
5
 
7
- export const useGL = (props: Partial<GL> = {}) => {
6
+ export const useGL = (...args: Partial<GL>[]) => {
8
7
  return useState(() => {
9
- const gl = isGL(props) ? props : createGL(props)
8
+ const gl = createGL(...args)
9
+ gl.isNative = true
10
10
  gl.ref = (ctx: any) => {
11
- gl.el = {} as any
12
11
  gl({
13
12
  render() {
14
13
  ctx.flush()
@@ -24,6 +23,6 @@ export const useGL = (props: Partial<GL> = {}) => {
24
23
  resize()
25
24
  // Dimensions.addEventListener('change', resize)
26
25
  }
27
- return gl({ isNative: true })
26
+ return gl()
28
27
  })[0]
29
28
  }
package/src/node/build.ts CHANGED
@@ -36,24 +36,9 @@ const generateStruct = (id: string, map: Map<string, string>) => {
36
36
  return `struct ${id} {\n ${Array.from(map.values()).join(',\n ')}\n}`
37
37
  }
38
38
 
39
+ const PRECISION = ['float', 'int', 'sampler2D', 'samplerCube', 'sampler3D', 'sampler2DArray', 'sampler2DShadow', 'samplerCubeShadow', 'sampler2DArrayShadow', 'isampler2D', 'isampler3D', 'isamplerCube', 'isampler2DArray', 'usampler2D', 'usampler3D', 'usamplerCube', 'usampler2DArray']
39
40
  const precisionHead = (result: string[], precision = 'highp') => {
40
- result.push(`precision ${precision} float;`)
41
- result.push(`precision ${precision} int;`)
42
- result.push(`precision ${precision} sampler2D;`)
43
- result.push(`precision ${precision} samplerCube;`)
44
- result.push(`precision ${precision} sampler3D;`)
45
- result.push(`precision ${precision} sampler2DArray;`)
46
- result.push(`precision ${precision} sampler2DShadow;`)
47
- result.push(`precision ${precision} samplerCubeShadow;`)
48
- result.push(`precision ${precision} sampler2DArrayShadow;`)
49
- result.push(`precision ${precision} isampler2D;`)
50
- result.push(`precision ${precision} isampler3D;`)
51
- result.push(`precision ${precision} isamplerCube;`)
52
- result.push(`precision ${precision} isampler2DArray;`)
53
- result.push(`precision ${precision} usampler2D;`)
54
- result.push(`precision ${precision} usampler3D;`)
55
- result.push(`precision ${precision} usamplerCube;`)
56
- result.push(`precision ${precision} usampler2DArray;`)
41
+ for (const key of PRECISION) result.push(`precision ${precision} ${key};`)
57
42
  }
58
43
 
59
44
  // ref: https://github.com/mrdoob/three.js/blob/master/src/renderers/webgl/WebGLCapabilities.js
@@ -69,7 +54,6 @@ const getMaxPrecision = (c?: WebGL2RenderingContext, precision = 'highp') => {
69
54
  const p0 = c.getShaderPrecisionFormat(c.VERTEX_SHADER, c.MEDIUM_FLOAT)
70
55
  const p1 = c.getShaderPrecisionFormat(c.FRAGMENT_SHADER, c.MEDIUM_FLOAT)
71
56
  if (p0 && p0.precision > 0 && p1 && p1.precision > 0) return 'mediump'
72
- precision = 'lowp'
73
57
  }
74
58
  return 'lowp'
75
59
  }
@@ -81,7 +65,7 @@ export const fragment = (x: X, c: NodeContext = {}) => {
81
65
  const result = []
82
66
  if (c.isWebGL) {
83
67
  result.push('#version 300 es')
84
- precisionHead(result, getMaxPrecision(c.gl?.webgl?.context, c.gl?.precision))
68
+ precisionHead(result, getMaxPrecision(c.gl?.gl, c.gl?.precision))
85
69
  result.push('out vec4 fragColor;')
86
70
  for (const code of c.code?.fragInputs?.values() || []) result.push(`in ${code}`)
87
71
  result.push(head)
@@ -1,7 +1,7 @@
1
1
  import { compute, fragment, vertex } from './build'
2
2
  import { addToScope, assign, toVar } from './scope'
3
3
  import { code, getConstant, isConversion, isFunction, isOperator, getId, isArrayAccess } from './utils'
4
- import { is } from '../utils/helpers'
4
+ import { is } from '../helpers'
5
5
  import type { Bool, Constants as C, Functions, NodeProps, NodeTypes, Operators, X, Y } from './types'
6
6
 
7
7
  const toPrimitive = (x: Y, hint: string) => {
@@ -35,9 +35,7 @@ export const create = <T extends C>(type: NodeTypes, props?: NodeProps | null, .
35
35
  if (key === 'assign') return assign.bind(null, x, x.type === 'gather')
36
36
  if (key === 'select') return select.bind(null, x)
37
37
  if (isOperator(key)) {
38
- return key.endsWith('Assign')
39
- ? (...args: Y[]) => addToScope(operator(key, x, ...args))
40
- : (...args: Y[]) => operator(key, x, ...args)
38
+ return key.endsWith('Assign') ? (...args: Y[]) => addToScope(operator(key, x, ...args)) : (...args: Y[]) => operator(key, x, ...args)
41
39
  }
42
40
  if (isFunction(key)) return (...args: Y[]) => function_(key, x, ...args)
43
41
  if (isConversion(key)) return () => conversion(getConstant(key), x)
package/src/node/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { hex2rgb } from './utils'
2
2
  import { builtin as b, conversion as c, function_ as f, uniform as u } from './create'
3
- import { is } from '../utils/helpers'
3
+ import { is } from '../helpers'
4
4
  import type { Constants as C, Float, X, Y } from './types'
5
5
  export * from './build'
6
6
  export * from './create'
@@ -72,12 +72,8 @@ export const any = <T extends C>(x: X<T>) => f<'bool'>('any', x)
72
72
 
73
73
  // 2. Always return float with WGSL-compliant type constraints and unified parameter types
74
74
  export const determinant = <T extends 'mat2' | 'mat3' | 'mat4'>(x: X<T>) => f<'float'>('determinant', x)
75
- export const distance = <T extends 'vec2' | 'vec3' | 'vec4', U extends C>(x: X<T>, y: number | X<U>) =>
76
- f<'float'>('distance', x, y)
77
- export const dot = <T extends 'vec2' | 'vec3' | 'vec4' | 'ivec2' | 'ivec3' | 'ivec4', U extends C>(
78
- x: X<T>,
79
- y: number | X<U>
80
- ) => f<T extends `ivec${string}` ? 'int' : 'float'>('dot', x, y)
75
+ export const distance = <T extends 'vec2' | 'vec3' | 'vec4', U extends C>(x: X<T>, y: number | X<U>) => f<'float'>('distance', x, y)
76
+ export const dot = <T extends 'vec2' | 'vec3' | 'vec4' | 'ivec2' | 'ivec3' | 'ivec4', U extends C>(x: X<T>, y: number | X<U>) => f<T extends `ivec${string}` ? 'int' : 'float'>('dot', x, y)
81
77
  export const length = <T extends 'vec2' | 'vec3' | 'vec4'>(x: X<T>) => f<'float'>('length', x)
82
78
  export const lengthSq = (x: X) => f<'float'>('lengthSq', x)
83
79
  export const luminance = (x: X) => f<'float'>('luminance', x)
@@ -134,24 +130,16 @@ export const trunc = <T extends C>(x: X<T>) => f<T>('trunc', x)
134
130
 
135
131
  // 1. Functions where first argument determines return type with unified parameter types
136
132
  export const atan2 = <T extends C, U extends C>(x: X<T>, y: number | X<U>) => f<T>('atan2', x, y)
137
- export const clamp = <T extends C, U extends C>(x: X<T>, min: number | X<U>, max: number | X<U>) =>
138
- f<T>('clamp', x, min, max)
133
+ export const clamp = <T extends C, U extends C>(x: X<T>, min: number | X<U>, max: number | X<U>) => f<T>('clamp', x, min, max)
139
134
  export const max = <T extends C, U extends C>(x: X<T>, y: number | X<U>) => f<T>('max', x, y)
140
135
  export const min = <T extends C, U extends C>(x: X<T>, y: number | X<U>) => f<T>('min', x, y)
141
- export const mix = <T extends C, U extends C>(x: X<T>, y: number | X<U>, a: number | Float | X<U>) =>
142
- f<T>('mix', x, y, a)
136
+ export const mix = <T extends C, U extends C>(x: X<T>, y: number | X<U>, a: number | Float | X<U>) => f<T>('mix', x, y, a)
143
137
  export const pow = <T extends C, U extends C>(x: X<T>, y: number | X<U>) => f<T>('pow', x, y)
144
- export const reflect = <T extends 'vec2' | 'vec3' | 'vec4', U extends C>(I: X<T>, N: number | X<U>) =>
145
- f<T>('reflect', I, N)
146
- export const refract = <T extends 'vec2' | 'vec3' | 'vec4', U extends C>(
147
- I: X<T>,
148
- N: number | X<U>,
149
- eta: number | Float
150
- ) => f<T>('refract', I, N, eta)
138
+ export const reflect = <T extends 'vec2' | 'vec3' | 'vec4', U extends C>(I: X<T>, N: number | X<U>) => f<T>('reflect', I, N)
139
+ export const refract = <T extends 'vec2' | 'vec3' | 'vec4', U extends C>(I: X<T>, N: number | X<U>, eta: number | Float) => f<T>('refract', I, N, eta)
151
140
 
152
141
  // 2. Functions where not first argument determines return type with unified parameter types
153
- export const smoothstep = <T extends C, U extends C>(e0: number | X<U>, e1: number | X<U>, x: X<T>) =>
154
- f<T>('smoothstep', e0, e1, x)
142
+ export const smoothstep = <T extends C, U extends C>(e0: number | X<U>, e1: number | X<U>, x: X<T>) => f<T>('smoothstep', e0, e1, x)
155
143
  export const step = <T extends C, U extends C>(edge: number | X<U>, x: X<T>) => f<T>('step', edge, x)
156
144
  export const mod = <T extends C, U extends C>(x: X<T>, y: number | X<U>) => {
157
145
  return (x as any).sub((x as any).div(y).floor().mul(y)) as X<T>
package/src/node/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CONSTANTS, CONVERSIONS, FUNCTIONS, OPERATOR_KEYS, OPERATOR_TYPE_RULES } from './utils/const'
2
2
  import type { GL } from '../types'
3
+ import type { Binding } from '../webgpu/utils'
3
4
 
4
5
  export type Constants = (typeof CONSTANTS)[number] | 'void'
5
6
  export type Conversions = (typeof CONVERSIONS)[number]
@@ -80,6 +81,7 @@ export interface NodeProps {
80
81
 
81
82
  export interface NodeContext {
82
83
  gl?: Partial<GL>
84
+ binding?: Binding
83
85
  label?: 'vert' | 'frag' | 'compute'
84
86
  isWebGL?: boolean
85
87
  units?: any // @TODO FIX
@@ -1,7 +1,7 @@
1
1
  import { infer } from './infer'
2
2
  import { parseArray, parseAttribHead, parseConstantHead, parseDeclare, parseDefine, parseGather, parseIf, parseLoop, parseScatter, parseStorageHead, parseStruct, parseStructHead, parseSwitch, parseTexture, parseUniformHead, parseVaryingHead } from './parse'
3
3
  import { getBluiltin, getConversions, getOperator, initNodeContext, isX, setupEvent } from './utils'
4
- import { is } from '../../utils/helpers'
4
+ import { is } from '../../helpers'
5
5
  import { mod } from '..'
6
6
  import type { Constants as C, NodeContext, Y } from '../types'
7
7
 
@@ -114,7 +114,10 @@ export const code = <T extends C>(target: Y<T>, c?: NodeContext | null): string
114
114
  setupEvent(c, id, varType, target, x)
115
115
  head = parseUniformHead(c, id, varType)
116
116
  }
117
- if (type === 'storage') head = parseStorageHead(c, id, infer(target, c))
117
+ if (type === 'storage') {
118
+ setupEvent(c, id, type, target, x)
119
+ head = parseStorageHead(c, id, infer(target, c))
120
+ }
118
121
  if (type === 'constant') head = parseConstantHead(c, id, infer(target, c), code(x, c))
119
122
  if (head) {
120
123
  c.code?.headers.set(id, head)
@@ -1,12 +1,6 @@
1
1
  import { isConstants, isElement, isX, isSwizzle } from './utils'
2
- import {
3
- BUILTIN_TYPES,
4
- COMPONENT_COUNT_TO_TYPE,
5
- FUNCTION_RETURN_TYPES,
6
- getOperatorResultType,
7
- validateOperatorTypes,
8
- } from './const'
9
- import { is, getStride } from '../../utils/helpers'
2
+ import { BUILTIN_TYPES, COMPONENT_COUNT_TO_TYPE, FUNCTION_RETURN_TYPES, getOperatorResultType, validateOperatorTypes } from './const'
3
+ import { is, getStride, isFloat32 } from '../../helpers'
10
4
  import type { Constants as C, NodeContext, X, Y } from '../types'
11
5
 
12
6
  const inferBuiltin = <T extends C>(id: string | undefined) => {
@@ -14,8 +8,7 @@ const inferBuiltin = <T extends C>(id: string | undefined) => {
14
8
  }
15
9
 
16
10
  const inferOperator = <T extends C>(L: T, R: T, op: string): T => {
17
- if (!validateOperatorTypes(L, R, op))
18
- console.warn(`GLRE Type Warning: Invalid operator '${op}' between types '${L}' and '${R}'`)
11
+ if (!validateOperatorTypes(L, R, op)) console.warn(`GLRE Type Warning: Invalid operator '${op}' between types '${L}' and '${R}'`)
19
12
  return getOperatorResultType(L, R, op) as T
20
13
  }
21
14
 
@@ -23,15 +16,14 @@ export const inferPrimitiveType = <T extends C>(x: Y<T>) => {
23
16
  if (is.bol(x)) return 'bool' as T
24
17
  if (is.str(x)) return 'texture' as T
25
18
  if (is.num(x)) return 'float' as T // @TODO FIX: Number.isInteger(x) ? 'int' : 'float'
26
- if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
19
+ if (is.arr(x) || isFloat32(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
27
20
  if (isElement(x)) return 'texture' as T
28
21
  return 'void' as T
29
22
  }
30
23
 
31
24
  const inferFromCount = <T extends C>(count: number) => {
32
25
  const ret = COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
33
- if (!ret)
34
- throw `glre node system error: Cannot infer type from array length ${count}. Check your data size. Supported: 1(float), 2(vec2), 3(vec3), 4(vec4), 9(mat3), 16(mat4)`
26
+ if (!ret) throw `glre node system error: Cannot infer type from array length ${count}. Check your data size. Supported: 1(float), 2(vec2), 3(vec3), 4(vec4), 9(mat3), 16(mat4)`
35
27
  return ret
36
28
  }
37
29
 
@@ -1,9 +1,9 @@
1
1
  import { code } from '.'
2
2
  import { infer } from './infer'
3
3
  import { getConversions, addDependency } from './utils'
4
- import { is } from '../../utils/helpers'
4
+ import { is } from '../../helpers'
5
5
  import type { Constants, NodeContext, NodeProps, StructFields, Y } from '../types'
6
- import { storageSize } from '../../utils/program'
6
+ import { storageSize } from '../../webgl/utils'
7
7
 
8
8
  export const parseArray = (children: Y[], c: NodeContext) => {
9
9
  return children
@@ -60,9 +60,7 @@ export const parseIf = (c: NodeContext, x: Y, y: Y, children: Y[]) => {
60
60
  let ret = `if (${code(x, c)}) {\n${code(y, c)}\n}`
61
61
  for (let i = 2; i < children.length; i += 2) {
62
62
  const isElse = i >= children.length - 1
63
- ret += !isElse
64
- ? ` else if (${code(children[i], c)}) {\n${code(children[i + 1], c)}\n}`
65
- : ` else {\n${code(children[i], c)}\n}`
63
+ ret += !isElse ? ` else if (${code(children[i], c)}) {\n${code(children[i + 1], c)}\n}` : ` else {\n${code(children[i], c)}\n}`
66
64
  }
67
65
  return ret
68
66
  }
@@ -73,8 +71,7 @@ export const parseSwitch = (c: NodeContext, x: Y, children: Y[]) => {
73
71
  const isDefault = i >= children.length - 1
74
72
  if (isDefault && children.length % 2 === 0) {
75
73
  ret += `default:\n${code(children[i], c)}\nbreak;\n`
76
- } else if (i + 1 < children.length)
77
- ret += `case ${code(children[i], c)}:\n${code(children[i + 1], c)}\nbreak;\n`
74
+ } else if (i + 1 < children.length) ret += `case ${code(children[i], c)}:\n${code(children[i + 1], c)}\nbreak;\n`
78
75
  }
79
76
  ret += '}'
80
77
  return ret
@@ -157,14 +154,12 @@ export const parseDefine = (c: NodeContext, props: NodeProps, target: Y) => {
157
154
  * headers
158
155
  */
159
156
  export const parseVaryingHead = (c: NodeContext, id: string, type: Constants) => {
160
- return c.isWebGL
161
- ? `${type} ${id};`
162
- : `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${getConversions(type, c)}`
157
+ return c.isWebGL ? `${type} ${id};` : `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${getConversions(type, c)}`
163
158
  }
164
159
 
165
160
  export const parseAttribHead = (c: NodeContext, id: string, type: Constants) => {
166
161
  if (c.isWebGL) return `${type} ${id};`
167
- const { location = 0 } = c.gl?.webgpu?.attribs.map.get(id) || {}
162
+ const { location = 0 } = c.binding?.attrib(id) || {}
168
163
  const wgslType = getConversions(type, c)
169
164
  return `@location(${location}) ${id}: ${wgslType}`
170
165
  }
@@ -176,13 +171,10 @@ export const parseUniformHead = (c: NodeContext, id: string, type: Constants) =>
176
171
  ? `uniform sampler2D ${id};`
177
172
  : `uniform ${type} ${id};`
178
173
  if (isTexture) {
179
- const { group = 1, binding = 0 } = c.gl?.webgpu?.textures.map.get(id) || {}
180
- return (
181
- `@group(${group}) @binding(${binding}) var ${id}Sampler: sampler;\n` +
182
- `@group(${group}) @binding(${binding + 1}) var ${id}: texture_2d<f32>;`
183
- )
174
+ const { group = 1, binding = 0 } = c.binding?.texture(id) || {}
175
+ return `@group(${group}) @binding(${binding}) var ${id}Sampler: sampler;\n` + `@group(${group}) @binding(${binding + 1}) var ${id}: texture_2d<f32>;`
184
176
  }
185
- const { group = 0, binding = 0 } = c.gl?.webgpu?.uniforms.map.get(id) || {}
177
+ const { group = 0, binding = 0 } = c.binding?.uniform(id) || {}
186
178
  const wgslType = getConversions(type, c)
187
179
  return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
188
180
  }
@@ -194,7 +186,7 @@ export const parseStorageHead = (c: NodeContext, id: string, type: Constants) =>
194
186
  const location = c.units?.(id)
195
187
  return `${ret}\nlayout(location = ${location}) out vec4 _${id};` // out texture buffer
196
188
  }
197
- const { group = 0, binding = 0 } = c.gl?.webgpu?.storages.map.get(id) || {}
189
+ const { group = 0, binding = 0 } = c.binding?.storage(id) || {}
198
190
  const wgslType = getConversions(type, c)
199
191
  return `@group(${group}) @binding(${binding}) var<storage, read_write> ${id}: array<${wgslType}>;`
200
192
  }
@@ -204,24 +196,16 @@ export const parseLoop = (c: NodeContext, x: Y, y: Y, id: string) => {
204
196
  const bodyCode = code(y, c)
205
197
  const conditionCode = code(x, c)
206
198
  if (c.isWebGL) {
207
- if (conditionType === 'int')
208
- return `for (int ${id} = 0; ${id} < ${conditionCode}; ${id} += 1) {\n${bodyCode}\n}`
209
- if (conditionType === 'float')
210
- return `for (float ${id} = 0.0; ${id} < ${conditionCode}; ${id} += 1.0) {\n${bodyCode}\n}`
211
- if (conditionType === 'vec2')
212
- return `for (vec2 ${id} = vec2(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y; ${id} += vec2(1.0)) {\n${bodyCode}\n}`
213
- if (conditionType === 'vec3')
214
- return `for (vec3 ${id} = vec3(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y && ${id}.z < ${conditionCode}.z; ${id} += vec3(1.0)) {\n${bodyCode}\n}`
199
+ if (conditionType === 'int') return `for (int ${id} = 0; ${id} < ${conditionCode}; ${id} += 1) {\n${bodyCode}\n}`
200
+ if (conditionType === 'float') return `for (float ${id} = 0.0; ${id} < ${conditionCode}; ${id} += 1.0) {\n${bodyCode}\n}`
201
+ if (conditionType === 'vec2') return `for (vec2 ${id} = vec2(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y; ${id} += vec2(1.0)) {\n${bodyCode}\n}`
202
+ if (conditionType === 'vec3') return `for (vec3 ${id} = vec3(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y && ${id}.z < ${conditionCode}.z; ${id} += vec3(1.0)) {\n${bodyCode}\n}`
215
203
  return `for (int ${id} = 0; ${id} < ${conditionCode}; ${id} += 1) {\n${bodyCode}\n}`
216
204
  }
217
- if (conditionType === 'int')
218
- return `for (var ${id}: i32 = 0; ${id} < ${conditionCode}; ${id}++) {\n${bodyCode}\n}`
219
- if (conditionType === 'float')
220
- return `for (var ${id}: f32 = 0.0; ${id} < ${conditionCode}; ${id} += 1.0) {\n${bodyCode}\n}`
221
- if (conditionType === 'vec2')
222
- return `for (var ${id}: vec2f = vec2f(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y; ${id} += vec2f(1.0)) {\n${bodyCode}\n}`
223
- if (conditionType === 'vec3')
224
- return `for (var ${id}: vec3f = vec3f(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y && ${id}.z < ${conditionCode}.z; ${id} += vec3f(1.0)) {\n${bodyCode}\n}`
205
+ if (conditionType === 'int') return `for (var ${id}: i32 = 0; ${id} < ${conditionCode}; ${id}++) {\n${bodyCode}\n}`
206
+ if (conditionType === 'float') return `for (var ${id}: f32 = 0.0; ${id} < ${conditionCode}; ${id} += 1.0) {\n${bodyCode}\n}`
207
+ if (conditionType === 'vec2') return `for (var ${id}: vec2f = vec2f(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y; ${id} += vec2f(1.0)) {\n${bodyCode}\n}`
208
+ if (conditionType === 'vec3') return `for (var ${id}: vec3f = vec3f(0.0); ${id}.x < ${conditionCode}.x && ${id}.y < ${conditionCode}.y && ${id}.z < ${conditionCode}.z; ${id} += vec3f(1.0)) {\n${bodyCode}\n}`
225
209
  return `for (var ${id}: i32 = 0; ${id} < ${conditionCode}; ${id}++) {\n${bodyCode}\n}`
226
210
  }
227
211
 
@@ -1,7 +1,7 @@
1
1
  import { CONSTANTS, CONVERSIONS, FUNCTIONS, OPERATOR_KEYS, OPERATORS, TYPE_MAPPING, WGSL_TO_GLSL_BUILTIN } from './const'
2
- import { is } from '../../utils/helpers'
3
- import type { Constants as C, Conversions, Functions, NodeContext, Operators, Swizzles, VaryingInfo, X, Y } from '../types'
4
- import { storageSize } from '../../utils/program'
2
+ import { is, isFloat32 } from '../../helpers'
3
+ import { storageSize } from '../../webgl/utils'
4
+ import type { Constants as C, Conversions, Functions, NodeContext, Operators, Swizzles, X, Y } from '../types'
5
5
 
6
6
  export const isSwizzle = (key: unknown): key is Swizzles => {
7
7
  return is.str(key) && /^[xyzwrgbastpq]{1,4}$/.test(key)
@@ -103,28 +103,33 @@ export const addDependency = (c: NodeContext, id = '', type: string) => {
103
103
  */
104
104
  const getEventFun = (c: NodeContext, id: string, type: string) => {
105
105
  if (c.isWebGL) {
106
- if (type === 'attribute') return (value: any) => c.gl?.attribute?.(id, value)
107
- if (type === 'instance') return (value: any) => c.gl?.instance?.(id, value)
108
- if (type === 'texture') return (value: any) => c.gl?.texture?.(id, value)
106
+ if (type === 'attribute') return c.gl?.attribute?.bind(null, id as any)
107
+ if (type === 'instance') return c.gl?.instance?.bind(null, id as any)
108
+ if (type === 'texture') return c.gl?.texture?.bind(null, id as any)
109
+ if (type === 'storage') return c.gl?.storage?.bind(null, id as any)
109
110
  return (value: any) => c.gl?.uniform?.(id, value)
110
111
  }
111
- if (type === 'attribute') return (value: any) => c.gl?._attribute?.(id, value)
112
- if (type === 'instance') return (value: any) => c.gl?._instance?.(id, value)
113
- if (type === 'texture') return (value: any) => c.gl?._texture?.(id, value)
112
+ if (type === 'attribute') return c.gl?._attribute?.bind(null, id)
113
+ if (type === 'instance') return c.gl?._instance?.bind(null, id)
114
+ if (type === 'texture') return c.gl?._texture?.bind(null, id)
115
+ if (type === 'storage') return c.gl?._storage?.bind(null, id)
114
116
  return (value: any) => c.gl?._uniform?.(id, value)
115
117
  }
116
118
 
117
- const safeEventCall = <T extends C>(x: X<T>, fun: (value: unknown) => void) => {
119
+ const safeEventCall = <T extends C>(x: X<T>, fun: (value: any) => void) => {
118
120
  if (is.und(x)) return
119
121
  if (!isX(x)) return fun(x) // for uniform(0) or uniform([0, 1])
120
122
  if (x.type !== 'conversion') return
121
123
  const args = x.props.children?.slice(1)
122
124
  if (is.und(args?.[0])) return // ignore if uniform(vec2())
125
+ if (is.arr(args[0])) return fun(args[0]) // for attribute(vec2([0, 0.73, -1, -1, 1, -1]))
126
+ if (isFloat32(args[0])) return fun(args[0]) // for storage(float(new Float32Array(1024)))
123
127
  fun(args.map((x) => x ?? args[0])) // for uniform(vec2(1)) or uniform(vec2(1, 1))
124
128
  }
125
129
 
126
130
  export const setupEvent = (c: NodeContext, id: string, type: string, target: X, child: X) => {
127
131
  const fun = getEventFun(c, id, type)
132
+ if (!fun) return
128
133
  safeEventCall(child, fun)
129
134
  target.listeners.add(fun)
130
135
  return fun
package/src/react.ts CHANGED
@@ -1,17 +1,14 @@
1
1
  import { useState } from 'react'
2
- import { createGL, isGL } from './index'
2
+ import { createGL } from './index'
3
3
  import type { GL } from './types'
4
4
  export * from './index'
5
5
 
6
- export const useGL = (props: Partial<GL> = {}) => {
7
- return useState(() => {
8
- const gl = isGL(props) ? props : createGL(props)
9
- gl.ref = (el: HTMLCanvasElement | null) => {
10
- if (el) {
11
- gl.el = el
12
- gl.mount()
13
- } else gl.clean()
14
- }
15
- return gl
16
- })[0]
6
+ export const useGL = (...args: Partial<GL>[]) => {
7
+ const [, set] = useState(null) // for error boundary // ref: https://github.com/facebook/react/issues/14981
8
+ if (args[0] && !args[0].error)
9
+ args[0].error = (error = '') =>
10
+ set(() => {
11
+ throw new Error(error)
12
+ })
13
+ return useState(() => createGL(...args))[0]
17
14
  }
package/src/solid.ts CHANGED
@@ -1,14 +1,7 @@
1
- import { createGL, isGL } from './index'
1
+ import { createGL } from './index'
2
2
  import type { GL } from './types'
3
3
  export * from './index'
4
4
 
5
- export const onGL = (props?: Partial<GL>) => {
6
- const gl = isGL(props) ? props : createGL(props)
7
- gl.ref = (el: HTMLCanvasElement | null) => {
8
- if (el) {
9
- gl.el = el
10
- gl.mount()
11
- } else gl.clean()
12
- }
13
- return gl
5
+ export const onGL = (...args: Partial<GL>[]) => {
6
+ return createGL(...args)
14
7
  }