glre 0.47.0 → 0.49.1
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 +1017 -29
- package/dist/addons.d.ts +60 -55
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +84 -79
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/native.d.ts +65 -58
- package/dist/node.cjs +42 -42
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +63 -58
- package/dist/node.js +15 -15
- package/dist/node.js.map +1 -1
- package/dist/react.d.ts +84 -79
- package/dist/solid.d.ts +84 -79
- package/package.json +1 -1
- package/src/helpers.ts +2 -2
- package/src/index.ts +9 -4
- package/src/node/create.ts +10 -9
- package/src/node/scope.ts +3 -15
- package/src/node/types.ts +20 -45
- package/src/node/utils/const.ts +27 -10
- package/src/node/utils/index.ts +15 -8
- package/src/node/utils/infer.ts +26 -20
- package/src/node/utils/parse.ts +5 -5
- package/src/types.ts +6 -4
- package/src/webgl/compute.ts +4 -4
- package/src/webgl/graphic.ts +6 -6
- package/src/webgpu/compute.ts +5 -5
- package/src/webgpu/graphic.ts +15 -27
- package/src/webgpu/index.ts +36 -17
- package/src/webgpu/utils.ts +23 -27
package/src/node/utils/const.ts
CHANGED
|
@@ -32,8 +32,34 @@ export const TYPE_MAPPING = {
|
|
|
32
32
|
|
|
33
33
|
export const CONSTANTS = Object.keys(TYPE_MAPPING) as (keyof typeof TYPE_MAPPING)[]
|
|
34
34
|
|
|
35
|
+
export const SWIZZLE_BASE_MAP = {
|
|
36
|
+
float: 'float',
|
|
37
|
+
vec2: 'float',
|
|
38
|
+
vec3: 'float',
|
|
39
|
+
vec4: 'float',
|
|
40
|
+
int: 'int',
|
|
41
|
+
ivec2: 'int',
|
|
42
|
+
ivec3: 'int',
|
|
43
|
+
ivec4: 'int',
|
|
44
|
+
uint: 'uint',
|
|
45
|
+
uvec2: 'uint',
|
|
46
|
+
uvec3: 'uint',
|
|
47
|
+
uvec4: 'uint',
|
|
48
|
+
bool: 'bool',
|
|
49
|
+
bvec2: 'bool',
|
|
50
|
+
bvec3: 'bool',
|
|
51
|
+
bvec4: 'bool',
|
|
52
|
+
} as const
|
|
53
|
+
|
|
54
|
+
export const SWIZZLE_RESULT_MAP = {
|
|
55
|
+
float: { 1: 'float', 2: 'vec2', 3: 'vec3', 4: 'vec4', 9: 'mat3', 16: 'mat4' } as const,
|
|
56
|
+
int: { 1: 'int', 2: 'ivec2', 3: 'ivec3', 4: 'ivec4' } as const,
|
|
57
|
+
uint: { 1: 'uint', 2: 'uvec2', 3: 'uvec3', 4: 'uvec4' } as const,
|
|
58
|
+
bool: { 1: 'bool', 2: 'bvec2', 3: 'bvec3', 4: 'bvec4' } as const,
|
|
59
|
+
}
|
|
60
|
+
|
|
35
61
|
export const OPERATORS = {
|
|
36
|
-
not: '',
|
|
62
|
+
not: '!',
|
|
37
63
|
add: '+',
|
|
38
64
|
sub: '-',
|
|
39
65
|
mul: '*',
|
|
@@ -66,15 +92,6 @@ export const OPERATORS = {
|
|
|
66
92
|
|
|
67
93
|
export const OPERATOR_KEYS = Object.keys(OPERATORS) as (keyof typeof OPERATORS)[]
|
|
68
94
|
|
|
69
|
-
export const COMPONENT_COUNT_TO_TYPE = {
|
|
70
|
-
1: 'float',
|
|
71
|
-
2: 'vec2',
|
|
72
|
-
3: 'vec3',
|
|
73
|
-
4: 'vec4',
|
|
74
|
-
9: 'mat3',
|
|
75
|
-
16: 'mat4',
|
|
76
|
-
} as const
|
|
77
|
-
|
|
78
95
|
export const BUILTIN_TYPES = {
|
|
79
96
|
// WGSL builtin variables
|
|
80
97
|
position: 'vec4',
|
package/src/node/utils/index.ts
CHANGED
|
@@ -7,18 +7,20 @@ import type { Constants as C, NodeContext, Y } from '../types'
|
|
|
7
7
|
|
|
8
8
|
export * from './utils'
|
|
9
9
|
|
|
10
|
+
const parseNumber = (target = 0) => {
|
|
11
|
+
const ret = `${target}`
|
|
12
|
+
if (ret.includes('.')) return ret
|
|
13
|
+
// Check if this number should be an integer based on the inferred type
|
|
14
|
+
// For now, keep the original behavior to maintain compatibility
|
|
15
|
+
return ret + '.0'
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
export const code = <T extends C>(target: Y<T>, c?: NodeContext | null): string => {
|
|
11
19
|
if (!c) c = {}
|
|
12
20
|
initNodeContext(c)
|
|
13
21
|
if (is.arr(target)) return parseArray(target, c)
|
|
14
22
|
if (is.str(target)) return target
|
|
15
|
-
if (is.num(target))
|
|
16
|
-
const ret = `${target}`
|
|
17
|
-
if (ret.includes('.')) return ret
|
|
18
|
-
// Check if this number should be an integer based on the inferred type
|
|
19
|
-
// For now, keep the original behavior to maintain compatibility
|
|
20
|
-
return ret + '.0'
|
|
21
|
-
}
|
|
23
|
+
if (is.num(target)) return parseNumber(target)
|
|
22
24
|
if (is.bol(target)) return target ? 'true' : 'false'
|
|
23
25
|
if (!target) return ''
|
|
24
26
|
if (!isX(target)) return ''
|
|
@@ -42,7 +44,12 @@ export const code = <T extends C>(target: Y<T>, c?: NodeContext | null): string
|
|
|
42
44
|
: `${code(storageNode, c)}[${code(indexNode, c)}] = ${code(y, c)};`
|
|
43
45
|
}
|
|
44
46
|
if (type === 'ternary') return c.isWebGL ? `(${code(z, c)} ? ${code(x, c)} : ${code(y, c)})` : `select(${code(x, c)}, ${code(y, c)}, ${code(z, c)})`
|
|
45
|
-
if (type === 'conversion')
|
|
47
|
+
if (type === 'conversion') {
|
|
48
|
+
if (x === 'float') if (is.num(y)) return parseNumber(y) // no conversion needed, e.g., float(1.0) → 1.0
|
|
49
|
+
if (x === 'bool') if (is.bol(y)) return y ? 'true' : 'false'
|
|
50
|
+
if (x === 'int') if (is.num(y)) return `${y << 0}`
|
|
51
|
+
return `${getConversions(x, c)}(${parseArray(children.slice(1), c)})`
|
|
52
|
+
}
|
|
46
53
|
if (type === 'operator') {
|
|
47
54
|
if (x === 'not' || x === 'bitNot') return `!${code(y, c)}`
|
|
48
55
|
if (x === 'mod') return code(mod(y, z), c)
|
package/src/node/utils/infer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isConstants, isElement, isX, isSwizzle } from './utils'
|
|
2
|
-
import { BUILTIN_TYPES,
|
|
2
|
+
import { BUILTIN_TYPES, FUNCTION_RETURN_TYPES, getOperatorResultType, SWIZZLE_BASE_MAP, SWIZZLE_RESULT_MAP, validateOperatorTypes } from './const'
|
|
3
3
|
import { is, getStride, isFloat32 } from '../../helpers'
|
|
4
4
|
import type { Constants as C, NodeContext, X, Y } from '../types'
|
|
5
5
|
|
|
@@ -7,7 +7,14 @@ const inferBuiltin = <T extends C>(id: string | undefined) => {
|
|
|
7
7
|
return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES] as T
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// Unified logic with types.ts inferArrayElement type
|
|
11
|
+
const inferSwizzleType = <T extends C>(L: T, len: 1 | 2 | 3 | 4): T => {
|
|
12
|
+
return SWIZZLE_RESULT_MAP[SWIZZLE_BASE_MAP[L as 'float']][len] as T
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Unified logic with types.ts InferOperator type
|
|
10
16
|
const inferOperator = <T extends C>(L: T, R: T, op: string): T => {
|
|
17
|
+
if (op === 'not') return 'bool' as T // 'not' operator's right side is none, causing a warning in the next validator
|
|
11
18
|
if (!validateOperatorTypes(L, R, op)) console.warn(`GLRE Type Warning: Invalid operator '${op}' between types '${L}' and '${R}'`)
|
|
12
19
|
return getOperatorResultType(L, R, op) as T
|
|
13
20
|
}
|
|
@@ -15,15 +22,15 @@ const inferOperator = <T extends C>(L: T, R: T, op: string): T => {
|
|
|
15
22
|
export const inferPrimitiveType = <T extends C>(x: Y<T>) => {
|
|
16
23
|
if (is.bol(x)) return 'bool' as T
|
|
17
24
|
if (is.str(x)) return 'texture' as T
|
|
18
|
-
if (is.num(x)) return 'float' as T // @TODO FIX:
|
|
19
|
-
if (is.arr(x) || isFloat32(x)) return
|
|
25
|
+
if (is.num(x)) return 'float' as T // @TODO FIX: Number.isInteger(x) ? 'int' : 'float'
|
|
26
|
+
if (is.arr(x) || isFloat32(x)) return SWIZZLE_RESULT_MAP.float[x.length as 1 | 2 | 3 | 4 | 9 | 16] as T
|
|
20
27
|
if (isElement(x)) return 'texture' as T
|
|
21
28
|
return 'void' as T
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
const inferFromCount = <T extends C>(count: number) => {
|
|
25
|
-
const ret =
|
|
26
|
-
if (!ret)
|
|
31
|
+
const inferFromCount = <T extends C>(count: number, error = console.warn, id = '') => {
|
|
32
|
+
const ret = SWIZZLE_RESULT_MAP.float[count as 1 | 2 | 3 | 4 | 9 | 16] as T
|
|
33
|
+
if (!ret) error(`glre node system error: Cannot infer ${id ? `${id} ` : ''} type from array length ${count}. Check your data size. Supported: 1(float), 2(vec2), 3(vec3), 4(vec4), 9(mat3), 16(mat4)`)
|
|
27
34
|
return ret
|
|
28
35
|
}
|
|
29
36
|
|
|
@@ -54,22 +61,21 @@ export const inferImpl = <T extends C>(target: X<T>, c: NodeContext): T => {
|
|
|
54
61
|
if (!inferFrom || inferFrom.length === 0) return 'void' as T
|
|
55
62
|
return inferFromArray(inferFrom, c)
|
|
56
63
|
}
|
|
57
|
-
if (type === 'attribute'
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
64
|
+
if (type === 'attribute' || type === 'instance')
|
|
65
|
+
if (is.arr(x)) {
|
|
66
|
+
const count = type === 'instance' ? c.gl?.instanceCount : c.gl?.count
|
|
67
|
+
const stride = getStride(x.length, count, c.gl?.error, id)
|
|
68
|
+
return inferFromCount(stride, c.gl?.error, id)
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
if (type === 'member') {
|
|
66
|
-
if (
|
|
67
|
-
if (
|
|
68
|
-
const
|
|
69
|
-
const fields = c.code?.structStructFields?.get(structType)
|
|
72
|
+
const constant = infer(x, c) // Check if struct first to avoid field/swizzle clash, e.g. Struct({ x: float(), y: float() })
|
|
73
|
+
if (!isConstants(constant)) {
|
|
74
|
+
const fields = c.code?.structStructFields?.get(constant)
|
|
70
75
|
if (fields && fields[y]) return infer(fields[y], c) as T
|
|
71
76
|
}
|
|
72
|
-
return
|
|
77
|
+
if (isSwizzle(y)) return inferSwizzleType(constant, y.length as 1 | 2 | 3 | 4) as T
|
|
78
|
+
return 'float' as T // throw Error
|
|
73
79
|
}
|
|
74
80
|
if (inferFrom) return inferFromArray(inferFrom, c)
|
|
75
81
|
return x ? infer(x, c) : ('void' as T) // for uniform and storage gather and scatter
|
|
@@ -78,7 +84,7 @@ export const inferImpl = <T extends C>(target: X<T>, c: NodeContext): T => {
|
|
|
78
84
|
export const infer = <T extends C>(target: Y<T>, c?: NodeContext | null): T => {
|
|
79
85
|
if (!c) c = {}
|
|
80
86
|
if (!isX(target)) return inferPrimitiveType(target)
|
|
81
|
-
if (is.arr(target)) return inferFromCount(target.length)
|
|
87
|
+
if (is.arr(target)) return inferFromCount(target.length, c.gl?.error, target.props.id)
|
|
82
88
|
if (!c.infers) c.infers = new WeakMap<X<T>, C>()
|
|
83
89
|
if (c.infers.has(target)) return c.infers.get(target) as T
|
|
84
90
|
const ret = inferImpl(target, c)
|
package/src/node/utils/parse.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { code } from '.'
|
|
|
2
2
|
import { infer } from './infer'
|
|
3
3
|
import { getConversions, addDependency } from './utils'
|
|
4
4
|
import { is } from '../../helpers'
|
|
5
|
-
import type { Constants, NodeContext, NodeProps, StructFields, Y } from '../types'
|
|
6
5
|
import { storageSize } from '../../webgl/utils'
|
|
6
|
+
import type { Constants, NodeContext, NodeProps, StructFields, Y } from '../types'
|
|
7
7
|
|
|
8
8
|
export const parseArray = (children: Y[], c: NodeContext) => {
|
|
9
9
|
return children
|
|
@@ -159,7 +159,7 @@ export const parseVaryingHead = (c: NodeContext, id: string, type: Constants) =>
|
|
|
159
159
|
|
|
160
160
|
export const parseAttribHead = (c: NodeContext, id: string, type: Constants) => {
|
|
161
161
|
if (c.isWebGL) return `${type} ${id};`
|
|
162
|
-
const { location = 0 } = c.binding?.attrib(id) || {}
|
|
162
|
+
const { location = 0 } = c.gl?.binding?.attrib(id) || {}
|
|
163
163
|
const wgslType = getConversions(type, c)
|
|
164
164
|
return `@location(${location}) ${id}: ${wgslType}`
|
|
165
165
|
}
|
|
@@ -171,10 +171,10 @@ export const parseUniformHead = (c: NodeContext, id: string, type: Constants) =>
|
|
|
171
171
|
? `uniform sampler2D ${id};`
|
|
172
172
|
: `uniform ${type} ${id};`
|
|
173
173
|
if (isTexture) {
|
|
174
|
-
const { group = 1, binding = 0 } = c.binding?.texture(id) || {}
|
|
174
|
+
const { group = 1, binding = 0 } = c.gl?.binding?.texture(id) || {}
|
|
175
175
|
return `@group(${group}) @binding(${binding}) var ${id}Sampler: sampler;\n` + `@group(${group}) @binding(${binding + 1}) var ${id}: texture_2d<f32>;`
|
|
176
176
|
}
|
|
177
|
-
const { group = 0, binding = 0 } = c.binding?.uniform(id) || {}
|
|
177
|
+
const { group = 0, binding = 0 } = c.gl?.binding?.uniform(id) || {}
|
|
178
178
|
const wgslType = getConversions(type, c)
|
|
179
179
|
return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
|
|
180
180
|
}
|
|
@@ -186,7 +186,7 @@ export const parseStorageHead = (c: NodeContext, id: string, type: Constants) =>
|
|
|
186
186
|
const location = c.units?.(id)
|
|
187
187
|
return `${ret}\nlayout(location = ${location}) out vec4 _${id};` // out texture buffer
|
|
188
188
|
}
|
|
189
|
-
const { group = 0, binding = 0 } = c.binding?.storage(id) || {}
|
|
189
|
+
const { group = 0, binding = 0 } = c.gl?.binding?.storage(id) || {}
|
|
190
190
|
const wgslType = getConversions(type, c)
|
|
191
191
|
return `@group(${group}) @binding(${binding}) var<storage, read_write> ${id}: array<${wgslType}>;`
|
|
192
192
|
}
|
package/src/types.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type GL = EventState<{
|
|
|
18
18
|
height?: number
|
|
19
19
|
size: [number, number]
|
|
20
20
|
mouse: [number, number]
|
|
21
|
-
count: number
|
|
21
|
+
count: number // triangleCount × 3
|
|
22
22
|
triangleCount: number
|
|
23
23
|
instanceCount: number
|
|
24
24
|
particleCount: number | [number, number] | [number, number, number]
|
|
@@ -40,7 +40,9 @@ export type GL = EventState<{
|
|
|
40
40
|
gpu: GPUCanvasContext
|
|
41
41
|
device: GPUDevice
|
|
42
42
|
format: GPUTextureFormat
|
|
43
|
-
|
|
43
|
+
passEncoder: GPURenderPassEncoder
|
|
44
|
+
commandEncoder: GPUCommandEncoder
|
|
45
|
+
depthTexture?: GPUTexture
|
|
44
46
|
binding: Binding
|
|
45
47
|
|
|
46
48
|
/**
|
|
@@ -91,10 +93,10 @@ type Storage = number[] | Float32Array
|
|
|
91
93
|
* for webgpu
|
|
92
94
|
*/
|
|
93
95
|
export interface UniformData {
|
|
94
|
-
array: Float32Array
|
|
95
|
-
buffer: GPUBuffer
|
|
96
96
|
binding: number
|
|
97
97
|
group: number
|
|
98
|
+
array: Float32Array
|
|
99
|
+
buffer: GPUBuffer
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
export interface TextureData {
|
package/src/webgl/compute.ts
CHANGED
|
@@ -4,17 +4,17 @@ import { GLSL_VS, is } from '../helpers'
|
|
|
4
4
|
import type { GL } from '../types'
|
|
5
5
|
|
|
6
6
|
export const compute = (gl: GL) => {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let { cs, particleCount, gl: c } = gl
|
|
8
|
+
if (!cs) return
|
|
9
9
|
c.getExtension('EXT_color_buffer_float') // Enable high precision GPGPU by writing to float textures
|
|
10
10
|
|
|
11
11
|
let _texture = 0 // for texture active units
|
|
12
12
|
let _storage = 0 // for storage current num
|
|
13
13
|
|
|
14
14
|
const units = nested(() => _texture++)
|
|
15
|
-
|
|
15
|
+
cs = is.str(cs) ? cs : cs!.compute({ isWebGL: true, gl, units })
|
|
16
16
|
const pg = createProgram(c, cs, GLSL_VS, gl)!
|
|
17
|
-
const size = storageSize(
|
|
17
|
+
const size = storageSize(particleCount)
|
|
18
18
|
|
|
19
19
|
const uniforms = nested((key) => c.getUniformLocation(pg, key)!)
|
|
20
20
|
const storages = nested((key) => {
|
package/src/webgl/graphic.ts
CHANGED
|
@@ -4,17 +4,17 @@ import { createBuffer, createProgram, createTexture, updateAttrib, updateBuffer,
|
|
|
4
4
|
import type { GL } from '../types'
|
|
5
5
|
|
|
6
6
|
export const graphic = (gl: GL) => {
|
|
7
|
+
let { fs, vs, gl: c } = gl // @TODO Save this WebGPU instance's count (overwritten per args) but no change now for top page
|
|
7
8
|
const config = { isWebGL: true, gl }
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const vs = gl.vs ? (is.str(gl.vs) ? gl.vs : gl.vs.vertex(config)) : GLSL_VS
|
|
9
|
+
fs = gl.fs ? (is.str(gl.fs) ? gl.fs : gl.fs.fragment(config)) : GLSL_FS
|
|
10
|
+
vs = gl.vs ? (is.str(gl.vs) ? gl.vs : gl.vs.vertex(config)) : GLSL_VS
|
|
11
11
|
const pg = createProgram(c, fs, vs, gl)!
|
|
12
12
|
let activeUnit = 0
|
|
13
13
|
|
|
14
14
|
const units = nested(() => activeUnit++)
|
|
15
15
|
const uniforms = nested((key) => c.getUniformLocation(pg, key))
|
|
16
16
|
const attributes = nested((key, value: number[], isInstance = false) => {
|
|
17
|
-
const stride = getStride(value.length, isInstance ? gl.instanceCount : gl.
|
|
17
|
+
const stride = getStride(value.length, isInstance ? gl.instanceCount : gl.count, gl.error, key)
|
|
18
18
|
return { stride, location: c.getAttribLocation(pg, key), ...createBuffer(c, value) }
|
|
19
19
|
})
|
|
20
20
|
|
|
@@ -56,8 +56,8 @@ export const graphic = (gl: GL) => {
|
|
|
56
56
|
gl('render', () => {
|
|
57
57
|
c.useProgram((gl.program = pg))
|
|
58
58
|
if (gl.instanceCount > 1) {
|
|
59
|
-
c.drawArraysInstanced(c.TRIANGLES, 0, gl.
|
|
60
|
-
} else c.drawArrays(c.TRIANGLES, 0, gl.
|
|
59
|
+
c.drawArraysInstanced(c.TRIANGLES, 0, gl.count, gl.instanceCount)
|
|
60
|
+
} else c.drawArrays(c.TRIANGLES, 0, gl.count)
|
|
61
61
|
c.bindFramebuffer(c.FRAMEBUFFER, null)
|
|
62
62
|
})
|
|
63
63
|
}
|
package/src/webgpu/compute.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { nested } from 'reev'
|
|
2
2
|
import { createBuffer, updateBuffer, workgroupCount } from './utils'
|
|
3
|
-
import type { Binding } from './utils'
|
|
4
3
|
import type { GL } from '../types'
|
|
5
4
|
|
|
6
|
-
export const compute = (gl: GL
|
|
5
|
+
export const compute = (gl: GL) => {
|
|
6
|
+
const { particleCount } = gl // Save this WebGPU instance's particleCount (overwritten per args)
|
|
7
7
|
let pipeline: GPUComputePipeline | undefined
|
|
8
8
|
let bindGroups: GPUBindGroup[] | undefined
|
|
9
9
|
|
|
10
10
|
const storages = nested((key, value: number[] | Float32Array) => {
|
|
11
|
-
return { ...
|
|
11
|
+
return { ...gl.binding.storage(key), ...createBuffer(gl.device, value, 'storage') }
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
gl('_storage', (key: string, value: number[] | Float32Array) => {
|
|
@@ -18,10 +18,10 @@ export const compute = (gl: GL, bindings: Binding) => {
|
|
|
18
18
|
|
|
19
19
|
gl('render', () => {
|
|
20
20
|
if (!pipeline || !bindGroups) return
|
|
21
|
-
const pass = gl.
|
|
21
|
+
const pass = gl.commandEncoder.beginComputePass()
|
|
22
22
|
pass.setPipeline(pipeline)
|
|
23
23
|
bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
|
|
24
|
-
const { x, y, z } = workgroupCount(
|
|
24
|
+
const { x, y, z } = workgroupCount(particleCount)
|
|
25
25
|
pass.dispatchWorkgroups(x, y, z)
|
|
26
26
|
pass.end()
|
|
27
27
|
})
|
package/src/webgpu/graphic.ts
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
import { nested } from 'reev'
|
|
2
|
-
import {
|
|
3
|
-
import { createBuffer,
|
|
4
|
-
import type { Binding } from './utils'
|
|
2
|
+
import { getStride, is, loadingTexture } from '../helpers'
|
|
3
|
+
import { createBuffer, createTextureSampler, updateBuffer } from './utils'
|
|
5
4
|
import type { GL } from '../types'
|
|
6
5
|
|
|
7
|
-
export const graphic = (gl: GL,
|
|
6
|
+
export const graphic = (gl: GL, update = () => {}) => {
|
|
7
|
+
const { count, instanceCount } = gl // // Save this WebGPU item's count (overwritten per args)
|
|
8
8
|
let pipeline: GPURenderPipeline
|
|
9
9
|
let bindGroups: GPUBindGroup[]
|
|
10
10
|
let vertexBuffers: GPUBuffer[]
|
|
11
|
-
let depthTexture: GPUTexture
|
|
12
11
|
|
|
13
|
-
const attributes = nested((key, value: number[], isInstance = false, stride = getStride(value.length, isInstance ?
|
|
12
|
+
const attributes = nested((key, value: number[], isInstance = false, stride = getStride(value.length, isInstance ? instanceCount : count, gl.error, key)) => {
|
|
14
13
|
update()
|
|
15
|
-
return { ...
|
|
14
|
+
return { ...gl.binding.attrib(key), ...createBuffer(gl.device, value, 'attrib'), isInstance, stride }
|
|
16
15
|
})
|
|
17
16
|
|
|
18
17
|
const uniforms = nested((key, value: number[] | Float32Array) => {
|
|
19
18
|
update()
|
|
20
|
-
return { ...
|
|
19
|
+
return { ...gl.binding.uniform(key), ...createBuffer(gl.device, value, 'uniform') }
|
|
21
20
|
})
|
|
22
21
|
|
|
23
22
|
const textures = nested((key, width = 1, height = 1) => {
|
|
24
23
|
update()
|
|
25
|
-
return { ...
|
|
24
|
+
return { ...gl.binding.texture(key), ...createTextureSampler(gl.device, width, height) }
|
|
26
25
|
})
|
|
27
26
|
|
|
28
27
|
gl('_attribute', (key: string, value: number[] | Float32Array) => {
|
|
@@ -45,11 +44,9 @@ export const graphic = (gl: GL, bindings: Binding, update = () => {}) => {
|
|
|
45
44
|
const t = textures(key)
|
|
46
45
|
loadingTexture(src, (source, isVideo) => {
|
|
47
46
|
const [width, height] = isVideo ? [source.videoWidth, source.videoHeight] : [source.width, source.height]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
update() // Rebuilding BindGroups because the texture size has changed
|
|
52
|
-
}
|
|
47
|
+
t.texture.destroy()
|
|
48
|
+
Object.assign(t, createTextureSampler(gl.device, width, height))
|
|
49
|
+
update() // Rebuilding BindGroups because the texture size has changed
|
|
53
50
|
const render = () => void gl.device.queue.copyExternalImageToTexture({ source }, { texture: t.texture }, { width, height })
|
|
54
51
|
if (isVideo) gl({ render })
|
|
55
52
|
else render()
|
|
@@ -58,22 +55,13 @@ export const graphic = (gl: GL, bindings: Binding, update = () => {}) => {
|
|
|
58
55
|
|
|
59
56
|
gl('render', () => {
|
|
60
57
|
if (!pipeline || !bindGroups || !vertexBuffers) return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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)
|
|
58
|
+
gl.passEncoder.setPipeline(pipeline)
|
|
59
|
+
bindGroups.forEach((v, i) => gl.passEncoder.setBindGroup(i, v))
|
|
60
|
+
vertexBuffers.forEach((v, i) => gl.passEncoder.setVertexBuffer(i, v))
|
|
61
|
+
gl.passEncoder.draw(count, instanceCount, 0, 0)
|
|
73
62
|
})
|
|
74
63
|
|
|
75
64
|
gl('clean', () => {
|
|
76
|
-
depthTexture?.destroy()
|
|
77
65
|
for (const { buffer } of attributes.map.values()) buffer.destroy()
|
|
78
66
|
for (const { texture } of textures.map.values()) texture.destroy()
|
|
79
67
|
for (const { buffer } of uniforms.map.values()) buffer.destroy()
|
package/src/webgpu/index.ts
CHANGED
|
@@ -1,42 +1,61 @@
|
|
|
1
1
|
import { compute } from './compute'
|
|
2
2
|
import { graphic } from './graphic'
|
|
3
|
-
import { createBinding, createDevice, updatePipeline } from './utils'
|
|
3
|
+
import { createBinding, createDepthTexture, createDescriptor, createDevice, updatePipeline } from './utils'
|
|
4
4
|
import { is, WGSL_FS, WGSL_VS } from '../helpers'
|
|
5
5
|
import type { GL } from '../types'
|
|
6
6
|
|
|
7
|
-
export const webgpu = async (gl: GL) => {
|
|
8
|
-
let
|
|
9
|
-
|
|
7
|
+
export const webgpu = async (gl: GL, isLast = false) => {
|
|
8
|
+
let { vs, fs, cs } = gl
|
|
9
|
+
let isUpdate = true
|
|
10
|
+
const isInit = !gl.gpu
|
|
10
11
|
if (isInit) {
|
|
11
12
|
const gpu = gl.el!.getContext('webgpu') as GPUCanvasContext
|
|
13
|
+
const binding = createBinding()
|
|
12
14
|
const { device, format } = await createDevice(gpu, gl.error)
|
|
13
|
-
gl({ device, format, gpu })
|
|
15
|
+
gl({ device, format, binding, gpu })
|
|
16
|
+
gl('resize', () => {
|
|
17
|
+
gl.depthTexture?.destroy()
|
|
18
|
+
if (gl.isDepth) gl.depthTexture = createDepthTexture(gl.device, ...gl.size)
|
|
19
|
+
})
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
gl('render', () => {
|
|
17
23
|
if (isUpdate) update()
|
|
18
|
-
gl.encoder = gl.device.createCommandEncoder()
|
|
19
24
|
})
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
if (isInit)
|
|
27
|
+
gl('render', () => {
|
|
28
|
+
gl.commandEncoder = gl.device.createCommandEncoder()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const c = compute(gl)
|
|
32
|
+
|
|
33
|
+
if (isInit)
|
|
34
|
+
gl('render', () => {
|
|
35
|
+
gl.passEncoder = gl.commandEncoder.beginRenderPass(createDescriptor(gl.gpu, gl.depthTexture))
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const g = graphic(gl, () => (isUpdate = true))
|
|
39
|
+
|
|
24
40
|
const update = () => {
|
|
25
41
|
isUpdate = false
|
|
26
|
-
const config = { isWebGL: false, gl
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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)
|
|
42
|
+
const config = { isWebGL: false, gl }
|
|
43
|
+
fs = fs ? (is.str(fs) ? fs : fs.fragment(config)) : WGSL_FS
|
|
44
|
+
vs = vs ? (is.str(vs) ? vs : vs.vertex(config)) : WGSL_VS
|
|
45
|
+
cs = cs ? (is.str(cs) ? cs : cs.compute(config)) : ''
|
|
46
|
+
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, gl.isDepth)
|
|
31
47
|
c.set(p.computePipeline, p.bindGroups)
|
|
32
48
|
g.set(p.graphicPipeline, p.bindGroups, p.vertexBuffers)
|
|
33
49
|
}
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
if (isLast)
|
|
52
|
+
gl('render', () => {
|
|
53
|
+
gl.passEncoder.end()
|
|
54
|
+
gl.device.queue.submit([gl.commandEncoder.finish()])
|
|
55
|
+
})
|
|
38
56
|
|
|
39
57
|
gl('clean', () => {
|
|
58
|
+
gl.depthTexture?.destroy()
|
|
40
59
|
gl.device.destroy()
|
|
41
60
|
})
|
|
42
61
|
}
|
package/src/webgpu/utils.ts
CHANGED
|
@@ -104,15 +104,15 @@ const createBindGroup = (device: GPUDevice, uniforms: IUniforms, textures: IText
|
|
|
104
104
|
layouts.push(layout)
|
|
105
105
|
bindings.push(binding)
|
|
106
106
|
}
|
|
107
|
-
for (const { binding, buffer, group
|
|
108
|
-
add(
|
|
107
|
+
for (const { binding, buffer, group } of uniforms) {
|
|
108
|
+
add(group, { binding, visibility: 7, buffer: { type: 'uniform' } }, { binding, resource: { buffer } })
|
|
109
109
|
}
|
|
110
|
-
for (const { binding, buffer, group
|
|
111
|
-
add(
|
|
110
|
+
for (const { binding, buffer, group } of storages) {
|
|
111
|
+
add(group, { binding, visibility: 6, buffer: { type: 'storage' } }, { binding, resource: { buffer } })
|
|
112
112
|
}
|
|
113
|
-
for (const { binding: b, group
|
|
114
|
-
add(
|
|
115
|
-
add(
|
|
113
|
+
for (const { binding: b, group, sampler, view } of textures) {
|
|
114
|
+
add(group, { binding: b, visibility: 2, sampler: {} }, { binding: b, resource: sampler })
|
|
115
|
+
add(group, { binding: b + 1, visibility: 2, texture: {} }, { binding: b + 1, resource: view })
|
|
116
116
|
}
|
|
117
117
|
for (const [i, { layouts, bindings }] of groups) {
|
|
118
118
|
ret.bindGroupLayouts[i] = device.createBindGroupLayout({ entries: layouts })
|
|
@@ -121,8 +121,10 @@ const createBindGroup = (device: GPUDevice, uniforms: IUniforms, textures: IText
|
|
|
121
121
|
return ret
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
const createPipeline = (device: GPUDevice, format: GPUTextureFormat, bufferLayouts: GPUVertexBufferLayout[], bindGroupLayouts: GPUBindGroupLayout[], vs: string, fs: string) => {
|
|
125
|
-
|
|
124
|
+
const createPipeline = (device: GPUDevice, format: GPUTextureFormat, bufferLayouts: GPUVertexBufferLayout[], bindGroupLayouts: GPUBindGroupLayout[], vs: string, fs: string, isDepth: boolean) => {
|
|
125
|
+
const config: GPURenderPipelineDescriptor = {
|
|
126
|
+
primitive: { topology: 'triangle-list' },
|
|
127
|
+
layout: device.createPipelineLayout({ bindGroupLayouts }),
|
|
126
128
|
vertex: {
|
|
127
129
|
module: device.createShaderModule({ label: 'vert', code: vs.trim() }),
|
|
128
130
|
entryPoint: 'main',
|
|
@@ -133,14 +135,9 @@ const createPipeline = (device: GPUDevice, format: GPUTextureFormat, bufferLayou
|
|
|
133
135
|
entryPoint: 'main',
|
|
134
136
|
targets: [{ format }],
|
|
135
137
|
},
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
depthWriteEnabled: true,
|
|
140
|
-
depthCompare: 'less',
|
|
141
|
-
format: 'depth24plus',
|
|
142
|
-
},
|
|
143
|
-
})
|
|
138
|
+
}
|
|
139
|
+
if (isDepth) config.depthStencil = { depthWriteEnabled: true, depthCompare: 'less', format: 'depth24plus' }
|
|
140
|
+
return device.createRenderPipeline(config)
|
|
144
141
|
}
|
|
145
142
|
|
|
146
143
|
const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBindGroupLayout[], cs: string) => {
|
|
@@ -154,11 +151,11 @@ const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBindGroup
|
|
|
154
151
|
})
|
|
155
152
|
}
|
|
156
153
|
|
|
157
|
-
export const updatePipeline = (device: GPUDevice, format: GPUTextureFormat, attribs: IAttribs, uniforms: IUniforms, textures: ITextures, storages: IStorages, fs: string, cs: string, vs: string) => {
|
|
154
|
+
export const updatePipeline = (device: GPUDevice, format: GPUTextureFormat, attribs: IAttribs, uniforms: IUniforms, textures: ITextures, storages: IStorages, fs: string, cs: string, vs: string, isDepth: boolean) => {
|
|
158
155
|
const { vertexBuffers, bufferLayouts } = createVertexBuffers(attribs)
|
|
159
156
|
const { bindGroups, bindGroupLayouts } = createBindGroup(device, uniforms, textures, storages)
|
|
160
157
|
const computePipeline = createComputePipeline(device, bindGroupLayouts, cs)
|
|
161
|
-
const graphicPipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, vs, fs)
|
|
158
|
+
const graphicPipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, vs, fs, isDepth)
|
|
162
159
|
return { bindGroups, vertexBuffers, computePipeline, graphicPipeline }
|
|
163
160
|
}
|
|
164
161
|
|
|
@@ -166,9 +163,9 @@ export const updatePipeline = (device: GPUDevice, format: GPUTextureFormat, attr
|
|
|
166
163
|
* buffers
|
|
167
164
|
*/
|
|
168
165
|
const bufferUsage = (type: 'uniform' | 'storage' | 'attrib') => {
|
|
169
|
-
if (type === 'uniform') return 72 // GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
170
|
-
if (type === 'attrib') return 40 // GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
171
|
-
return 140 // GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
|
|
166
|
+
if (type === 'uniform') return 72 // 72 is GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
167
|
+
if (type === 'attrib') return 40 // 40 is GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
168
|
+
return 140 // 140 is GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
|
|
172
169
|
}
|
|
173
170
|
|
|
174
171
|
export const createBuffer = (device: GPUDevice, array: number[] | Float32Array, type: 'uniform' | 'storage' | 'attrib') => {
|
|
@@ -184,11 +181,10 @@ export const updateBuffer = (device: GPUDevice, value: number[] | Float32Array,
|
|
|
184
181
|
device.queue.writeBuffer(buffer, 0, array as GPUAllowSharedBufferSource)
|
|
185
182
|
}
|
|
186
183
|
|
|
187
|
-
export const createDescriptor = (c: GPUCanvasContext, depthTexture
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
} as GPURenderPassDescriptor
|
|
184
|
+
export const createDescriptor = (c: GPUCanvasContext, depthTexture?: GPUTexture) => {
|
|
185
|
+
const ret: GPURenderPassDescriptor = { colorAttachments: [{ view: c.getCurrentTexture().createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }] }
|
|
186
|
+
if (depthTexture) ret.depthStencilAttachment = { view: depthTexture.createView(), depthClearValue: 1.0, depthLoadOp: 'clear', depthStoreOp: 'store' }
|
|
187
|
+
return ret
|
|
192
188
|
}
|
|
193
189
|
|
|
194
190
|
/**
|