glre 0.45.0 → 0.46.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 +14 -14
  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 +1 -1
  37. package/src/node/utils/infer.ts +4 -12
  38. package/src/node/utils/parse.ts +18 -34
  39. package/src/node/utils/utils.ts +3 -3
  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
@@ -0,0 +1,56 @@
1
+ import { nested } from 'reev'
2
+ import { cleanStorage, createAttachment, createProgram, createStorage, storageSize, updateUniform } from './utils'
3
+ import { GLSL_VS, is } from '../helpers'
4
+ import type { GL } from '../types'
5
+
6
+ export const compute = (gl: GL) => {
7
+ if (!gl.cs) return
8
+ const c = gl.gl
9
+ c.getExtension('EXT_color_buffer_float') // Enable high precision GPGPU by writing to float textures
10
+
11
+ let _texture = 0 // for texture active units
12
+ let _storage = 0 // for storage current num
13
+
14
+ const units = nested(() => _texture++)
15
+ const cs = is.str(gl.cs) ? gl.cs : gl.cs!.compute({ isWebGL: true, gl, units })
16
+ const pg = createProgram(c, cs, GLSL_VS, gl)!
17
+ const size = storageSize(gl.particleCount)
18
+
19
+ const uniforms = nested((key) => c.getUniformLocation(pg, key)!)
20
+ const storages = nested((key) => {
21
+ const array = new Float32Array(size.x * size.y * 4) // RGBA texture data
22
+ const ping = { texture: c.createTexture(), buffer: c.createFramebuffer() }
23
+ const pong = { texture: c.createTexture(), buffer: c.createFramebuffer() }
24
+ return { ping, pong, array, loc: uniforms(key), unit: units(key) }
25
+ })
26
+
27
+ gl('_uniform', (key: string, value: number | number[]) => {
28
+ c.useProgram((gl.program = pg))
29
+ updateUniform(c, uniforms(key), value)
30
+ })
31
+
32
+ gl('_storage', (key: string, value: number[]) => {
33
+ c.useProgram((gl.program = pg))
34
+ const { ping, pong, unit, array } = storages(key)
35
+ createStorage(c, value, size.x, size.y, ping, pong, unit, array)
36
+ })
37
+
38
+ gl('clean', () => {
39
+ c.deleteProgram(pg)
40
+ cleanStorage(c, storages.map.values())
41
+ })
42
+
43
+ gl('render', () => {
44
+ c.useProgram((gl.program = pg))
45
+ const attachments = storages.map.values().map(({ ping, pong, loc, unit }, index) => {
46
+ const [i, o] = _storage % 2 ? [ping, pong] : [pong, ping]
47
+ return createAttachment(c, i, o, loc, unit, index)
48
+ })
49
+ c.drawBuffers(attachments)
50
+ c.drawArrays(c.TRIANGLES, 0, 3)
51
+ c.bindFramebuffer(c.FRAMEBUFFER, null)
52
+ _storage++
53
+ })
54
+ }
55
+
56
+ export type WebGLCompute = ReturnType<typeof compute>
@@ -0,0 +1,65 @@
1
+ import { nested } from 'reev'
2
+ import { getStride, GLSL_FS, GLSL_VS, is, loadingTexture } from '../helpers'
3
+ import { createBuffer, createProgram, createTexture, updateAttrib, updateBuffer, updateInstance, updateUniform } from './utils'
4
+ import type { GL } from '../types'
5
+
6
+ export const graphic = (gl: GL) => {
7
+ const config = { isWebGL: true, gl }
8
+ const c = gl.gl
9
+ const fs = gl.fs ? (is.str(gl.fs) ? gl.fs : gl.fs.fragment(config)) : GLSL_FS
10
+ const vs = gl.vs ? (is.str(gl.vs) ? gl.vs : gl.vs.vertex(config)) : GLSL_VS
11
+ const pg = createProgram(c, fs, vs, gl)!
12
+ let activeUnit = 0
13
+
14
+ const units = nested(() => activeUnit++)
15
+ const uniforms = nested((key) => c.getUniformLocation(pg, key))
16
+ const attributes = nested((key, value: number[], isInstance = false) => {
17
+ const stride = getStride(value.length, isInstance ? gl.instanceCount : gl.triangleCount, gl.error)
18
+ return { stride, location: c.getAttribLocation(pg, key), ...createBuffer(c, value) }
19
+ })
20
+
21
+ gl('_attribute', (key: string, value: number[]) => {
22
+ c.useProgram((gl.program = pg))
23
+ const a = attributes(key, value)
24
+ updateBuffer(c, a.array, a.buffer, value)
25
+ updateAttrib(c, a.location, a.stride, a.buffer)
26
+ })
27
+
28
+ gl('_instance', (key: string, value: number[]) => {
29
+ c.useProgram((gl.program = pg))
30
+ const a = attributes(key, value, true)
31
+ updateBuffer(c, a.array, a.buffer, value)
32
+ updateInstance(c, a.location, a.stride, a.buffer)
33
+ })
34
+
35
+ gl('_uniform', (key: string, value: number | number[]) => {
36
+ c.useProgram((gl.program = pg))
37
+ updateUniform(c, uniforms(key), value)
38
+ })
39
+
40
+ gl('_texture', (key: string, src: string) => {
41
+ c.useProgram((gl.program = pg))
42
+ const location = uniforms(key)
43
+ const unit = units(key)
44
+ createTexture(c, null, location, unit, false)
45
+ loadingTexture(src, (source, isVideo) => {
46
+ c.useProgram((gl.program = pg))
47
+ const render = createTexture(c, source, location, unit, isVideo)
48
+ if (render) gl({ render })
49
+ })
50
+ })
51
+
52
+ gl('clean', () => {
53
+ c.deleteProgram(pg)
54
+ })
55
+
56
+ gl('render', () => {
57
+ c.useProgram((gl.program = pg))
58
+ if (gl.instanceCount > 1) {
59
+ c.drawArraysInstanced(c.TRIANGLES, 0, gl.triangleCount, gl.instanceCount)
60
+ } else c.drawArrays(c.TRIANGLES, 0, gl.triangleCount)
61
+ c.bindFramebuffer(c.FRAMEBUFFER, null)
62
+ })
63
+ }
64
+
65
+ export type WebGLGraphic = ReturnType<typeof graphic>
@@ -0,0 +1,21 @@
1
+ import { compute } from './compute'
2
+ import { graphic } from './graphic'
3
+ import { enableDepth, enableWireframe, loseContext } from './utils'
4
+ import type { GL } from '../types'
5
+
6
+ export const webgl = (gl: GL) => {
7
+ const isInit = !gl.gl
8
+ if (isInit) {
9
+ const c = (gl.gl = gl.el.getContext('webgl2')!)
10
+ gl('render', () => c.viewport(0, 0, ...gl.size!)) // Run before other renderers' events to prevent flickering
11
+ }
12
+
13
+ compute(gl)
14
+ graphic(gl)
15
+
16
+ if (isInit) {
17
+ gl('clean', () => loseContext(gl.gl))
18
+ if (gl.isDepth) enableDepth(gl.gl)
19
+ if (gl.wireframe) enableWireframe(gl.gl)
20
+ }
21
+ }
@@ -1,4 +1,4 @@
1
- import { is } from './helpers'
1
+ import { is } from '../helpers'
2
2
  import type { GL } from '../types'
