glre 0.44.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.
- package/README.md +4 -26
- package/dist/addons.d.ts +41 -51
- package/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +43 -91
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +1 -1
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.ts +51 -94
- package/dist/native.js +1 -1
- package/dist/native.js.map +1 -1
- package/dist/node.cjs +15 -15
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +41 -52
- package/dist/node.js +14 -14
- package/dist/node.js.map +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +43 -92
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/solid.cjs +1 -1
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.ts +43 -92
- package/dist/solid.js +1 -1
- package/dist/solid.js.map +1 -1
- package/package.json +1 -1
- package/src/{utils/helpers.ts → helpers.ts} +10 -32
- package/src/index.ts +45 -42
- package/src/native.ts +6 -7
- package/src/node/build.ts +6 -21
- package/src/node/create.ts +2 -4
- package/src/node/index.ts +8 -20
- package/src/node/types.ts +9 -1
- package/src/node/utils/index.ts +5 -25
- package/src/node/utils/infer.ts +4 -12
- package/src/node/utils/parse.ts +18 -34
- package/src/node/utils/utils.ts +3 -11
- package/src/react.ts +9 -12
- package/src/solid.ts +3 -10
- package/src/types.ts +30 -22
- package/src/webgl/compute.ts +56 -0
- package/src/webgl/graphic.ts +65 -0
- package/src/webgl/index.ts +21 -0
- package/src/{utils/program.ts → webgl/utils.ts} +30 -8
- package/src/webgpu/compute.ts +39 -0
- package/src/webgpu/graphic.ts +89 -0
- package/src/webgpu/index.ts +42 -0
- package/src/{utils/pipeline.ts → webgpu/utils.ts} +75 -78
- package/src/utils/webgl.ts +0 -135
- package/src/utils/webgpu.ts +0 -178
package/src/types.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { EventState
|
|
1
|
+
import type { EventState } from 'reev'
|
|
2
2
|
import type { Queue, Frame } from 'refr'
|
|
3
3
|
import type { Vec4, Void } from './node'
|
|
4
|
+
import type { Binding } from './webgpu/utils'
|
|
4
5
|
|
|
5
6
|
export type GL = EventState<{
|
|
6
7
|
/**
|
|
@@ -13,16 +14,17 @@ export type GL = EventState<{
|
|
|
13
14
|
isDebug: boolean
|
|
14
15
|
isDepth: boolean
|
|
15
16
|
wireframe: boolean
|
|
16
|
-
isGL: true
|
|
17
17
|
width?: number
|
|
18
18
|
height?: number
|
|
19
19
|
size: [number, number]
|
|
20
20
|
mouse: [number, number]
|
|
21
21
|
count: number
|
|
22
|
+
triangleCount: number
|
|
22
23
|
instanceCount: number
|
|
23
24
|
particleCount: number | [number, number] | [number, number, number]
|
|
24
25
|
precision: 'lowp' | 'mediump' | 'highp'
|
|
25
|
-
|
|
26
|
+
element?: HTMLCanvasElement
|
|
27
|
+
elem?: HTMLCanvasElement
|
|
26
28
|
el: HTMLCanvasElement
|
|
27
29
|
vs?: string | Vec4
|
|
28
30
|
cs?: string | Void
|
|
@@ -33,12 +35,19 @@ export type GL = EventState<{
|
|
|
33
35
|
vertex?: string | Vec4
|
|
34
36
|
compute?: string | Void
|
|
35
37
|
fragment?: string | Vec4
|
|
38
|
+
program: WebGLProgram
|
|
39
|
+
gl: WebGL2RenderingContext
|
|
40
|
+
gpu: GPUCanvasContext
|
|
41
|
+
device: GPUDevice
|
|
42
|
+
format: GPUTextureFormat
|
|
43
|
+
encoder: GPUCommandEncoder
|
|
44
|
+
binding: Binding
|
|
36
45
|
|
|
37
46
|
/**
|
|
38
47
|
* core state
|
|
39
48
|
*/
|
|
40
|
-
webgpu: WebGPUState
|
|
41
|
-
webgl: WebGLState
|
|
49
|
+
// webgpu: WebGPUState
|
|
50
|
+
// webgl: WebGLState
|
|
42
51
|
queue: Queue
|
|
43
52
|
frame: Frame
|
|
44
53
|
|
|
@@ -52,7 +61,6 @@ export type GL = EventState<{
|
|
|
52
61
|
render(): void
|
|
53
62
|
resize(e?: Event): void
|
|
54
63
|
mousemove(e: MouseEvent): void
|
|
55
|
-
loop(): void
|
|
56
64
|
|
|
57
65
|
/**
|
|
58
66
|
* setter
|
|
@@ -112,19 +120,19 @@ export interface StorageData {
|
|
|
112
120
|
group: number
|
|
113
121
|
}
|
|
114
122
|
|
|
115
|
-
export interface WebGPUState {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
export interface WebGLState {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
123
|
+
// export interface WebGPUState {
|
|
124
|
+
// device: GPUDevice
|
|
125
|
+
// uniforms: Nested<UniformData>
|
|
126
|
+
// textures: Nested<TextureData>
|
|
127
|
+
// attribs: Nested<AttribData>
|
|
128
|
+
// storages: Nested<StorageData>
|
|
129
|
+
// }
|
|
130
|
+
//
|
|
131
|
+
// /**
|
|
132
|
+
// * for webgl
|
|
133
|
+
// */
|
|
134
|
+
// export interface WebGLState {
|
|
135
|
+
// context: WebGL2RenderingContext
|
|
136
|
+
// program: WebGLProgram
|
|
137
|
+
// uniforms: Nested<WebGLUniformLocation | null>
|
|
138
|
+
// }
|
|
@@ -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 '
|
|
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
|
|
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
|
|
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
|
-
|
|
67
|
-
|
|
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
|
+
}
|