glre 0.34.0 → 0.35.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glre",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "author": "tseijp",
5
5
  "license": "MIT",
6
6
  "private": false,
package/src/index.ts CHANGED
@@ -45,9 +45,10 @@ const defaultVertex = () =>
45
45
  )
46
46
 
47
47
  export const createGL = (props?: Partial<GL>) => {
48
- const gl = event<Partial<GL>>({
48
+ const gl = event({
49
49
  isNative: false,
50
50
  isWebGL: true,
51
+ isError: false,
51
52
  isLoop: true,
52
53
  isGL: true,
53
54
  size: [0, 0],
@@ -55,6 +56,12 @@ export const createGL = (props?: Partial<GL>) => {
55
56
  count: 6,
56
57
  webgl: {},
57
58
  webgpu: {},
59
+ loading: 0,
60
+ error() {
61
+ gl.isError = true
62
+ gl.isLoop = false
63
+ gl.clean()
64
+ },
58
65
  }) as EventState<GL>
59
66
 
60
67
  gl.queue = createQueue()
@@ -72,10 +79,12 @@ export const createGL = (props?: Partial<GL>) => {
72
79
  if (gl.isWebGL) {
73
80
  gl((await webgl(gl)) as GL)
74
81
  } else gl((await webgpu(gl)) as GL)
82
+ if (gl.isError) return // stop if error
75
83
  gl.resize()
76
84
  gl.frame(() => {
77
85
  gl.loop()
78
86
  gl.queue.flush()
87
+ if (gl.loading) return true // wait for textures @TODO FIX
79
88
  gl.render()
80
89
  return gl.isLoop
81
90
  })
@@ -86,7 +95,6 @@ export const createGL = (props?: Partial<GL>) => {
86
95
 
87
96
  gl('clean', () => {
88
97
  gl.frame.stop()
89
- gl.frame.clean(gl.render)
90
98
  if (gl.isNative) return
91
99
  window.removeEventListener('resize', gl.resize)
92
100
  gl.el.removeEventListener('mousemove', gl.mousemove)
package/src/node/code.ts CHANGED
@@ -32,7 +32,7 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
32
32
  if (is.bol(target)) return target ? 'true' : 'false'
33
33
  if (!target) return ''
34
34
  const { type, props } = target
35
- const { id = '', children = [], fields, initialValues } = props
35
+ const { id = 'i', children = [], fields, initialValues } = props
36
36
  const [x, y, z, w] = children
37
37
  /**
38
38
  * variables
@@ -49,8 +49,10 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
49
49
  return `(${code(y, c)} ${getOperator(x)} ${code(z, c)})`
50
50
  }
51
51
  if (type === 'function') {
52
- if (x === 'negate') return `(-${parseArray(children.slice(1), c)})`
52
+ if (x === 'negate') return `(-${code(y, c)})`
53
+ if (x === 'oneMinus') return `(1.0-${code(y, c)})`
53
54
  if (x === 'texture') return parseTexture(c, y, z, w)
55
+ if (x === 'atan2' && c.isWebGL) return `atan(${code(y, c)}, ${code(z, c)})`
54
56
  return `${x}(${parseArray(children.slice(1), c)})`
55
57
  }
56
58
  /**
@@ -61,8 +63,8 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
61
63
  if (type === 'return') return `return ${code(x, c)};`
62
64
  if (type === 'loop')
63
65
  return c.isWebGL
64
- ? `for (int i = 0; i < ${code(x, c)}; i += 1) {\n${code(y, c)}\n}`
65
- : `for (var i: i32 = 0; i < ${code(x, c)}; i++) {\n${code(y, c)}\n}`
66
+ ? `for (int ${id} = 0; ${id} < ${code(x, c)}; ${id} += 1) {\n${code(y, c)}\n}`
67
+ : `for (var ${id}: i32 = 0; ${id} < ${code(x, c)}; ${id}++) {\n${code(y, c)}\n}`
66
68
  if (type === 'if') return parseIf(c, x, y, children)
67
69
  if (type === 'switch') return parseSwitch(c, x, children)
68
70
  if (type === 'declare') return parseDeclare(c, x, y)
package/src/node/index.ts CHANGED
@@ -2,7 +2,7 @@ import { is } from '../utils/helpers'
2
2
  import { code } from './code'
3
3
  import { builtin, conversion as c, function_ as f, uniform as u } from './node'
4
4
  import { hex2rgb, sortHeadersByDependencies } from './utils'
5
- import type { Constants as C, NodeContext, X, Vec2, Float } from './types'
5
+ import type { Constants as C, NodeContext, X, Vec2, Float, NodeProxy } from './types'
6
6
  export * from './code'
7
7
  export * from './node'
8
8
  export * from './scope'
@@ -56,7 +56,7 @@ export const vertex = (x: X, c: NodeContext) => {
56
56
  }
57
57
  ret.push('}')
58
58
  const main = ret.filter(Boolean).join('\n').trim()
59
- console.log(`↓↓↓generated↓↓↓\n${main}`)
59
+ // console.log(`↓↓↓generated↓↓↓\n${main}`)
60
60
  return main
61
61
  }
62
62
 
@@ -79,7 +79,7 @@ export const fragment = (x: X, c: NodeContext) => {
79
79
  }
80
80
  ret.push('}')
81
81
  const main = ret.filter(Boolean).join('\n').trim()
82
- console.log(`↓↓↓generated↓↓↓\n${main}`)
82
+ // console.log(`↓↓↓generated↓↓↓\n${main}`)
83
83
  return main
84
84
  }
85
85
 
@@ -219,3 +219,4 @@ export const difference = <T extends C>(x: X<T>, y: X) => f<T>('difference', x,
219
219
  export const equals = (x: X, y: X) => f<'bool'>('equals', x, y)
220
220
  export const faceforward = <T extends C>(N: X<T>, I: X, Nref: X) => f<T>('faceforward', N, I, Nref)
221
221
  export const transformDirection = <T extends C>(dir: X<T>, matrix: X) => f<T>('transformDirection', dir, matrix)
222
+ export const mod = <T extends C>(x: NodeProxy<T>, y: X<T>) => x.sub(x.div(y).floor().mul(y))
package/src/node/parse.ts CHANGED
@@ -58,37 +58,6 @@ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
58
58
  return `var ${varName}: ${wgslType} = ${code(x, c)};`
59
59
  }
60
60
 
61
- export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
62
- const { id, children = [], layout } = props
63
- const [x, ...args] = children
64
- const argParams: [name: string, type: string][] = []
65
- const params: string[] = []
66
- if (layout?.inputs)
67
- for (const input of layout.inputs) {
68
- argParams.push([input.name, input.type])
69
- }
70
- else
71
- for (let i = 0; i < args.length; i++) {
72
- argParams.push([`p${i}`, infer(args[i], c)])
73
- }
74
- const ret = []
75
- if (c?.isWebGL) {
76
- for (const [paramId, type] of argParams) {
77
- addDependency(c, id!, type)
78
- params.push(`${type} ${paramId}`)
79
- }
80
- addDependency(c, id!, returnType)
81
- ret.push(`${returnType} ${id}(${params}) {`)
82
- } else {
83
- for (const [paramId, type] of argParams) params.push(`${paramId}: ${formatConversions(type, c)}`)
84
- ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
85
- }
86
- const scopeCode = code(x, c)
87
- if (scopeCode) ret.push(scopeCode)
88
- ret.push('}')
89
- return ret.join('\n')
90
- }
91
-
92
61
  export const parseStructHead = (c: NodeContext, id: string, fields: Record<string, NodeProxy> = {}) => {
93
62
  const lines: string[] = []
94
63
  for (const key in fields) {
@@ -123,6 +92,40 @@ export const parseStruct = (
123
92
  }
124
93
  }
125
94
 
95
+ /**
96
+ * define
97
+ */
98
+ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
99
+ const { id, children = [], layout } = props
100
+ const [x, ...args] = children
101
+ const argParams: [name: string, type: string][] = []
102
+ const params: string[] = []
103
+ if (layout?.inputs)
104
+ for (const input of layout.inputs) {
105
+ argParams.push([input.name, input.type])
106
+ }
107
+ else
108
+ for (let i = 0; i < args.length; i++) {
109
+ argParams.push([`p${i}`, infer(args[i], c)])
110
+ }
111
+ const ret = []
112
+ if (c?.isWebGL) {
113
+ for (const [paramId, type] of argParams) {
114
+ addDependency(c, id!, type)
115
+ params.push(`${type} ${paramId}`)
116
+ }
117
+ addDependency(c, id!, returnType)
118
+ ret.push(`${returnType} ${id}(${params}) {`)
119
+ } else {
120
+ for (const [paramId, type] of argParams) params.push(`${paramId}: ${formatConversions(type, c)}`)
121
+ ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
122
+ }
123
+ const scopeCode = code(x, c)
124
+ if (scopeCode) ret.push(scopeCode)
125
+ ret.push('}')
126
+ return ret.join('\n')
127
+ }
128
+
126
129
  /**
127
130
  * headers
128
131
  */
package/src/node/scope.ts CHANGED
@@ -82,8 +82,9 @@ export const If = (x: NodeProxy, fun: () => void) => {
82
82
 
83
83
  export const Loop = (x: NodeProxy, fun: (params: { i: Int }) => void) => {
84
84
  const y = node('scope')
85
- scoped(y, () => fun({ i: node<'int'>('variable', { id: 'i', inferFrom: [conversion('int', 0)] }) }))
86
- const ret = node('loop', null, x, y)
85
+ const id = getId()
86
+ scoped(y, () => fun({ i: node<'int'>('variable', { id, inferFrom: [conversion('int', 0)] }) }))
87
+ const ret = node('loop', { id }, x, y)
87
88
  addToScope(ret)
88
89
  return ret
89
90
  }
package/src/node/utils.ts CHANGED
@@ -46,7 +46,7 @@ export const hex2rgb = (hex: number) => {
46
46
 
47
47
  let count = 0
48
48
 
49
- export const getId = () => `i${count++}`
49
+ export const getId = () => `x${count++}`
50
50
 
51
51
  export const formatConversions = <T extends Constants>(x: X<T>, c?: NodeContext) => {
52
52
  if (!is.str(x)) return ''
@@ -79,7 +79,7 @@ export const getEventFun = (c: NodeContext, id: string, isAttribute = false, isT
79
79
  }
80
80
 
81
81
  export const safeEventCall = <T extends Constants>(x: X<T>, fun: (value: unknown) => void) => {
82
- if (!x) return
82
+ if (is.und(x)) return
83
83
  if (!isNodeProxy(x)) return fun(x) // for uniform(1)
84
84
  if (x.type !== 'conversion') return
85
85
  const value = x.props.children?.slice(1).filter(Boolean)
package/src/types.ts CHANGED
@@ -59,20 +59,22 @@ export type GL = EventState<{
59
59
  */
60
60
  isNative: boolean
61
61
  isWebGL: boolean
62
+ isError: boolean
62
63
  isLoop: boolean
63
64
  isGL: true
64
- width: number
65
- height: number
65
+ width?: number
66
+ height?: number
66
67
  size: [number, number]
67
68
  mouse: [number, number]
68
69
  count: number
70
+ loading: number
69
71
  el: HTMLCanvasElement
70
- vs: string | Vec4
71
- fs: string | Vec4
72
- vert: string | Vec4
73
- frag: string | Vec4
74
- vertex: string | Vec4
75
- fragment: string | Vec4
72
+ vs?: string | Vec4
73
+ fs?: string | Vec4
74
+ vert?: string | Vec4
75
+ frag?: string | Vec4
76
+ vertex?: string | Vec4
77
+ fragment?: string | Vec4
76
78
 
77
79
  /**
78
80
  * core state
@@ -86,13 +88,13 @@ export type GL = EventState<{
86
88
  * events
87
89
  */
88
90
  ref?: any
89
- init(): void
90
- loop(): void
91
91
  mount(): void
92
92
  clean(): void
93
+ error(e?: string): void
93
94
  render(): void
94
95
  resize(e?: Event): void
95
96
  mousemove(e: Event): void
97
+ loop(): void
96
98
 
97
99
  /**
98
100
  * setter
@@ -1,27 +1,26 @@
1
- const createShader = (c: WebGLRenderingContext, source: string, type: number) => {
1
+ const createShader = (c: WebGLRenderingContext, source: string, type: number, onError = console.warn) => {
2
2
  const shader = c.createShader(type)
3
- if (!shader) throw new Error('Failed to create shader')
3
+ if (!shader) return onError('Failed to create shader')
4
4
  c.shaderSource(shader, source.trim())
5
5
  c.compileShader(shader)
6
6
  if (c.getShaderParameter(shader, c.COMPILE_STATUS)) return shader
7
7
  const error = c.getShaderInfoLog(shader)
8
8
  c.deleteShader(shader)
9
- console.warn(`Could not compile shader: ${error}`)
9
+ onError(`Could not compile shader: ${error}`)
10
10
  }
11
11
 
12
- export const createProgram = (c: WebGLRenderingContext, vert: string, frag: string, onError = () => {}) => {
12
+ export const createProgram = (c: WebGLRenderingContext, vert: string, frag: string, onError = console.warn) => {
13
13
  const pg = c.createProgram()
14
- const fs = createShader(c, frag, c.FRAGMENT_SHADER)
15
- const vs = createShader(c, vert, c.VERTEX_SHADER)
16
- if (!fs || !vs) return onError()
17
- c.attachShader(pg, vs)
18
- c.attachShader(pg, fs)
14
+ const fs = createShader(c, frag, c.FRAGMENT_SHADER, onError)
15
+ const vs = createShader(c, vert, c.VERTEX_SHADER, onError)
16
+ if (!fs || !vs) return
17
+ c.attachShader(pg, vs!)
18
+ c.attachShader(pg, fs!)
19
19
  c.linkProgram(pg)
20
20
  if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
21
21
  const error = c.getProgramInfoLog(pg)
22
22
  c.deleteProgram(pg)
23
- onError()
24
- console.warn(`Could not link program: ${error}`)
23
+ onError(`Could not link program: ${error}`)
25
24
  }
26
25
 
27
26
  export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
package/src/webgl.ts CHANGED
@@ -4,24 +4,29 @@ import { is } from './utils/helpers'
4
4
  import { createAttrib, createIbo, createProgram, createTexture, createVbo, getStride } from './utils/program'
5
5
  import type { GL, WebGLState } from './types'
6
6
 
7
- export const webgl = async (gl: Partial<GL>) => {
7
+ export const webgl = async (gl: GL) => {
8
8
  const c = gl.el!.getContext('webgl2')!
9
9
  const config = { isWebGL: true, gl }
10
10
  const fs = fragment(gl.fs, config)
11
11
  const vs = vertex(gl.vs, config)
12
- const pg = createProgram(c, vs, fs, () => void (gl.isLoop = false))!
12
+ const pg = createProgram(c, vs, fs, gl.error)!
13
13
  c.useProgram(pg)
14
14
 
15
- let _activeUnit = 0
15
+ let activeUnit = 0
16
16
  const uniforms = cached((key) => c.getUniformLocation(pg, key))
17
17
  const attribs = cached((key) => c.getAttribLocation(pg, key))
18
- const units = cached(() => _activeUnit++)
18
+ const units = cached(() => activeUnit++)
19
19
 
20
- const clean = () => c.deleteProgram(pg)
20
+ const clean = () => {
21
+ c.deleteProgram(pg)
22
+ c.getExtension('WEBGL_lose_context')?.loseContext()
23
+ gl.el.width = 1
24
+ gl.el.height = 1
25
+ }
21
26
 
22
27
  const render = () => {
23
28
  c.clear(c.COLOR_BUFFER_BIT)
24
- c.viewport(0, 0, ...gl.size!)
29
+ c.viewport(0, 0, ...gl.size)
25
30
  c.drawArrays(c.TRIANGLES, 0, 3)
26
31
  }
27
32
 
@@ -29,7 +34,7 @@ export const webgl = async (gl: Partial<GL>) => {
29
34
  const loc = attribs(key, true)
30
35
  const vbo = createVbo(c, value)
31
36
  const ibo = createIbo(c, iboValue)
32
- const str = getStride(gl.count!, value, iboValue)
37
+ const str = getStride(gl.count, value, iboValue)
33
38
  createAttrib(c, str, loc, vbo, ibo)
34
39
  }
35
40
 
@@ -43,14 +48,18 @@ export const webgl = async (gl: Partial<GL>) => {
43
48
  }
44
49
 
45
50
  const _texture = (key: string, src: string) => {
51
+ gl.loading++
46
52
  const image = new Image()
47
53
  Object.assign(image, { src, crossOrigin: 'anonymous' })
48
54
  image.decode().then(() => {
49
55
  const loc = uniforms(key)
50
56
  const unit = units(key)
51
57
  createTexture(c, image, loc, unit)
58
+ gl.loading--
52
59
  })
53
60
  }
54
61
 
55
- return { render, clean, _attribute, _uniform, _texture, webgl: { context: c, program: pg } as WebGLState }
62
+ const webgl: WebGLState = { context: c, program: pg }
63
+
64
+ return { webgl, render, clean, _attribute, _uniform, _texture }
56
65
  }
package/src/webgpu.ts CHANGED
@@ -15,14 +15,14 @@ import {
15
15
  import type { GL, WebGPUState } from './types'
16
16
  import { fragment, vertex } from './node'
17
17
 
18
- export const webgpu = async (gl: Partial<GL>) => {
18
+ export const webgpu = async (gl: GL) => {
19
19
  const context = gl.el!.getContext('webgpu') as GPUCanvasContext
20
20
  const { device, format } = await createDevice(context)
21
+ device.onuncapturederror = (e) => gl.error(e.error.message)
21
22
  const bindings = createBindings()
22
23
  let frag: string
23
24
  let vert: string
24
25
  let flush = (_pass: GPURenderPassEncoder) => {}
25
- let imageLoading = 0
26
26
  let needsUpdate = true
27
27
  let depthTexture: GPUTexture
28
28
 
@@ -42,7 +42,7 @@ export const webgpu = async (gl: Partial<GL>) => {
42
42
 
43
43
  const attribs = cached((_key, value: number[]) => {
44
44
  needsUpdate = true
45
- const stride = value.length / gl.count!
45
+ const stride = value.length / gl.count
46
46
  const { location } = bindings.attrib()
47
47
  const { array, buffer } = createAttribBuffer(device, value)
48
48
  return { array, buffer, location, stride }
@@ -60,7 +60,7 @@ export const webgpu = async (gl: Partial<GL>) => {
60
60
  pass.setPipeline(pipeline)
61
61
  bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
62
62
  vertexBuffers.forEach((v, i) => pass.setVertexBuffer(i, v))
63
- pass.draw(gl.count!, 1, 0, 0)
63
+ pass.draw(gl.count, 1, 0, 0)
64
64
  pass.end()
65
65
  }
66
66
  }
@@ -71,7 +71,7 @@ export const webgpu = async (gl: Partial<GL>) => {
71
71
  frag = fragment(gl.fs, config)
72
72
  vert = vertex(gl.vs, config)
73
73
  }
74
- if (imageLoading) return // MEMO: loading after build node
74
+ if (gl.loading) return // MEMO: loading after build node
75
75
  if (needsUpdate) update()
76
76
  needsUpdate = false
77
77
  const encoder = device.createCommandEncoder()
@@ -86,7 +86,11 @@ export const webgpu = async (gl: Partial<GL>) => {
86
86
  }
87
87
 
88
88
  const clean = () => {
89
+ device.destroy()
89
90
  depthTexture?.destroy()
91
+ for (const { texture } of textures.map.values()) texture.destroy()
92
+ for (const { buffer } of uniforms.map.values()) buffer.destroy()
93
+ for (const { buffer } of attribs.map.values()) buffer.destroy()
90
94
  }
91
95
 
92
96
  const _attribute = (key = '', value: number[]) => {
@@ -103,13 +107,13 @@ export const webgpu = async (gl: Partial<GL>) => {
103
107
  }
104
108
 
105
109
  const _texture = (key: string, src: string) => {
106
- imageLoading++
110
+ gl.loading++
107
111
  const source = Object.assign(new Image(), { src, crossOrigin: 'anonymous' })
108
112
  source.decode().then(() => {
109
113
  const { width, height } = source
110
114
  const { texture } = textures(key, width, height)
111
115
  device.queue.copyExternalImageToTexture({ source }, { texture }, { width, height })
112
- imageLoading--
116
+ gl.loading--
113
117
  })
114
118
  }
115
119