3
3
 
4
4
  const createShader = (c: WebGL2RenderingContext, source: string, type: number, onError = console.warn) => {
@@ -26,13 +26,13 @@ export const createProgram = (c: WebGL2RenderingContext, frag: string, vert: str
26
26
  gl.error(`Could not link program: ${error}`)
27
27
  }
28
28
 
29
- export const createArrayBuffer = (c: WebGL2RenderingContext, data: number[]) => {
29
+ export const createBuffer = (c: WebGL2RenderingContext, data: number[]) => {
30
30
  const array = new Float32Array(data)
31
31
  const buffer = c.createBuffer()
32
32
  return { array, buffer }
33
33
  }
34
34
 
35
- export const setArrayBuffer = (c: WebGL2RenderingContext, array: Float32Array, buffer: WebGLBuffer, value: number[]) => {
35
+ export const updateBuffer = (c: WebGL2RenderingContext, array: Float32Array, buffer: WebGLBuffer, value: number[]) => {
36
36
  array.set(value)
37
37
  c.bindBuffer(c.ARRAY_BUFFER, buffer)
38
38
  c.bufferData(c.ARRAY_BUFFER, array, c.STATIC_DRAW)
@@ -52,7 +52,8 @@ export const updateInstance = (c: WebGL2RenderingContext, loc: number, stride: n
52
52
  c.vertexAttribDivisor(loc, 1) // divisor is 1
53
53
  }
54
54
 
55
- export const updateUniform = (c: WebGL2RenderingContext, loc: WebGLUniformLocation, value: number | number[]) => {
55
+ export const updateUniform = (c: WebGL2RenderingContext, loc: WebGLUniformLocation | null, value: number | number[]) => {
56
+ if (is.nul(loc)) return
56
57
  if (is.num(value)) return c.uniform1f(loc, value)
57
58
  let l = value.length
58
59
  if (l <= 4) return c[`uniform${l as 2}fv`](loc, value)
@@ -60,11 +61,15 @@ export const updateUniform = (c: WebGL2RenderingContext, loc: WebGLUniformLocati
60
61
  c[`uniformMatrix${l as 2}fv`](loc, false, value)
61
62
  }
62
63
 
63
- export const createTexture = (c: WebGL2RenderingContext, el: HTMLImageElement | HTMLVideoElement, loc: WebGLUniformLocation, unit: number, isVideo = false) => {
64
+ export const createTexture = (c: WebGL2RenderingContext, el: HTMLImageElement | HTMLVideoElement | null, loc: WebGLUniformLocation | null, unit: number, isVideo = false) => {
64
65
  const texture = c.createTexture()
65
66
  c.bindTexture(c.TEXTURE_2D, texture)
66
- c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, el)
67
- if (!isVideo) c.generateMipmap(c.TEXTURE_2D)
67
+ if (el) {
68
+ c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, el)
69
+ if (!isVideo) c.generateMipmap(c.TEXTURE_2D)
70
+ } else {
71
+ c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, 1, 1, 0, c.RGBA, c.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]))
72
+ }
68
73
  c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.LINEAR)
69
74
  c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.LINEAR)
70
75
  c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
@@ -77,7 +82,7 @@ export const createTexture = (c: WebGL2RenderingContext, el: HTMLImageElement |
77
82
  return () => {
78
83
  c.activeTexture(c.TEXTURE0 + unit)
79
84
  c.bindTexture(c.TEXTURE_2D, texture)
80
- c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, el)
85
+ c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, el!)
81
86
  }
