glre 0.32.0 → 0.34.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/src/node/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { is } from '../utils/helpers'
2
- import { code } from './code'
3
2
  import {
4
3
  CONSTANTS,
5
4
  CONVERSIONS,
@@ -27,16 +26,15 @@ export const isConversion = (key: unknown): key is Conversions => {
27
26
  return CONVERSIONS.includes(key as Conversions)
28
27
  }
29
28
 
30
- export const isNodeProxy = (x: unknown): x is NodeProxy => {
29
+ export const isNodeProxy = <T extends Constants>(x: unknown): x is NodeProxy<T> => {
31
30
  if (!x) return false
32
31
  if (typeof x !== 'object') return false // @ts-ignore
33
32
  return x.isProxy
34
33
  }
35
34
 
36
- export const isConstantsType = (type?: Constants | 'auto'): type is Constants => {
37
- if (!type) return false
38
- if (type === 'auto') return false
39
- return true
35
+ export const isConstants = (type?: unknown): type is Constants => {
36
+ if (!is.str(type)) return false
37
+ return CONSTANTS.includes(type)
40
38
  }
41
39
 
42
40
  export const hex2rgb = (hex: number) => {
@@ -50,20 +48,13 @@ let count = 0
50
48
 
51
49
  export const getId = () => `i${count++}`
52
50
 
53
- export const joins = (children: X[], c: NodeContext) => {
54
- return children
55
- .filter((x) => !is.und(x) && !is.nul(x))
56
- .map((x) => code(x, c))
57
- .join(', ')
58
- }
59
-
60
- export const formatConversions = (x: X, c?: NodeContext) => {
51
+ export const formatConversions = <T extends Constants>(x: X<T>, c?: NodeContext) => {
61
52
  if (!is.str(x)) return ''
62
53
  if (c?.isWebGL) return x
63
- return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING]
54
+ return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING] || x // for struct type
64
55
  }
65
56
 
66
- export const getOperator = (op: X) => {
57
+ export const getOperator = (op: X<string>) => {
67
58
  return OPERATORS[op as keyof typeof OPERATORS] || op
68
59
  }
69
60
 
@@ -75,3 +66,64 @@ export const conversionToConstant = (conversionKey: string): Constants => {
75
66
  const index = CONVERSIONS.indexOf(conversionKey as Conversions)
76
67
  return index !== -1 ? CONSTANTS[index] : 'float'
77
68
  }
69
+
70
+ export const getEventFun = (c: NodeContext, id: string, isAttribute = false, isTexture = false) => {
71
+ if (c.isWebGL) {
72
+ if (isAttribute) return (value: any) => c.gl?.attribute?.(id, value)
73
+ if (isTexture) return (value: any) => c.gl?.texture?.(id, value)
74
+ return (value: any) => c.gl?.uniform?.(id, value)
75
+ }
76
+ if (isAttribute) return (value: any) => c.gl?._attribute?.(id, value)
77
+ if (isTexture) return (value: any) => c.gl?._texture?.(id, value)
78
+ return (value: any) => c.gl?._uniform?.(id, value)
79
+ }
80
+
81
+ export const safeEventCall = <T extends Constants>(x: X<T>, fun: (value: unknown) => void) => {
82
+ if (!x) return
83
+ if (!isNodeProxy(x)) return fun(x) // for uniform(1)
84
+ if (x.type !== 'conversion') return
85
+ const value = x.props.children?.slice(1).filter(Boolean)
86
+ if (!value?.length) return // for uniform(vec2())
87
+ fun(value)
88
+ }
89
+
90
+ export const initNodeContext = (c: NodeContext) => {
91
+ if (!c.code) {
92
+ c.code = {
93
+ headers: new Map(),
94
+ fragInputs: new Map(),
95
+ vertInputs: new Map(),
96
+ vertOutputs: new Map(),
97
+ vertVaryings: new Map(),
98
+ dependencies: new Map(),
99
+ }
100
+ if (!c.isWebGL) {
101
+ c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
102
+ c.code.vertOutputs.set('position', '@builtin(position) position: vec4f')
103
+ }
104
+ }
105
+ return c
106
+ }
107
+
108
+ export const addDependency = (c: NodeContext, id = '', type: string) => {
109
+ if (!c.code?.dependencies?.has(id)) c.code!.dependencies.set(id, new Set())
110
+ if (!isConstants(type)) c.code!.dependencies.get(id)!.add(type)
111
+ }
112
+
113
+ export const sortHeadersByDependencies = (headers: Map<string, string>, dependencies: Map<string, Set<string>>) => {
114
+ const sorted: [string, string][] = []
115
+ const visited = new Set<string>()
116
+ const visiting = new Set<string>()
117
+ const visit = (id: string) => {
118
+ if (visiting.has(id)) return
119
+ if (visited.has(id)) return
120
+ visiting.add(id)
121
+ const deps = dependencies.get(id) || new Set()
122
+ for (const dep of deps) if (headers.has(dep)) visit(dep)
123
+ visiting.delete(id)
124
+ visited.add(id)
125
+ if (headers.has(id)) sorted.push([id, headers.get(id)!])
126
+ }
127
+ for (const [id] of headers) visit(id)
128
+ return sorted
129
+ }
package/src/types.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { EventState, Nested } from 'reev'
2
2
  import type { Fun, Queue, Frame } from 'refr'
3
- import type { NodeProxy } from './node'
3
+ import type { NodeProxy, Vec4 } from './node'
4
4
  export type { Fun, Queue, Frame }
5
5
  export type PrecisionMode = 'highp' | 'mediump' | 'lowp'
6
6
  export type GLClearMode = 'COLOR_BUFFER_BIT' | 'DEPTH_BUFFER_BIT' | 'STENCIL_BUFFER_BIT'
@@ -26,6 +26,7 @@ export interface TextureData {
26
26
  group: number
27
27
  texture: GPUTexture
28
28
  sampler: GPUSampler
29
+ view: GPUTextureView
29
30
  }
30
31
 
31
32
  export interface AttribData {
@@ -66,12 +67,12 @@ export type GL = EventState<{
66
67
  mouse: [number, number]
67
68
  count: number
68
69
  el: HTMLCanvasElement
69
- vs: string | NodeProxy
70
- fs: string | NodeProxy
71
- vert: string | NodeProxy
72
- frag: string | NodeProxy
73
- vertex: string | NodeProxy
74
- fragment: string | NodeProxy
70
+ vs: string | Vec4
71
+ fs: string | Vec4
72
+ vert: string | Vec4
73
+ frag: string | Vec4
74
+ vertex: string | Vec4
75
+ fragment: string | Vec4
75
76
 
76
77
  /**
77
78
  * core state
@@ -1,7 +1,4 @@
1
- import { fragment, vertex } from '../node'
2
- import type { X } from '../node'
3
- import type { AttribData, TextureData, UniformData, WebGPUState } from '../types'
4
- import { is } from './helpers'
1
+ import type { AttribData, TextureData, UniformData } from '../types'
5
2
 
6
3
  /**
7
4
  * initialize
@@ -41,31 +38,82 @@ export const createBindings = () => {
41
38
  }
42
39
  }
43
40
 
41
+ /**
42
+ * pipeline update
43
+ */
44
+ const getVertexFormat = (stride: number): GPUVertexFormat => {
45
+ if (stride === 2) return 'float32x2'
46
+ if (stride === 3) return 'float32x3'
47
+ if (stride === 4) return 'float32x4'
48
+ return 'float32'
49
+ }
50
+
51
+ export const createVertexBuffers = (attribs: Iterable<AttribData>) => {
52
+ const vertexBuffers: GPUBuffer[] = []
53
+ const bufferLayouts: GPUVertexBufferLayout[] = []
54
+ for (const { buffer, location, stride } of attribs) {
55
+ vertexBuffers[location] = buffer
56
+ bufferLayouts[location] = {
57
+ arrayStride: stride * 4,
58
+ attributes: [
59
+ {
60
+ shaderLocation: location,
61
+ offset: 0,
62
+ format: getVertexFormat(stride),
63
+ },
64
+ ],
65
+ }
66
+ }
67
+ return { vertexBuffers, bufferLayouts }
68
+ }
69
+
70
+ export const createBindGroup = (
71
+ device: GPUDevice,
72
+ uniforms: Iterable<UniformData>,
73
+ textures: Iterable<TextureData>
74
+ ) => {
75
+ const groups = new Map<number, { layouts: GPUBindGroupLayoutEntry[]; bindings: GPUBindGroupEntry[] }>()
76
+ const ret = { bindGroups: [] as GPUBindGroup[], bindGroupLayouts: [] as GPUBindGroupLayout[] }
77
+ const add = (i: number, layout: GPUBindGroupLayoutEntry, binding: GPUBindGroupEntry) => {
78
+ if (!groups.has(i)) groups.set(i, { layouts: [], bindings: [] })
79
+ const { layouts, bindings } = groups.get(i)!
80
+ layouts.push(layout)
81
+ bindings.push(binding)
82
+ }
83
+ for (const { binding, buffer, group: i } of uniforms) {
84
+ add(i, { binding, visibility: 3, buffer: { type: 'uniform' } }, { binding, resource: { buffer } })
85
+ }
86
+ for (const { binding: b, group: i, sampler, view } of textures) {
87
+ add(i, { binding: b, visibility: 2, sampler: {} }, { binding: b, resource: sampler })
88
+ add(i, { binding: b + 1, visibility: 2, texture: {} }, { binding: b + 1, resource: view })
89
+ }
90
+ for (const [i, { layouts, bindings }] of groups) {
91
+ ret.bindGroupLayouts[i] = device.createBindGroupLayout({ entries: layouts })
92
+ ret.bindGroups[i] = device.createBindGroup({ layout: ret.bindGroupLayouts[i], entries: bindings })
93
+ }
94
+ return ret
95
+ }
96
+
44
97
  export const createPipeline = (
45
98
  device: GPUDevice,
46
99
  format: GPUTextureFormat,
47
100
  bufferLayouts: GPUVertexBufferLayout[],
48
101
  bindGroupLayouts: GPUBindGroupLayout[],
49
- webgpu: WebGPUState,
50
- vs: string | X,
51
- fs: string | X
102
+ vs: string,
103
+ fs: string
52
104
  ) => {
53
- const config = { isWebGL: false, webgpu }
54
- if (!is.str(fs)) fs = fragment(fs, config)
55
- if (!is.str(vs)) vs = vertex(vs, config)
56
- const layout = device.createPipelineLayout({ bindGroupLayouts })
57
105
  return device.createRenderPipeline({
58
106
  vertex: {
59
- module: device.createShaderModule({ code: vs.trim() }),
107
+ module: device.createShaderModule({ label: 'vert', code: vs }),
60
108
  entryPoint: 'main',
61
109
  buffers: bufferLayouts,
62
110
  },
63
111
  fragment: {
64
- module: device.createShaderModule({ code: fs.trim() }),
112
+ module: device.createShaderModule({ label: 'frag', code: fs }),
65
113
  entryPoint: 'main',
66
114
  targets: [{ format }],
67
115
  },
68
- layout,
116
+ layout: device.createPipelineLayout({ bindGroupLayouts }),
69
117
  primitive: { topology: 'triangle-list' },
70
118
  depthStencil: {
71
119
  depthWriteEnabled: true,
@@ -91,39 +139,6 @@ export const createAttribBuffer = (device: GPUDevice, value: number[]) => {
91
139
  return { array, buffer }
92
140
  }
93
141
 
94
- /**
95
- * uniforms
96
- */
97
- export const createBindGroup = (
98
- device: GPUDevice,
99
- uniforms: Map<string, UniformData>,
100
- textures: Map<string, TextureData>
101
- ) => {
102
- const groups = new Map<number, any>()
103
- const getGroup = (i = 0) => {
104
- if (!groups.has(i)) groups.set(i, { entries0: [], entries1: [] })
105
- return groups.get(i)
106
- }
107
- for (const { binding, buffer, group: i } of uniforms.values()) {
108
- const { entries0, entries1 } = getGroup(i)
109
- entries0.push({ binding, visibility: 3, buffer: { type: 'uniform' } })
110
- entries1.push({ binding, resource: { buffer } })
111
- }
112
- for (const { binding, group: i, sampler, texture } of textures.values()) {
113
- const { entries0, entries1 } = getGroup(i)
114
- entries0.push({ binding, visibility: 2, sampler: {} })
115
- entries0.push({ binding: binding + 1, visibility: 2, texture: {} })
116
- entries1.push({ binding, resource: sampler })
117
- entries1.push({ binding: binding + 1, resource: texture.createView() })
118
- }
119
- const ret = { bindGroups: [] as GPUBindGroup[], bindGroupLayouts: [] as GPUBindGroupLayout[] }
120
- for (const [i, { entries0, entries1 }] of groups) {
121
- ret.bindGroupLayouts[i] = device.createBindGroupLayout({ entries: entries0 })
122
- ret.bindGroups[i] = device.createBindGroup({ layout: ret.bindGroupLayouts[i], entries: entries1 })
123
- }
124
- return ret
125
- }
126
-
127
142
  export const createDescriptor = (c: GPUCanvasContext, depthTexture: GPUTexture) => {
128
143
  return {
129
144
  colorAttachments: [
@@ -143,6 +158,9 @@ export const createDescriptor = (c: GPUCanvasContext, depthTexture: GPUTexture)
143
158
  } as GPURenderPassDescriptor
144
159
  }
145
160
 
161
+ /**
162
+ * textures
163
+ */
146
164
  export const createTextureSampler = (device: GPUDevice, width = 1280, height = 800) => {
147
165
  const texture = device.createTexture({ size: [width, height], format: 'rgba8unorm', usage: 22 }) // 22 is GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
148
166
  const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' })
@@ -156,32 +174,3 @@ export const createDepthTexture = (device: GPUDevice, width: number, height: num
156
174
  usage: GPUTextureUsage.RENDER_ATTACHMENT,
157
175
  })
158
176
  }
159
-
160
- /**
161
- * attribs
162
- */
163
- const getVertexFormat = (stride: number): GPUVertexFormat => {
164
- if (stride === 2) return 'float32x2'
165
- if (stride === 3) return 'float32x3'
166
- if (stride === 4) return 'float32x4'
167
- return 'float32'
168
- }
169
-
170
- export const createVertexBuffers = (attribs: Map<string, AttribData>) => {
171
- const vertexBuffers: GPUBuffer[] = []
172
- const bufferLayouts: GPUVertexBufferLayout[] = []
173
- for (const [, { buffer, location, stride }] of attribs) {
174
- vertexBuffers[location] = buffer
175
- bufferLayouts[location] = {
176
- arrayStride: stride * 4,
177
- attributes: [
178
- {
179
- shaderLocation: location,
180
- offset: 0,
181
- format: getVertexFormat(stride),
182
- },
183
- ],
184
- }
185
- }
186
- return { vertexBuffers, bufferLayouts }
187
- }
@@ -1,7 +1,3 @@
1
- import { fragment, vertex } from '../node'
2
- import type { X } from '../node'
3
- import { is } from './helpers'
4
-
5
1
  const createShader = (c: WebGLRenderingContext, source: string, type: number) => {
6
2
  const shader = c.createShader(type)
7
3
  if (!shader) throw new Error('Failed to create shader')
@@ -13,22 +9,19 @@ const createShader = (c: WebGLRenderingContext, source: string, type: number) =>
13
9
  console.warn(`Could not compile shader: ${error}`)
14
10
  }
15
11
 
16
- export const createProgram = (c: WebGLRenderingContext, vs: X, fs: string | X, onError = () => {}, gl?: any) => {
17
- const config = { isWebGL: true, gl }
18
- if (!is.str(fs)) fs = fragment(fs, config)
19
- if (!is.str(vs)) vs = vertex(vs, config)
12
+ export const createProgram = (c: WebGLRenderingContext, vert: string, frag: string, onError = () => {}) => {
20
13
  const pg = c.createProgram()
21
- const _vs = createShader(c, vs, c.VERTEX_SHADER)
22
- const _fs = createShader(c, fs, c.FRAGMENT_SHADER)
23
- if (!_vs || !_fs) return onError()
24
- c.attachShader(pg, _vs)
25
- c.attachShader(pg, _fs)
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)
26
19
  c.linkProgram(pg)
27
20
  if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
28
21
  const error = c.getProgramInfoLog(pg)
29
22
  c.deleteProgram(pg)
30
23
  onError()
31
- console.warn(`Could not link pg: ${error}`)
24
+ console.warn(`Could not link program: ${error}`)
32
25
  }
33
26
 
34
27
  export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
package/src/webgl.ts CHANGED
@@ -1,12 +1,17 @@
1
1
  import { nested as cached } from 'reev'
2
+ import { fragment, vertex } from './node'
2
3
  import { is } from './utils/helpers'
3
4
  import { createAttrib, createIbo, createProgram, createTexture, createVbo, getStride } from './utils/program'
4
5
  import type { GL, WebGLState } from './types'
5
6
 
6
7
  export const webgl = async (gl: Partial<GL>) => {
7
8
  const c = gl.el!.getContext('webgl2')!
8
- const pg = createProgram(c, gl.vs, gl.fs, () => void (gl.isLoop = false), gl)!
9
+ const config = { isWebGL: true, gl }
10
+ const fs = fragment(gl.fs, config)
11
+ const vs = vertex(gl.vs, config)
12
+ const pg = createProgram(c, vs, fs, () => void (gl.isLoop = false))!
9
13
  c.useProgram(pg)
14
+
10
15
  let _activeUnit = 0
11
16
  const uniforms = cached((key) => c.getUniformLocation(pg, key))
12
17
  const attribs = cached((key) => c.getAttribLocation(pg, key))
package/src/webgpu.ts CHANGED
@@ -2,8 +2,8 @@ import { nested as cached } from 'reev'
2
2
  import { is } from './utils/helpers'
3
3
  import {
4
4
  createAttribBuffer,
5
- createBindGroup,
6
5
  createBindings,
6
+ createBindGroup,
7
7
  createDepthTexture,
8
8
  createDescriptor,
9
9
  createDevice,
@@ -13,28 +13,31 @@ import {
13
13
  createVertexBuffers,
14
14
  } from './utils/pipeline'
15
15
  import type { GL, WebGPUState } from './types'
16
+ import { fragment, vertex } from './node'
16
17
 
17
18
  export const webgpu = async (gl: Partial<GL>) => {
18
- const c = gl.el!.getContext('webgpu') as GPUCanvasContext
19
- const { device, format } = await createDevice(c)
19
+ const context = gl.el!.getContext('webgpu') as GPUCanvasContext
20
+ const { device, format } = await createDevice(context)
20
21
  const bindings = createBindings()
22
+ let frag: string
23
+ let vert: string
24
+ let flush = (_pass: GPURenderPassEncoder) => {}
21
25
  let imageLoading = 0
22
26
  let needsUpdate = true
23
27
  let depthTexture: GPUTexture
24
- let _render = (_pass: GPURenderPassEncoder) => {}
25
28
 
26
29
  const uniforms = cached((_key, value: number[]) => {
27
30
  needsUpdate = true
28
- const { group, binding } = bindings.uniform()
29
31
  const { array, buffer } = createUniformBuffer(device, value)
30
- return { array, buffer, binding, group }
32
+ const { binding, group } = bindings.uniform()
33
+ return { binding, group, array, buffer }
31
34
  })
32
35
 
33
- const textures = cached((_key, { width, height }: HTMLImageElement) => {
36
+ const textures = cached((_key, width = 0, height = 0) => {
34
37
  needsUpdate = true
35
- const { group, binding } = bindings.texture()
36
38
  const { texture, sampler } = createTextureSampler(device, width, height)
37
- return { texture, sampler, binding, group }
39
+ const { binding, group } = bindings.texture()
40
+ return { binding, group, texture, sampler, view: texture.createView() }
38
41
  })
39
42
 
40
43
  const attribs = cached((_key, value: number[]) => {
@@ -46,10 +49,14 @@ export const webgpu = async (gl: Partial<GL>) => {
46
49
  })
47
50
 
48
51
  const update = () => {
49
- const { vertexBuffers, bufferLayouts } = createVertexBuffers(attribs.map)
50
- const { bindGroups, bindGroupLayouts } = createBindGroup(device, uniforms.map, textures.map)
51
- const pipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, webgpu, gl.vs, gl.fs)
52
- _render = (pass) => {
52
+ const { vertexBuffers, bufferLayouts } = createVertexBuffers(attribs.map.values())
53
+ const { bindGroups, bindGroupLayouts } = createBindGroup(
54
+ device,
55
+ uniforms.map.values(),
56
+ textures.map.values()
57
+ )
58
+ const pipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, vert, frag)
59
+ flush = (pass) => {
53
60
  pass.setPipeline(pipeline)
54
61
  bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
55
62
  vertexBuffers.forEach((v, i) => pass.setVertexBuffer(i, v))
@@ -59,11 +66,16 @@ export const webgpu = async (gl: Partial<GL>) => {
59
66
  }
60
67
 
61
68
  const render = () => {
62
- if (imageLoading) return
69
+ if (!frag || !vert) {
70
+ const config = { isWebGL: false, gl }
71
+ frag = fragment(gl.fs, config)
72
+ vert = vertex(gl.vs, config)
73
+ }
74
+ if (imageLoading) return // MEMO: loading after build node
63
75
  if (needsUpdate) update()
64
76
  needsUpdate = false
65
77
  const encoder = device.createCommandEncoder()
66
- _render(encoder.beginRenderPass(createDescriptor(c, depthTexture)))
78
+ flush(encoder.beginRenderPass(createDescriptor(context, depthTexture)))
67
79
  device.queue.submit([encoder.finish()])
68
80
  }
69
81
 
@@ -94,19 +106,22 @@ export const webgpu = async (gl: Partial<GL>) => {
94
106
  imageLoading++
95
107
  const source = Object.assign(new Image(), { src, crossOrigin: 'anonymous' })
96
108
  source.decode().then(() => {
97
- const texture = textures(key, source)
98
- device.queue.copyExternalImageToTexture(
99
- { source },
100
- { texture: texture.texture },
101
- { width: source.width, height: source.height }
102
- )
109
+ const { width, height } = source
110
+ const { texture } = textures(key, width, height)
111
+ device.queue.copyExternalImageToTexture({ source }, { texture }, { width, height })
103
112
  imageLoading--
104
113
  })
105
114
  }
106
115
 
107
116
  resize()
108
117
 
109
- const webgpu = { device, uniforms, textures, attribs } as WebGPUState
110
-
111
- return { webgpu, render, resize, clean, _attribute, _uniform, _texture }
118
+ return {
119
+ webgpu: { device, uniforms, textures, attribs } as WebGPUState,
120
+ render,
121
+ resize,
122
+ clean,
123
+ _attribute,
124
+ _uniform,
125
+ _texture,
126
+ }
112
127
  }