glre 0.36.0 → 0.37.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/webgl.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  import { nested as cached } from 'reev'
2
2
  import { loadingImage } from './utils/helpers'
3
- import { createAttrib, createProgram, createStorage, createTexture, createUniform } from './utils/program'
3
+ import {
4
+ cleanStorage,
5
+ createAttachment,
6
+ createAttrib,
7
+ createProgram,
8
+ createStorage,
9
+ createTexture,
10
+ createUniform,
11
+ } from './utils/program'
12
+ import { compute, fragment, vertex } from './node'
4
13
  import type { GL, WebGLState } from './types'
5
14
 
6
15
  const vert = /* cpp */ `
@@ -11,100 +20,103 @@ void main() {
11
20
  gl_Position = vec4(x, y, 0.0, 1.0);
12
21
  }`.trim()
13
22
 
14
- export const webgl = async (gl: GL) => {
15
- const c = gl.el!.getContext('webgl2')!
23
+ const computeProgram = (gl: GL, c: WebGL2RenderingContext) => {
24
+ if (!gl.cs) return null // ignore if no compute shader
16
25
  c.getExtension('EXT_color_buffer_float')
17
- const pg1 = createProgram(c, gl.vs, gl.fs, gl)!
18
- const pg2 = createProgram(c, vert, gl.cs, gl)!
19
- c.useProgram(pg1)
20
26
 
21
27
  let activeUnit = 0 // for texture units
22
28
  let currentNum = 0 // for storage buffers
23
29
 
24
- const attribs = cached((key) => c.getAttribLocation(pg1, key))
25
- const uniforms1 = cached((key) => c.getUniformLocation(pg1, key))
26
- const uniforms2 = cached((key) => c.getUniformLocation(pg2, key))
27
- const textures = cached(() => activeUnit++)
28
- const storages = cached(() => {
29
- const unit = activeUnit++
30
- const a = { texture: c.createTexture(), buffer: c.createFramebuffer() }
31
- const b = { texture: c.createTexture(), buffer: c.createFramebuffer() }
32
- return { a, b, unit, width: 0, height: 0 }
30
+ const units = cached(() => activeUnit++)
31
+ const config = { isWebGL: true, gl, units }
32
+
33
+ const pg = createProgram(c, compute(gl.cs, config), vert, gl)!
34
+ const size = Math.ceil(Math.sqrt(gl.particles))
35
+
36
+ const uniforms = cached((key) => c.getUniformLocation(pg, key)!)
37
+ const storages = cached((key) => {
38
+ const array = new Float32Array(size * size * 4) // RGBA texture data
39
+ const ping = { texture: c.createTexture(), buffer: c.createFramebuffer() }
40
+ const pong = { texture: c.createTexture(), buffer: c.createFramebuffer() }
41
+ return { ping, pong, array, loc: uniforms(key), unit: units(key) }
33
42
  })
34
43
 
44
+ const _uniform = (key: string, value: number | number[]) => {
45
+ c.useProgram(pg)
46
+ createUniform(c, uniforms(key), value)
47
+ }
48
+
49
+ const _storage = (key: string, value: number[]) => {
50
+ const { ping, pong, unit, array } = storages(key)
51
+ createStorage(c, value, size, ping, pong, unit, array)
52
+ }
53
+
54
+ const clean = () => {
55
+ c.deleteProgram(pg)
56
+ cleanStorage(c, storages.map.values())
57
+ }
58
+
59
+ const render = () => {
60
+ c.useProgram(pg)
61
+ const attachments = storages.map.values().map(({ ping, pong, loc, unit }, index) => {
62
+ const [i, o] = currentNum % 2 ? [ping, pong] : [pong, ping]
63
+ return createAttachment(c, i, o, loc, unit, index)
64
+ })
65
+ c.drawBuffers(attachments)
66
+ c.drawArrays(c.TRIANGLES, 0, 3)
67
+ c.bindFramebuffer(c.FRAMEBUFFER, null)
68
+ currentNum++
69
+ }
70
+
71
+ return { render, clean, _uniform, _storage, storages }
72
+ }
73
+
74
+ export const webgl = async (gl: GL) => {
75
+ const config = { isWebGL: true, gl }
76
+ const c = gl.el!.getContext('webgl2')!
77
+ const cp = computeProgram(gl, c)
78
+ const pg = createProgram(c, fragment(gl.fs, config), vertex(gl.vs, config), gl)!
79
+ c.useProgram(pg)
80
+
81
+ let activeUnit = 0 // for texture units
82
+
83
+ const units = cached(() => activeUnit++)
84
+ const attribs = cached((key) => c.getAttribLocation(pg, key))
85
+ const uniforms = cached((key) => c.getUniformLocation(pg, key))
86
+
35
87
  const _attribute = (key = '', value: number[], iboValue: number[]) => {
36
88
  const loc = attribs(key, true)
37
89
  createAttrib(c, loc, gl.count, value, iboValue)
38
90
  }
39
91
 
40
92
  const _uniform = (key: string, value: number | number[]) => {
41
- createUniform(c, uniforms1(key)!, value)
42
- if (!pg2) return
43
- c.useProgram(pg2)
44
- createUniform(c, uniforms2(key)!, value)
45
- c.useProgram(pg1)
93
+ c.useProgram(pg)
94
+ createUniform(c, uniforms(key)!, value)
95
+ cp?._uniform(key, value)
46
96
  }
47
97
 
48
98
  const _texture = (key: string, src: string) => {
99
+ c.useProgram(pg)
49
100
  loadingImage(gl, src, (source) => {
50
- const loc = uniforms1(key)
51
- const unit = textures(key)
52
- createTexture(c, source, loc, unit)
101
+ createTexture(c, source, uniforms(key), units(key))
53
102
  })
54
103
  }
55
104
 
56
- const _storage = (key: string, value: number[] | Float32Array) => {
57
- const array = value instanceof Float32Array ? value : new Float32Array(value)
58
- const storage = storages(key)
59
- const size = Math.ceil(Math.sqrt(array.length))
60
- storage.width = size
61
- storage.height = size
62
- createStorage(c, size, storage, array)
63
- c.uniform1i(uniforms1(key), storage.unit)
64
- }
65
-
66
105
  const clean = () => {
67
- c.deleteProgram(pg1)
68
- if (pg2) c.deleteProgram(pg2)
69
- for (const { a, b } of storages.map.values()) {
70
- c.deleteTexture(a.texture)
71
- c.deleteTexture(b.texture)
72
- c.deleteFramebuffer(a.buffer)
73
- c.deleteFramebuffer(b.buffer)
74
- }
106
+ cp?.clean()
107
+ c.deleteProgram(pg)
75
108
  c.getExtension('WEBGL_lose_context')?.loseContext()
76
109
  }
77
110
 
78
- const _compute = () => {
79
- c.useProgram(pg2)
80
- for (const [, storage] of storages.map) {
81
- const output = currentNum % 2 ? storage.b : storage.a
82
- c.bindFramebuffer(c.FRAMEBUFFER, output.buffer)
83
- c.framebufferTexture2D(c.FRAMEBUFFER, c.COLOR_ATTACHMENT0, c.TEXTURE_2D, output.texture, 0)
84
- c.viewport(0, 0, storage.width, storage.height)
85
- c.drawArrays(c.TRIANGLES, 0, 6)
86
- c.bindFramebuffer(c.FRAMEBUFFER, null)
87
- }
88
- currentNum++
89
- c.useProgram(pg1)
90
- }
91
-
92
111
  const render = () => {
93
- if (pg2) _compute()
94
- c.bindFramebuffer(c.FRAMEBUFFER, null)
95
- c.clear(c.COLOR_BUFFER_BIT)
112
+ cp?.render()
113
+ c.useProgram(pg)
96
114
  c.viewport(0, 0, ...gl.size)
97
- for (const [key, { unit, a, b }] of storages.map) {
98
- const loc = uniforms1(key)
99
- const output = currentNum % 2 ? a : b
100
- c.activeTexture(c.TEXTURE0 + unit)
101
- c.bindTexture(c.TEXTURE_2D, output.texture)
102
- c.uniform1i(loc, unit)
103
- }
104
115
  c.drawArrays(c.TRIANGLES, 0, gl.count)
116
+ c.bindFramebuffer(c.FRAMEBUFFER, null)
105
117
  }
106
118
 
107
- const webgl: WebGLState = { context: c, program: pg1 }
119
+ const webgl: WebGLState = { context: c, program: pg, storages: cp?.storages }
108
120
 
109
- return { webgl, render, clean, _attribute, _uniform, _texture, _storage }
121
+ return { webgl, render, clean, _attribute, _uniform, _texture, _storage: cp?._storage }
110
122
  }
package/src/webgpu.ts CHANGED
@@ -2,8 +2,8 @@ import { nested as cached } from 'reev'
2
2
  import { is, loadingImage } from './utils/helpers'
3
3
  import {
4
4
  createArrayBuffer,
5
- createBindings,
6
5
  createBindGroup,
6
+ createBindings,
7
7
  createComputePipeline,
8
8
  createDepthTexture,
9
9
  createDescriptor,
@@ -15,15 +15,53 @@ import {
15
15
  import type { GL, WebGPUState } from './types'
16
16
  import { compute, fragment, vertex } from './node'
17
17
 
18
+ const WORKING_GROUP_SIZE = 32
19
+
20
+ const computeProgram = (gl: GL, device: GPUDevice, bindings: any) => {
21
+ let flush = (_pass: GPUComputePassEncoder) => {}
22
+
23
+ const storages = cached((_key, value: number[] | Float32Array) => {
24
+ const { array, buffer } = createArrayBuffer(device, value, 'storage')
25
+ const { binding, group } = bindings.storage()
26
+ return { array, buffer, binding, group }
27
+ })
28
+
29
+ const _storage = (key: string, value: number[] | Float32Array) => {
30
+ const { array, buffer } = storages(key, value)
31
+ device.queue.writeBuffer(buffer, 0, array as any)
32
+ }
33
+
34
+ const update = (bindGroups: GPUBindGroup[], bindGroupLayouts: GPUBindGroupLayout[], comp: string) => {
35
+ const pipeline = createComputePipeline(device, bindGroupLayouts, comp!)
36
+ flush = (pass) => {
37
+ pass.setPipeline(pipeline)
38
+ bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
39
+ const workgroupCount = Math.ceil(gl.particles / WORKING_GROUP_SIZE)
40
+ pass.dispatchWorkgroups(workgroupCount, 1, 1)
41
+ pass.end()
42
+ }
43
+ }
44
+
45
+ const render = (pass: GPUComputePassEncoder) => {
46
+ flush(pass)
47
+ }
48
+
49
+ const clean = () => {
50
+ for (const { buffer } of storages.map.values()) buffer.destroy()
51
+ }
52
+
53
+ return { storages, _storage, update, render, clean }
54
+ }
55
+
18
56
  export const webgpu = async (gl: GL) => {
19
57
  const context = gl.el!.getContext('webgpu') as GPUCanvasContext
20
58
  const { device, format } = await createDevice(context, gl.error)
21
59
  const bindings = createBindings()
60
+ const cp = computeProgram(gl, device, bindings)
22
61
  let frag: string
23
- let vert: string
24
62
  let comp: string
63
+ let vert: string
25
64
  let flush = (_pass: GPURenderPassEncoder) => {}
26
- let computeFlush = (_pass: GPUComputePassEncoder) => {}
27
65
  let needsUpdate = true
28
66
  let depthTexture: GPUTexture
29
67
 
@@ -35,13 +73,6 @@ export const webgpu = async (gl: GL) => {
35
73
  return { array, buffer, location, stride }
36
74
  })
37
75
 
38
- const storages = cached((_key, value: number[] | Float32Array) => {
39
- needsUpdate = true
40
- const { array, buffer } = createArrayBuffer(device, value, 'storage')
41
- const { binding, group } = bindings.storage()
42
- return { array, buffer, binding, group }
43
- })
44
-
45
76
  const uniforms = cached((_key, value: number[]) => {
46
77
  needsUpdate = true
47
78
  const { binding, group } = bindings.uniform()
@@ -61,14 +92,10 @@ export const webgpu = async (gl: GL) => {
61
92
  device.queue.writeBuffer(buffer, 0, array as any)
62
93
  }
63
94
 
64
- const _storage = (key: string, value: number[] | Float32Array) => {
65
- const { array, buffer } = storages(key, value)
66
- device.queue.writeBuffer(buffer, 0, array as any)
67
- }
68
-
69
95
  const _uniform = (key: string, value: number | number[]) => {
70
96
  if (is.num(value)) value = [value]
71
97
  const { array, buffer } = uniforms(key, value)
98
+ array.set(value) // needs to set leatest value
72
99
  device.queue.writeBuffer(buffer, 0, array as any)
73
100
  }
74
101
 
@@ -86,7 +113,7 @@ export const webgpu = async (gl: GL) => {
86
113
  device,
87
114
  uniforms.map.values(),
88
115
  textures.map.values(),
89
- storages.map.values()
116
+ cp.storages.map.values()
90
117
  )
91
118
  const pipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, vert, frag)
92
119
  flush = (pass) => {
@@ -96,33 +123,21 @@ export const webgpu = async (gl: GL) => {
96
123
  pass.draw(gl.count, 1, 0, 0)
97
124
  pass.end()
98
125
  }
99
- if (comp) {
100
- const computePipeline = createComputePipeline(device, bindGroupLayouts, comp)
101
- computeFlush = (pass) => {
102
- pass.setPipeline(computePipeline)
103
- bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
104
- let maxElements = 1
105
- for (const { array } of storages.map.values())
106
- maxElements = Math.max(maxElements, array.length)
107
- const workgroupCount = Math.ceil(maxElements / 64)
108
- pass.dispatchWorkgroups(workgroupCount)
109
- pass.end()
110
- }
111
- }
126
+ if (gl.cs) cp.update(bindGroups, bindGroupLayouts, comp)
112
127
  }
113
128
 
114
129
  const render = () => {
115
130
  if (!frag || !vert) {
116
131
  const config = { isWebGL: false, gl }
117
132
  frag = fragment(gl.fs, config) // needs to be before vertex
118
- comp = compute(gl.cs, config)
119
133
  vert = vertex(gl.vs, config)
134
+ comp = compute(gl.cs, config)
120
135
  }
121
136
  if (gl.loading) return // MEMO: loading after build node
122
137
  if (needsUpdate) update()
123
138
  needsUpdate = false
124
139
  const encoder = device.createCommandEncoder()
125
- if (comp) computeFlush(encoder.beginComputePass())
140
+ if (gl.cs) cp.render(encoder.beginComputePass())
126
141
  flush(encoder.beginRenderPass(createDescriptor(context, depthTexture)))
127
142
  device.queue.submit([encoder.finish()])
128
143
  }
@@ -139,19 +154,12 @@ export const webgpu = async (gl: GL) => {
139
154
  for (const { texture } of textures.map.values()) texture.destroy()
140
155
  for (const { buffer } of uniforms.map.values()) buffer.destroy()
141
156
  for (const { buffer } of attribs.map.values()) buffer.destroy()
142
- for (const { buffer } of storages.map.values()) buffer.destroy()
157
+ cp.clean()
143
158
  }
144
159
 
145
160
  resize()
146
161
 
147
- return {
148
- webgpu: { device, uniforms, textures, attribs, storages } as WebGPUState,
149
- render,
150
- resize,
151
- clean,
152
- _attribute,
153
- _uniform,
154
- _texture,
155
- _storage,
156
- }
162
+ const webgpu = { device, uniforms, textures, attribs, storages: cp.storages } as WebGPUState
163
+
164
+ return { webgpu, render, resize, clean, _attribute, _uniform, _texture, _storage: cp._storage }
157
165
  }