82
87
  }
83
88
 
@@ -149,3 +154,20 @@ export const storageSize = (particleCount: number | number[] = 1024) => {
149
154
  }
150
155
  return { x, y }
151
156
  }
157
+
158
+ export const loseContext = (c: WebGL2RenderingContext) => {
159
+ const ext = c.getExtension('WEBGL_lose_context')
160
+ if (ext) ext.loseContext()
161
+ }
162
+
163
+ export const enableDepth = (c: WebGL2RenderingContext) => {
164
+ c.enable(c.DEPTH_TEST)
165
+ c.depthFunc(c.LEQUAL)
166
+ c.enable(c.CULL_FACE)
167
+ c.cullFace(c.BACK)
168
+ }
169
+
170
+ export const enableWireframe = (c: WebGL2RenderingContext) => {
171
+ const ext = c.getExtension('WEBGL_polygon_mode')
172
+ if (ext) ext.polygonModeWEBGL(c.FRONT_AND_BACK, ext.LINE_WEBGL)
173
+ }
@@ -0,0 +1,39 @@
1
+ import { nested } from 'reev'
2
+ import { createBuffer, updateBuffer, workgroupCount } from './utils'
3
+ import type { Binding } from './utils'
4
+ import type { GL } from '../types'
5
+
6
+ export const compute = (gl: GL, bindings: Binding) => {
7
+ let pipeline: GPUComputePipeline | undefined
8
+ let bindGroups: GPUBindGroup[] | undefined
9
+
10
+ const storages = nested((key, value: number[] | Float32Array) => {
11
+ return { ...bindings.storage(key), ...createBuffer(gl.device, value, 'storage') }
12
+ })
13
+
14
+ gl('_storage', (key: string, value: number[] | Float32Array) => {
15
+ const { array, buffer } = storages(key, value)
16
+ updateBuffer(gl.device, value, array, buffer)
17
+ })
18
+
19
+ gl('render', () => {
20
+ if (!pipeline || !bindGroups) return
21
+ const pass = gl.encoder.beginComputePass()
22
+ pass.setPipeline(pipeline)
23
+ bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
24
+ const { x, y, z } = workgroupCount(gl.particleCount)
25
+ pass.dispatchWorkgroups(x, y, z)
26
+ pass.end()
27
+ })
28
+
29
+ gl('clean', () => {
30
+ for (const { buffer } of storages.map.values()) buffer.destroy()
31
+ })
32
+
33
+ const set = (_pipeline?: GPUComputePipeline, _bindGroups?: GPUBindGroup[]) => {
34
+ pipeline = _pipeline
35
+ bindGroups = _bindGroups
36
+ }
37
+
38
+ return { storages, set }
39
+ }
@@ -0,0 +1,89 @@
1
+ import { nested } from 'reev'
2
+ import { is, getStride, loadingTexture } from '../helpers'
3
+ import { createBuffer, createDepthTexture, createDescriptor, createTextureSampler, updateBuffer } from './utils'
4
+ import type { Binding } from './utils'
5
+ import type { GL } from '../types'
6
+
7
+ export const graphic = (gl: GL, bindings: Binding, update = () => {}) => {
8
+ let pipeline: GPURenderPipeline
9
+ let bindGroups: GPUBindGroup[]
10
+ let vertexBuffers: GPUBuffer[]
11
+ let depthTexture: GPUTexture
12
+
13
+ const attributes = nested((key, value: number[], isInstance = false, stride = getStride(value.length, isInstance ? gl.instanceCount : gl.triangleCount)) => {
14
+ update()
15
+ return { ...bindings.attrib(key), ...createBuffer(gl.device, value, 'attrib'), isInstance, stride }
16
+ })
17
+
18
+ const uniforms = nested((key, value: number[] | Float32Array) => {
19
+ update()
20
+ return { ...bindings.uniform(key), ...createBuffer(gl.device, value, 'uniform') }
21
+ })
22
+
23
+ const textures = nested((key, width = 1, height = 1) => {
24
+ update()
25
+ return { ...bindings.texture(key), ...createTextureSampler(gl.device, width, height) }
26
+ })
27
+
28
+ gl('_attribute', (key: string, value: number[] | Float32Array) => {
29
+ const a = attributes(key, value)
30
+ updateBuffer(gl.device, value, a.array, a.buffer)
31
+ })
32
+
33
+ gl('_instance', (key: string, value: number[] | Float32Array) => {
34
+ const a = attributes(key, value, true)
35
+ updateBuffer(gl.device, value, a.array, a.buffer)
36
+ })
37
+
38
+ gl('_uniform', (key: string, value: number | number[] | Float32Array) => {
39
+ if (is.num(value)) value = [value]
40
+ const u = uniforms(key, value)
41
+ updateBuffer(gl.device, value, u.array, u.buffer)
42
+ })
43
+
44
+ gl('_texture', (key: string, src: string) => {
45
+ const t = textures(key)
46
+ loadingTexture(src, (source, isVideo) => {
47
+ const [width, height] = isVideo ? [source.videoWidth, source.videoHeight] : [source.width, source.height]
48
+ if (t.texture.width !== width || t.texture.height !== height) {
49
+ t.texture.destroy()
50
+ Object.assign(t, createTextureSampler(gl.device, width, height))
51
+ update() // Rebuilding BindGroups because the texture size has changed
52
+ }
53
+ const render = () => void gl.device.queue.copyExternalImageToTexture({ source }, { texture: t.texture }, { width, height })
54
+ if (isVideo) gl({ render })
55
+ else render()
56
+ })
57
+ })
58
+
59
+ gl('render', () => {
60
+ if (!pipeline || !bindGroups || !vertexBuffers) return
61
+ const pass = gl.encoder.beginRenderPass(createDescriptor(gl.gpu, depthTexture))
62
+ pass.setPipeline(pipeline)
63
+ bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
64
+ vertexBuffers.forEach((v, i) => pass.setVertexBuffer(i, v))
65
+ pass.draw(gl.triangleCount, gl.instanceCount, 0, 0)
66
+ pass.end()
67
+ })
68
+
69
+ gl('resize', () => {
70
+ const canvas = gl.el as HTMLCanvasElement
71
+ depthTexture?.destroy()
72
+ depthTexture = createDepthTexture(gl.device, canvas.width, canvas.height)
73
+ })
74
+
75
+ gl('clean', () => {
76
+ depthTexture?.destroy()
77
+ for (const { buffer } of attributes.map.values()) buffer.destroy()
78
+ for (const { texture } of textures.map.values()) texture.destroy()
79
+ for (const { buffer } of uniforms.map.values()) buffer.destroy()
80
+ })
81
+
82
+ const set = (_pipeline: GPURenderPipeline, _bindGroups: GPUBindGroup[], _vertexBuffers: GPUBuffer[]) => {
83
+ pipeline = _pipeline
84
+ bindGroups = _bindGroups
85
+ vertexBuffers = _vertexBuffers
86
+ }
87
+
88
+ return { uniforms, textures, attributes, set }
89
+ }
@@ -0,0 +1,42 @@
1
+ import { compute } from './compute'
2
+ import { graphic } from './graphic'
3
+ import { createBinding, createDevice, updatePipeline } from './utils'
4
+ import { is, WGSL_FS, WGSL_VS } from '../helpers'
5
+ import type { GL } from '../types'
6
+
7
+ export const webgpu = async (gl: GL) => {
8
+ let isUpdate = false
9
+ const isInit = !gl.gl
10
+ if (isInit) {
11
+ const gpu = gl.el!.getContext('webgpu') as GPUCanvasContext
12
+ const { device, format } = await createDevice(gpu, gl.error)
13
+ gl({ device, format, gpu })
14
+ }
15
+
16
+ gl('render', () => {
17
+ if (isUpdate) update()
18
+ gl.encoder = gl.device.createCommandEncoder()
19
+ })
20
+
21
+ const binding = createBinding()
22
+ const c = compute(gl, binding)
23
+ const g = graphic(gl, binding, () => (isUpdate = true))
24
+ const update = () => {
25
+ isUpdate = false
26
+ const config = { isWebGL: false, gl, binding }
27
+ const fs = gl.fs ? (is.str(gl.fs) ? gl.fs : gl.fs.fragment(config)) : WGSL_FS
28
+ const vs = gl.vs ? (is.str(gl.vs) ? gl.vs : gl.vs.vertex(config)) : WGSL_VS
29
+ const cs = gl.cs ? (is.str(gl.cs) ? gl.cs : gl.cs.compute(config)) : ''
30
+ const p = updatePipeline(gl.device, gl.format, g.attributes.map.values(), g.uniforms.map.values(), g.textures.map.values(), c.storages.map.values(), fs, cs, vs)
31
+ c.set(p.computePipeline, p.bindGroups)
32
+ g.set(p.graphicPipeline, p.bindGroups, p.vertexBuffers)
33
+ }
34
+
35
+ gl('render', () => {
36
+ if (gl.encoder) gl.device.queue.submit([gl.encoder.finish()])
37
+ })
38
+
39
+ gl('clean', () => {
40
+ gl.device.destroy()
41
+ })
42
+ }
@@ -1,53 +1,68 @@
1
- import { is, isFloat32 } from './helpers'
1
+ import { nested } from 'reev'
2
+ import { is, isFloat32 } from '../helpers'
2
3
  import type { AttribData, TextureData, UniformData, StorageData } from '../types'
3
4
 
5
+ type IAttribs = Iterable<AttribData & { isInstance?: boolean }>
6
+ type IUniforms = Iterable<UniformData>
7
+ type ITextures = Iterable<TextureData>
8
+ type IStorages = Iterable<StorageData>
9
+
10
+ /**
11
+ * binding
12
+ */
13
+ export const createBinding = () => {
14
+ let _uniform = 0
15
+ let _texture = 0
16
+ let _storage = 0
17
+ let _attrib = 0
18
+ const uniform = nested(() => {
19
+ const group = Math.floor(_uniform / 12)
20
+ const binding = _uniform % 12
21
+ _uniform++
22
+ return { group, binding }
23
+ })
24
+ const texture = nested(() => {
25
+ const baseGroup = Math.floor(_uniform / 12) + 1
26
+ const group = baseGroup + Math.floor(_texture / 6)
27
+ const binding = (_texture % 6) * 2
28
+ _texture++
29
+ return { group, binding }
30
+ })
31
+ const storage = nested(() => {
32
+ const baseGroup = Math.floor(_uniform / 12) + Math.floor(_texture / 6) + 2
33
+ const group = baseGroup + Math.floor(_storage / 12)
34
+ const binding = _storage % 12
35
+ _storage++
36
+ return { group, binding }
37
+ })
38
+ const attrib = nested(() => {
39
+ const location = _attrib
40
+ _attrib++
41
+ return { location }
42
+ })
43
+ return { uniform, texture, storage, attrib }
44
+ }
45
+
46
+ export type Binding = ReturnType<typeof createBinding>
47
+
4
48
  /**
5
49
  * initialize
6
50
  */
7
- export const createDevice = async (c: GPUCanvasContext, log = console.log) => {
51
+ export const createDevice = async (c: GPUCanvasContext, log = console.log, signal?: AbortSignal) => {
8
52
  const gpu = navigator.gpu
9
53
  const format = gpu.getPreferredCanvasFormat()
10
54
  const adapter = await gpu.requestAdapter()
55
+ if (signal?.aborted) throw new DOMException('Aborted', 'AbortError')
11
56
  const device = await adapter!.requestDevice()
57
+ if (signal?.aborted) {
58
+ device.destroy()
59
+ if (signal?.aborted) throw new DOMException('Aborted', 'AbortError')
60
+ }
12
61
  device.onuncapturederror = (e) => log(e.error.message)
13
- c.configure({ device, format, alphaMode: 'opaque' })
62
+ c.configure({ device, format, alphaMode: 'premultiplied' })
14
63
  return { device, format }
15
64
  }
16
65
 
17
- export const createBindings = () => {
18
- let uniform = 0
19
- let texture = 0
20
- let storage = 0
21
- let attrib = 0
22
- return {
23
- uniform: () => {
24
- const group = Math.floor(uniform / 12)
25
- const binding = uniform % 12
26
- uniform++
27
- return { group, binding }
28
- },
29
- texture: () => {
30
- const baseGroup = Math.floor(uniform / 12) + 1
31
- const group = baseGroup + Math.floor(texture / 6)
32
- const binding = (texture % 6) * 2
33
- texture++
34
- return { group, binding }
35
- },
36
- storage: () => {
37
- const baseGroup = Math.floor(uniform / 12) + Math.floor(texture / 6) + 2
38
- const group = baseGroup + Math.floor(storage / 12)
39
- const binding = storage % 12
40
- storage++
41
- return { group, binding }
42
- },
43
- attrib: () => {
44
- const location = attrib
45
- attrib++
46
- return { location }
47
- },
48
- }
49
- }
50
-
51
66
  /**
52
67
  * pipeline update
53
68
  */
@@ -58,7 +73,7 @@ const getVertexFormat = (stride: number): GPUVertexFormat => {
58
73
  return 'float32'
59
74
  }
60
75
 
61
- export const createVertexBuffers = (attribs: Iterable<AttribData & { isInstance?: boolean }>) => {
76
+ const createVertexBuffers = (attribs: IAttribs) => {
62
77
  const vertexBuffers: GPUBuffer[] = []
63
78
  const bufferLayouts: GPUVertexBufferLayout[] = []
64
79
  for (const { buffer, location, stride, isInstance } of attribs) {
@@ -80,12 +95,7 @@ export const createVertexBuffers = (attribs: Iterable<AttribData & { isInstance?
80
95
  return { vertexBuffers, bufferLayouts }
81
96
  }
82
97
 
83
- export const createBindGroup = (
84
- device: GPUDevice,
85
- uniforms: Iterable<UniformData>,
86
- textures: Iterable<TextureData>,
87
- storages: Iterable<StorageData> = []
88
- ) => {
98
+ const createBindGroup = (device: GPUDevice, uniforms: IUniforms, textures: ITextures, storages: IStorages = []) => {
89
99
  const groups = new Map<number, { layouts: GPUBindGroupLayoutEntry[]; bindings: GPUBindGroupEntry[] }>()
90
100
  const ret = { bindGroups: [] as GPUBindGroup[], bindGroupLayouts: [] as GPUBindGroupLayout[] }
91
101
  const add = (i: number, layout: GPUBindGroupLayoutEntry, binding: GPUBindGroupEntry) => {
@@ -111,14 +121,7 @@ export const createBindGroup = (
111
121
  return ret
112
122
  }
113
123
 
114
- export const createPipeline = (
115
- device: GPUDevice,
116
- format: GPUTextureFormat,
117
- bufferLayouts: GPUVertexBufferLayout[],
118
- bindGroupLayouts: GPUBindGroupLayout[],
119
- vs: string,
120
- fs: string
121
- ) => {
124
+ const createPipeline = (device: GPUDevice, format: GPUTextureFormat, bufferLayouts: GPUVertexBufferLayout[], bindGroupLayouts: GPUBindGroupLayout[], vs: string, fs: string) => {
122
125
  return device.createRenderPipeline({
123
126
  vertex: {
124
127
  module: device.createShaderModule({ label: 'vert', code: vs.trim() }),
@@ -140,7 +143,8 @@ export const createPipeline = (
140
143
  })
141
144
  }
142
145
 
143
- export const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBindGroupLayout[], cs: string) => {
146
+ const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBindGroupLayout[], cs: string) => {
147
+ if (!cs) return
144
148
  return device.createComputePipeline({
145
149
  compute: {
146
150
  module: device.createShaderModule({ label: 'compute', code: cs.trim() }),
@@ -150,6 +154,14 @@ export const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBi
150
154
  })
151
155
  }
152
156
 
157
+ export const updatePipeline = (device: GPUDevice, format: GPUTextureFormat, attribs: IAttribs, uniforms: IUniforms, textures: ITextures, storages: IStorages, fs: string, cs: string, vs: string) => {
158
+ const { vertexBuffers, bufferLayouts } = createVertexBuffers(attribs)
159
+ const { bindGroups, bindGroupLayouts } = createBindGroup(device, uniforms, textures, storages)
160
+ const computePipeline = createComputePipeline(device, bindGroupLayouts, cs)
161
+ const graphicPipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, vs, fs)
162
+ return { bindGroups, vertexBuffers, computePipeline, graphicPipeline }
163
+ }
164
+
153
165
  /**
154
166
  * buffers
155
167
  */
@@ -159,11 +171,7 @@ const bufferUsage = (type: 'uniform' | 'storage' | 'attrib') => {
159
171
  return 140 // GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
160
172
  }
161
173
 
162
- export const createArrayBuffer = (
163
- device: GPUDevice,
164
- array: number[] | Float32Array,
165
- type: 'uniform' | 'storage' | 'attrib'
166
- ) => {
174
+ export const createBuffer = (device: GPUDevice, array: number[] | Float32Array, type: 'uniform' | 'storage' | 'attrib') => {
167
175
  if (!isFloat32(array)) array = new Float32Array(array)
168
176
  const usage = bufferUsage(type)
169
177
  const size = type === 'uniform' ? Math.ceil(array.byteLength / 256) * 256 : array.byteLength
@@ -171,22 +179,15 @@ export const createArrayBuffer = (
171
179
  return { array, buffer }
172
180
  }
173
181
 
182
+ export const updateBuffer = (device: GPUDevice, value: number[] | Float32Array, array: Float32Array, buffer: GPUBuffer) => {
183
+ array.set(value)
184
+ device.queue.writeBuffer(buffer, 0, array as GPUAllowSharedBufferSource)
185
+ }
186
+
174
187
  export const createDescriptor = (c: GPUCanvasContext, depthTexture: GPUTexture) => {
175
188
  return {
176
- colorAttachments: [
177
- {
178
- view: c.getCurrentTexture().createView(),
179
- clearValue: { r: 0, g: 0, b: 0, a: 1 },
180
- loadOp: 'clear' as GPULoadOp,
181
- storeOp: 'store' as GPUStoreOp,
182
- },
183
- ],
184
- depthStencilAttachment: {
185
- view: depthTexture.createView(),
186
- depthClearValue: 1.0,
187
- depthLoadOp: 'clear' as GPULoadOp,
188
- depthStoreOp: 'store' as GPUStoreOp,
189
- },
189
+ colorAttachments: [{ view: c.getCurrentTexture().createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }],
190
+ depthStencilAttachment: { view: depthTexture.createView(), depthClearValue: 1.0, depthLoadOp: 'clear', depthStoreOp: 'store' },
190
191
  } as GPURenderPassDescriptor
191
192
  }
192
193
 
@@ -196,15 +197,11 @@ export const createDescriptor = (c: GPUCanvasContext, depthTexture: GPUTexture)
196
197
  export const createTextureSampler = (device: GPUDevice, width = 1280, height = 800) => {
197
198
  const texture = device.createTexture({ size: [width, height], format: 'rgba8unorm', usage: 22 }) // 22 is GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT
198
199
  const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' })
199
- return { texture, sampler }
200
+ return { texture, sampler, view: texture.createView() }
200
201
  }
201
202
 
202
203
  export const createDepthTexture = (device: GPUDevice, width: number, height: number) => {
203
- return device.createTexture({
204
- size: [width, height],
205
- format: 'depth24plus',
206
- usage: GPUTextureUsage.RENDER_ATTACHMENT,
207
- })
204
+ return device.createTexture({ size: [width, height], format: 'depth24plus', usage: 16 }) // 16 is GPUTextureUsage.RENDER_ATTACHMENT
208
205
  }
209
206
 
210
207
  /**