glre 0.35.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/dist/index.cjs +37 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +256 -301
- package/dist/index.js +37 -29
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +37 -29
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.ts +18 -9
- package/dist/native.js +37 -29
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +37 -29
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +37 -29
- package/dist/react.js.map +1 -1
- package/dist/solid.cjs +37 -29
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.ts +1 -1
- package/dist/solid.js +37 -29
- package/dist/solid.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +5 -4
- package/src/node/core.ts +121 -0
- package/src/node/index.ts +80 -159
- package/src/node/node.ts +32 -21
- package/src/node/scope.ts +7 -13
- package/src/node/types.ts +168 -139
- package/src/node/{const.ts → utils/const.ts} +111 -119
- package/src/node/{code.ts → utils/index.ts} +29 -12
- package/src/node/{infer.ts → utils/infer.ts} +12 -8
- package/src/node/{parse.ts → utils/parse.ts} +60 -16
- package/src/node/{utils.ts → utils/utils.ts} +30 -39
- package/src/types.ts +60 -50
- package/src/utils/helpers.ts +16 -0
- package/src/utils/pipeline.ts +41 -12
- package/src/utils/program.ts +95 -13
- package/src/webgl.ts +95 -38
- package/src/webgpu.ts +87 -53
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { is } from '../utils/helpers'
|
|
2
1
|
import { infer } from './infer'
|
|
3
2
|
import {
|
|
4
3
|
parseArray,
|
|
@@ -6,16 +5,22 @@ import {
|
|
|
6
5
|
parseConstantHead,
|
|
7
6
|
parseDeclare,
|
|
8
7
|
parseDefine,
|
|
8
|
+
parseGather,
|
|
9
9
|
parseIf,
|
|
10
|
+
parseScatter,
|
|
11
|
+
parseStorageHead,
|
|
10
12
|
parseStruct,
|
|
11
13
|
parseStructHead,
|
|
12
14
|
parseSwitch,
|
|
13
15
|
parseTexture,
|
|
14
|
-
parseVaryingHead,
|
|
15
16
|
parseUniformHead,
|
|
17
|
+
parseVaryingHead,
|
|
16
18
|
} from './parse'
|
|
17
|
-
import { getBluiltin, getOperator,
|
|
18
|
-
import
|
|
19
|
+
import { getBluiltin, getOperator, getConversions, safeEventCall, getEventFun, initNodeContext } from './utils'
|
|
20
|
+
import { is } from '../../utils/helpers'
|
|
21
|
+
import type { Constants, NodeContext, X } from '../types'
|
|
22
|
+
|
|
23
|
+
export * from './utils'
|
|
19
24
|
|
|
20
25
|
export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null): string => {
|
|
21
26
|
if (!c) c = {}
|
|
@@ -31,19 +36,30 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
|
|
|
31
36
|
}
|
|
32
37
|
if (is.bol(target)) return target ? 'true' : 'false'
|
|
33
38
|
if (!target) return ''
|
|
34
|
-
const { type, props } = target
|
|
39
|
+
const { type, props = {} } = target
|
|
35
40
|
const { id = 'i', children = [], fields, initialValues } = props
|
|
36
41
|
const [x, y, z, w] = children
|
|
37
42
|
/**
|
|
38
43
|
* variables
|
|
39
44
|
*/
|
|
40
45
|
if (type === 'variable') return id
|
|
41
|
-
if (type === 'member') return `${code(
|
|
46
|
+
if (type === 'member') return `${code(x, c)}.${code(y, c)}`
|
|
47
|
+
if (type === 'element') return `${code(x, c)}[${code(y, c)}]`
|
|
48
|
+
if (type === 'gather')
|
|
49
|
+
return c.isWebGL //
|
|
50
|
+
? parseGather(c, x, y, target)
|
|
51
|
+
: `${code(x, c)}[${code(y, c)}]`
|
|
52
|
+
if (type === 'scatter') {
|
|
53
|
+
const [storageNode, indexNode] = x.props.children ?? [] // x is gather node
|
|
54
|
+
return c.isWebGL
|
|
55
|
+
? parseScatter(c, storageNode, y) // indexNode is not using
|
|
56
|
+
: `${code(storageNode, c)}[${code(indexNode, c)}] = ${code(y, c)};`
|
|
57
|
+
}
|
|
42
58
|
if (type === 'ternary')
|
|
43
59
|
return c.isWebGL
|
|
44
60
|
? `(${code(z, c)} ? ${code(x, c)} : ${code(y, c)})`
|
|
45
61
|
: `select(${code(x, c)}, ${code(y, c)}, ${code(z, c)})`
|
|
46
|
-
if (type === 'conversion') return `${
|
|
62
|
+
if (type === 'conversion') return `${getConversions(x, c)}(${parseArray(children.slice(1), c)})`
|
|
47
63
|
if (type === 'operator') {
|
|
48
64
|
if (x === 'not' || x === 'bitNot') return `!${code(y, c)}`
|
|
49
65
|
return `(${code(y, c)} ${getOperator(x)} ${code(z, c)})`
|
|
@@ -88,12 +104,12 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
|
|
|
88
104
|
return c.isWebGL ? `${id}` : `out.${id}`
|
|
89
105
|
}
|
|
90
106
|
if (type === 'builtin') {
|
|
91
|
-
if (c.isWebGL) return getBluiltin(id)
|
|
107
|
+
if (c.isWebGL) return getBluiltin(c, id)
|
|
92
108
|
if (id === 'position') return 'out.position'
|
|
93
|
-
const field = `@builtin(${id}) ${id}: ${
|
|
94
|
-
if (c.
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
const field = `@builtin(${id}) ${id}: ${getConversions(infer(target, c), c)}`
|
|
110
|
+
if (c.label === 'compute') c.code?.computeInputs.set(id, field)
|
|
111
|
+
else if (c.label === 'frag') c.code?.fragInputs.set(id, field)
|
|
112
|
+
else if (c.label === 'vert') c.code?.vertInputs.set(id, field)
|
|
97
113
|
return `in.${id}`
|
|
98
114
|
}
|
|
99
115
|
if (type === 'attribute') {
|
|
@@ -112,6 +128,7 @@ export const code = <T extends Constants>(target: X<T>, c?: NodeContext | null):
|
|
|
112
128
|
target.listeners.add(fun)
|
|
113
129
|
head = parseUniformHead(c, id, varType)
|
|
114
130
|
}
|
|
131
|
+
if (type === 'storage') head = parseStorageHead(c, id, infer(target, c))
|
|
115
132
|
if (type === 'constant') head = parseConstantHead(c, id, infer(target, c), code(x, c))
|
|
116
133
|
if (head) {
|
|
117
134
|
c.code?.headers.set(id, head)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isConstants, isNodeProxy, isSwizzle } from './utils'
|
|
2
2
|
import {
|
|
3
3
|
BUILTIN_TYPES,
|
|
4
4
|
COMPARISON_OPERATORS,
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
FUNCTION_RETURN_TYPES,
|
|
7
7
|
LOGICAL_OPERATORS,
|
|
8
8
|
} from './const'
|
|
9
|
-
import {
|
|
10
|
-
import type { Constants as C, NodeContext, NodeProxy, X } from '
|
|
9
|
+
import { is } from '../../utils/helpers'
|
|
10
|
+
import type { Constants as C, NodeContext, NodeProxy, X } from '../types'
|
|
11
11
|
|
|
12
12
|
const inferBuiltin = <T extends C>(id: string | undefined) => {
|
|
13
13
|
return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES] as T
|
|
@@ -66,18 +66,22 @@ export const inferImpl = <T extends C>(target: NodeProxy<T>, c: NodeContext): T
|
|
|
66
66
|
if (type === 'ternary') return inferOperator(infer(y, c), infer(z, c), 'add')
|
|
67
67
|
if (type === 'builtin') return inferBuiltin(id)
|
|
68
68
|
if (type === 'function') return inferFunction(x) || infer(y, c)
|
|
69
|
-
if (type === 'define'
|
|
69
|
+
if (type === 'define') {
|
|
70
|
+
if (isConstants(layout?.type)) return layout?.type as T
|
|
71
|
+
if (!inferFrom || inferFrom.length === 0) return 'void' as T
|
|
72
|
+
return inferFromArray(inferFrom, c)
|
|
73
|
+
}
|
|
70
74
|
if (type === 'attribute' && is.arr(x) && c.gl?.count) return inferFromCount(x.length / c.gl.count)
|
|
71
75
|
if (type === 'member') {
|
|
72
|
-
if (isSwizzle(
|
|
73
|
-
if (isNodeProxy(
|
|
74
|
-
const field = (
|
|
76
|
+
if (isSwizzle(y)) return inferFromCount(y.length)
|
|
77
|
+
if (isNodeProxy(x)) {
|
|
78
|
+
const field = (x as any).props.fields[y] // for variable node of struct member
|
|
75
79
|
if (field) return infer(field, c)
|
|
76
80
|
}
|
|
77
81
|
return 'float' as T // fallback @TODO FIX
|
|
78
82
|
}
|
|
79
83
|
if (inferFrom) return inferFromArray(inferFrom, c)
|
|
80
|
-
return infer(x, c) // for uniform
|
|
84
|
+
return infer(x, c) // for uniform and storage gather and scatter
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
export const infer = <T extends C>(target: X<T>, c?: NodeContext | null): T => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { code } from './code'
|
|
1
|
+
import { code } from '.'
|
|
3
2
|
import { infer } from './infer'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
3
|
+
import { getConversions, addDependency } from './utils'
|
|
4
|
+
import { is } from '../../utils/helpers'
|
|
5
|
+
import type { Constants, NodeContext, NodeProps, NodeProxy, X } from '../types'
|
|
6
6
|
|
|
7
7
|
export const parseArray = (children: X[], c: NodeContext) => {
|
|
8
8
|
return children
|
|
@@ -11,6 +11,35 @@ export const parseArray = (children: X[], c: NodeContext) => {
|
|
|
11
11
|
.join(', ')
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// only for webgl
|
|
15
|
+
export const parseGather = (c: NodeContext, x: X, y: X, target: X) => {
|
|
16
|
+
const parseSwizzle = () => {
|
|
17
|
+
const valueType = infer(target, c)
|
|
18
|
+
if (valueType === 'float') return '.x'
|
|
19
|
+
if (valueType === 'vec2') return '.xy'
|
|
20
|
+
if (valueType === 'vec3') return '.xyz'
|
|
21
|
+
if (valueType === 'vec4') return ''
|
|
22
|
+
throw new Error(`Unsupported storage scatter type: ${valueType}`)
|
|
23
|
+
}
|
|
24
|
+
const indexVar = code(y, c)
|
|
25
|
+
const texSize = Math.floor(Math.sqrt(c.gl?.particles || 1024))
|
|
26
|
+
const coordX = `int(${indexVar}) % ${texSize}`
|
|
27
|
+
const coordY = `int(${indexVar}) / ${texSize}`
|
|
28
|
+
return `texelFetch(${code(x, c)}, ivec2(${coordX}, ${coordY}), 0)${parseSwizzle()}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// only for webgl
|
|
32
|
+
export const parseScatter = (c: NodeContext, storageNode: X, valueNode: X) => {
|
|
33
|
+
const storageId = code(storageNode, c)
|
|
34
|
+
const valueCode = code(valueNode, c)
|
|
35
|
+
const valueType = infer(valueNode, c)
|
|
36
|
+
if (valueType === 'float') return `_${storageId} = vec4(${valueCode}, 0.0, 0.0, 1.0);`
|
|
37
|
+
if (valueType === 'vec2') return `_${storageId} = vec4(${valueCode}, 0.0, 1.0);`
|
|
38
|
+
if (valueType === 'vec3') return `_${storageId} = vec4(${valueCode}, 1.0);`
|
|
39
|
+
if (valueType === 'vec4') return `_${storageId} = ${valueCode};`
|
|
40
|
+
throw new Error(`Unsupported storage scatter type: ${valueType}`)
|
|
41
|
+
}
|
|
42
|
+
|
|
14
43
|
export const parseTexture = (c: NodeContext, y: X, z: X, w: X) => {
|
|
15
44
|
if (c.isWebGL) {
|
|
16
45
|
const args = w ? [y, z, w] : [y, z]
|
|
@@ -54,7 +83,7 @@ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
|
|
|
54
83
|
const type = infer(x, c)
|
|
55
84
|
const varName = (y as any)?.props?.id
|
|
56
85
|
if (c.isWebGL) return `${type} ${varName} = ${code(x, c)};`
|
|
57
|
-
const wgslType =
|
|
86
|
+
const wgslType = getConversions(type)
|
|
58
87
|
return `var ${varName}: ${wgslType} = ${code(x, c)};`
|
|
59
88
|
}
|
|
60
89
|
|
|
@@ -64,7 +93,7 @@ export const parseStructHead = (c: NodeContext, id: string, fields: Record<strin
|
|
|
64
93
|
const fieldType = fields[key]
|
|
65
94
|
const type = infer(fieldType, c)
|
|
66
95
|
if (c.isWebGL) addDependency(c, id, type)
|
|
67
|
-
lines.push(c.isWebGL ? `${type} ${key};` : `${key}: ${
|
|
96
|
+
lines.push(c.isWebGL ? `${type} ${key};` : `${key}: ${getConversions(type, c)},`)
|
|
68
97
|
}
|
|
69
98
|
const ret = lines.join('\n ')
|
|
70
99
|
return `struct ${id} {\n ${ret}\n};`
|
|
@@ -117,8 +146,11 @@ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Consta
|
|
|
117
146
|
addDependency(c, id!, returnType)
|
|
118
147
|
ret.push(`${returnType} ${id}(${params}) {`)
|
|
119
148
|
} else {
|
|
120
|
-
for (const [paramId, type] of argParams) params.push(`${paramId}: ${
|
|
121
|
-
|
|
149
|
+
for (const [paramId, type] of argParams) params.push(`${paramId}: ${getConversions(type, c)}`)
|
|
150
|
+
const isVoid = returnType === 'void'
|
|
151
|
+
if (isVoid) {
|
|
152
|
+
ret.push(`fn ${id}(${params}) {`)
|
|
153
|
+
} else ret.push(`fn ${id}(${params}) -> ${getConversions(returnType, c)} {`)
|
|
122
154
|
}
|
|
123
155
|
const scopeCode = code(x, c)
|
|
124
156
|
if (scopeCode) ret.push(scopeCode)
|
|
@@ -132,7 +164,14 @@ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Consta
|
|
|
132
164
|
export const parseVaryingHead = (c: NodeContext, id: string, type: string) => {
|
|
133
165
|
return c.isWebGL
|
|
134
166
|
? `${type} ${id};`
|
|
135
|
-
: `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${
|
|
167
|
+
: `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${getConversions(type, c)}`
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const parseAttribHead = (c: NodeContext, id: string, type: Constants) => {
|
|
171
|
+
if (c.isWebGL) return `${type} ${id};`
|
|
172
|
+
const { location = 0 } = c.gl?.webgpu?.attribs.map.get(id) || {}
|
|
173
|
+
const wgslType = getConversions(type, c)
|
|
174
|
+
return `@location(${location}) ${id}: ${wgslType}`
|
|
136
175
|
}
|
|
137
176
|
|
|
138
177
|
export const parseUniformHead = (c: NodeContext, id: string, type: Constants) => {
|
|
@@ -149,17 +188,22 @@ export const parseUniformHead = (c: NodeContext, id: string, type: Constants) =>
|
|
|
149
188
|
)
|
|
150
189
|
}
|
|
151
190
|
const { group = 0, binding = 0 } = c.gl?.webgpu?.uniforms.map.get(id) || {}
|
|
152
|
-
const wgslType =
|
|
191
|
+
const wgslType = getConversions(type, c)
|
|
153
192
|
return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
|
|
154
193
|
}
|
|
155
194
|
|
|
156
|
-
export const
|
|
157
|
-
if (c.isWebGL)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
195
|
+
export const parseStorageHead = (c: NodeContext, id: string, type: Constants) => {
|
|
196
|
+
if (c.isWebGL) {
|
|
197
|
+
const ret = `uniform sampler2D ${id};`
|
|
198
|
+
if (c.label !== 'compute') return ret
|
|
199
|
+
const location = c.units?.(id)
|
|
200
|
+
return `${ret}\nlayout(location = ${location}) out vec4 _${id};` // out texture buffer
|
|
201
|
+
}
|
|
202
|
+
const { group = 0, binding = 0 } = c.gl?.webgpu?.storages.map.get(id) || {}
|
|
203
|
+
const wgslType = getConversions(type, c)
|
|
204
|
+
return `@group(${group}) @binding(${binding}) var<storage, read_write> ${id}: array<${wgslType}>;`
|
|
161
205
|
}
|
|
162
206
|
|
|
163
207
|
export const parseConstantHead = (c: NodeContext, id: string, type: Constants, value: string) => {
|
|
164
|
-
return c.isWebGL ? `const ${type} ${id} = ${value};` : `const ${id}: ${
|
|
208
|
+
return c.isWebGL ? `const ${type} ${id} = ${value};` : `const ${id}: ${getConversions(type, c)} = ${value};`
|
|
165
209
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { is } from '../utils/helpers'
|
|
2
1
|
import {
|
|
3
2
|
CONSTANTS,
|
|
4
3
|
CONVERSIONS,
|
|
@@ -8,7 +7,8 @@ import {
|
|
|
8
7
|
TYPE_MAPPING,
|
|
9
8
|
WGSL_TO_GLSL_BUILTIN,
|
|
10
9
|
} from './const'
|
|
11
|
-
import
|
|
10
|
+
import { is } from '../../utils/helpers'
|
|
11
|
+
import type { Constants, Conversions, Functions, NodeContext, NodeProxy, Operators, Swizzles, X } from '../types'
|
|
12
12
|
|
|
13
13
|
export const isSwizzle = (key: unknown): key is Swizzles => {
|
|
14
14
|
return is.str(key) && /^[xyzwrgbastpq]{1,4}$/.test(key)
|
|
@@ -48,7 +48,17 @@ let count = 0
|
|
|
48
48
|
|
|
49
49
|
export const getId = () => `x${count++}`
|
|
50
50
|
|
|
51
|
-
export const
|
|
51
|
+
export const getBluiltin = (c: NodeContext, id: string) => {
|
|
52
|
+
if (id === 'global_invocation_id') {
|
|
53
|
+
const size = Math.floor(Math.sqrt(c.gl?.particles || 1024))
|
|
54
|
+
return `uvec3(uint(gl_FragCoord.y) * uint(${size}) + uint(gl_FragCoord.x), 0u, 0u)`
|
|
55
|
+
}
|
|
56
|
+
const ret = WGSL_TO_GLSL_BUILTIN[id as keyof typeof WGSL_TO_GLSL_BUILTIN]
|
|
57
|
+
if (!ret) throw new Error(`Error: unknown builtin variable ${id}`)
|
|
58
|
+
return ret
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const getConversions = <T extends Constants>(x: X<T>, c?: NodeContext) => {
|
|
52
62
|
if (!is.str(x)) return ''
|
|
53
63
|
if (c?.isWebGL) return x
|
|
54
64
|
return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING] || x // for struct type
|
|
@@ -58,11 +68,7 @@ export const getOperator = (op: X<string>) => {
|
|
|
58
68
|
return OPERATORS[op as keyof typeof OPERATORS] || op
|
|
59
69
|
}
|
|
60
70
|
|
|
61
|
-
export const
|
|
62
|
-
return WGSL_TO_GLSL_BUILTIN[id as keyof typeof WGSL_TO_GLSL_BUILTIN]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const conversionToConstant = (conversionKey: string): Constants => {
|
|
71
|
+
export const getConstant = (conversionKey: string): Constants => {
|
|
66
72
|
const index = CONVERSIONS.indexOf(conversionKey as Conversions)
|
|
67
73
|
return index !== -1 ? CONSTANTS[index] : 'float'
|
|
68
74
|
}
|
|
@@ -88,42 +94,27 @@ export const safeEventCall = <T extends Constants>(x: X<T>, fun: (value: unknown
|
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
export const initNodeContext = (c: NodeContext) => {
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!c.isWebGL) {
|
|
101
|
-
c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
|
|
102
|
-
c.code.vertOutputs.set('position', '@builtin(position) position: vec4f')
|
|
103
|
-
}
|
|
97
|
+
if (c.code) return c
|
|
98
|
+
c.code = {
|
|
99
|
+
headers: new Map(),
|
|
100
|
+
fragInputs: new Map(),
|
|
101
|
+
vertInputs: new Map(),
|
|
102
|
+
vertOutputs: new Map(),
|
|
103
|
+
vertVaryings: new Map(),
|
|
104
|
+
computeInputs: new Map(),
|
|
105
|
+
dependencies: new Map(),
|
|
104
106
|
}
|
|
107
|
+
if (c.isWebGL) return c
|
|
108
|
+
c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
|
|
109
|
+
c.code.vertOutputs.set('position', '@builtin(position) position: vec4f')
|
|
105
110
|
return c
|
|
106
111
|
}
|
|
107
112
|
|
|
113
|
+
export const isArrayAccess = (key: unknown): boolean => {
|
|
114
|
+
return is.num(key) || (is.str(key) && /^\d+$/.test(key))
|
|
115
|
+
}
|
|
116
|
+
|
|
108
117
|
export const addDependency = (c: NodeContext, id = '', type: string) => {
|
|
109
118
|
if (!c.code?.dependencies?.has(id)) c.code!.dependencies.set(id, new Set())
|
|
110
119
|
if (!isConstants(type)) c.code!.dependencies.get(id)!.add(type)
|
|
111
120
|
}
|
|
112
|
-
|
|
113
|
-
export const sortHeadersByDependencies = (headers: Map<string, string>, dependencies: Map<string, Set<string>>) => {
|
|
114
|
-
const sorted: [string, string][] = []
|
|
115
|
-
const visited = new Set<string>()
|
|
116
|
-
const visiting = new Set<string>()
|
|
117
|
-
const visit = (id: string) => {
|
|
118
|
-
if (visiting.has(id)) return
|
|
119
|
-
if (visited.has(id)) return
|
|
120
|
-
visiting.add(id)
|
|
121
|
-
const deps = dependencies.get(id) || new Set()
|
|
122
|
-
for (const dep of deps) if (headers.has(dep)) visit(dep)
|
|
123
|
-
visiting.delete(id)
|
|
124
|
-
visited.add(id)
|
|
125
|
-
if (headers.has(id)) sorted.push([id, headers.get(id)!])
|
|
126
|
-
}
|
|
127
|
-
for (const [id] of headers) visit(id)
|
|
128
|
-
return sorted
|
|
129
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -2,56 +2,6 @@ import type { EventState, Nested } from 'reev'
|
|
|
2
2
|
import type { Fun, Queue, Frame } from 'refr'
|
|
3
3
|
import type { NodeProxy, Vec4 } from './node'
|
|
4
4
|
export type { Fun, Queue, Frame }
|
|
5
|
-
export type PrecisionMode = 'highp' | 'mediump' | 'lowp'
|
|
6
|
-
export type GLClearMode = 'COLOR_BUFFER_BIT' | 'DEPTH_BUFFER_BIT' | 'STENCIL_BUFFER_BIT'
|
|
7
|
-
export type GLDrawType = 'UNSIGNED_BYTE' | 'UNSIGNED_SHORT' | 'UNSIGNED_INT'
|
|
8
|
-
export type GLDrawMode =
|
|
9
|
-
| 'POINTS'
|
|
10
|
-
| 'LINE_STRIP'
|
|
11
|
-
| 'LINE_LOOP'
|
|
12
|
-
| 'LINES'
|
|
13
|
-
| 'TRIANGLE_STRIP'
|
|
14
|
-
| 'TRIANGLE_FAN'
|
|
15
|
-
| 'TRIANGLES'
|
|
16
|
-
|
|
17
|
-
export interface UniformData {
|
|
18
|
-
array: Float32Array
|
|
19
|
-
buffer: GPUBuffer
|
|
20
|
-
binding: number
|
|
21
|
-
group: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface TextureData {
|
|
25
|
-
binding: number
|
|
26
|
-
group: number
|
|
27
|
-
texture: GPUTexture
|
|
28
|
-
sampler: GPUSampler
|
|
29
|
-
view: GPUTextureView
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface AttribData {
|
|
33
|
-
array: Float32Array
|
|
34
|
-
buffer: GPUBuffer
|
|
35
|
-
location: number
|
|
36
|
-
stride: number
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface WebGLState {
|
|
40
|
-
context: WebGLRenderingContext
|
|
41
|
-
program: WebGLProgram
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface WebGPUState {
|
|
45
|
-
device: GPUDevice
|
|
46
|
-
uniforms: Nested<UniformData>
|
|
47
|
-
textures: Nested<TextureData>
|
|
48
|
-
attribs: Nested<AttribData>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type Uniform = number | number[]
|
|
52
|
-
export type Attribute = number[]
|
|
53
|
-
export type Attributes = Record<string, Attribute>
|
|
54
|
-
export type Uniforms = Record<string, Uniform>
|
|
55
5
|
|
|
56
6
|
export type GL = EventState<{
|
|
57
7
|
/**
|
|
@@ -68,12 +18,16 @@ export type GL = EventState<{
|
|
|
68
18
|
mouse: [number, number]
|
|
69
19
|
count: number
|
|
70
20
|
loading: number
|
|
21
|
+
particles: 64 | 256 | 576 | 1024 | 1600 | 2304 | 3136 | 4096 | 4096 | 5184 | 6400 // (8k)^2
|
|
71
22
|
el: HTMLCanvasElement
|
|
72
23
|
vs?: string | Vec4
|
|
24
|
+
cs?: string | Vec4
|
|
73
25
|
fs?: string | Vec4
|
|
74
26
|
vert?: string | Vec4
|
|
27
|
+
comp?: string | Vec4
|
|
75
28
|
frag?: string | Vec4
|
|
76
29
|
vertex?: string | Vec4
|
|
30
|
+
compute?: string | Vec4
|
|
77
31
|
fragment?: string | Vec4
|
|
78
32
|
|
|
79
33
|
/**
|
|
@@ -109,4 +63,60 @@ export type GL = EventState<{
|
|
|
109
63
|
_attribute?(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
110
64
|
attribute(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
111
65
|
attribute(target: { [key: string]: Attribute }): GL
|
|
66
|
+
_storage?(key: string, value: Storage): GL
|
|
67
|
+
storage(key: string, value: Storage): GL
|
|
68
|
+
storage(target: { [key: string]: Storage }): GL
|
|
112
69
|
}>
|
|
70
|
+
|
|
71
|
+
type Uniform = number | number[] | Float32Array
|
|
72
|
+
type Attribute = number[] | Float32Array
|
|
73
|
+
type Storage = number[] | Float32Array
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* for webgpu
|
|
77
|
+
*/
|
|
78
|
+
export interface UniformData {
|
|
79
|
+
array: Float32Array
|
|
80
|
+
buffer: GPUBuffer
|
|
81
|
+
binding: number
|
|
82
|
+
group: number
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface TextureData {
|
|
86
|
+
binding: number
|
|
87
|
+
group: number
|
|
88
|
+
texture: GPUTexture
|
|
89
|
+
sampler: GPUSampler
|
|
90
|
+
view: GPUTextureView
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface AttribData {
|
|
94
|
+
array: Float32Array
|
|
95
|
+
buffer: GPUBuffer
|
|
96
|
+
location: number
|
|
97
|
+
stride: number
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface StorageData {
|
|
101
|
+
array: Float32Array
|
|
102
|
+
buffer: GPUBuffer
|
|
103
|
+
binding: number
|
|
104
|
+
group: number
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface WebGPUState {
|
|
108
|
+
device: GPUDevice
|
|
109
|
+
uniforms: Nested<UniformData>
|
|
110
|
+
textures: Nested<TextureData>
|
|
111
|
+
attribs: Nested<AttribData>
|
|
112
|
+
storages: Nested<StorageData>
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* for webgl
|
|
117
|
+
*/
|
|
118
|
+
export interface WebGLState {
|
|
119
|
+
context: WebGLRenderingContext
|
|
120
|
+
program: WebGLProgram
|
|
121
|
+
storages: any
|
|
122
|
+
}
|
package/src/utils/helpers.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { GL } from './../types'
|
|
2
|
+
|
|
1
3
|
export const is = {
|
|
2
4
|
arr: Array.isArray,
|
|
3
5
|
bol: (a: unknown): a is boolean => typeof a === 'boolean',
|
|
@@ -42,3 +44,17 @@ export const sig = (value = 0, digit = -2) => {
|
|
|
42
44
|
value /= digit
|
|
43
45
|
return value
|
|
44
46
|
}
|
|
47
|
+
|
|
48
|
+
export const isFloat32 = (value: unknown): value is Float32Array => {
|
|
49
|
+
return value instanceof Float32Array
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const loadingImage = (gl: GL, src: string, fun: (source: HTMLImageElement) => void) => {
|
|
53
|
+
gl.loading++
|
|
54
|
+
const source = new Image()
|
|
55
|
+
Object.assign(source, { src, crossOrigin: 'anonymous' })
|
|
56
|
+
source.decode().then(() => {
|
|
57
|
+
fun(source)
|
|
58
|
+
gl.loading--
|
|
59
|
+
})
|
|
60
|
+
}
|
package/src/utils/pipeline.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isFloat32 } from './helpers'
|
|
2
|
+
import type { AttribData, TextureData, UniformData, StorageData } from '../types'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* initialize
|
|
5
6
|
*/
|
|
6
|
-
export const createDevice = async (c: GPUCanvasContext) => {
|
|
7
|
+
export const createDevice = async (c: GPUCanvasContext, log = console.log) => {
|
|
7
8
|
const gpu = navigator.gpu
|
|
8
9
|
const format = gpu.getPreferredCanvasFormat()
|
|
9
10
|
const adapter = await gpu.requestAdapter()
|
|
10
11
|
const device = await adapter!.requestDevice()
|
|
12
|
+
device.onuncapturederror = (e) => log(e.error.message)
|
|
11
13
|
c.configure({ device, format, alphaMode: 'opaque' })
|
|
12
14
|
return { device, format }
|
|
13
15
|
}
|
|
@@ -15,6 +17,7 @@ export const createDevice = async (c: GPUCanvasContext) => {
|
|
|
15
17
|
export const createBindings = () => {
|
|
16
18
|
let uniform = 0
|
|
17
19
|
let texture = 0
|
|
20
|
+
let storage = 0
|
|
18
21
|
let attrib = 0
|
|
19
22
|
return {
|
|
20
23
|
uniform: () => {
|
|
@@ -30,6 +33,13 @@ export const createBindings = () => {
|
|
|
30
33
|
texture++
|
|
31
34
|
return { group, binding }
|
|
32
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
|
+
},
|
|
33
43
|
attrib: () => {
|
|
34
44
|
const location = attrib
|
|
35
45
|
attrib++
|
|
@@ -70,7 +80,8 @@ export const createVertexBuffers = (attribs: Iterable<AttribData>) => {
|
|
|
70
80
|
export const createBindGroup = (
|
|
71
81
|
device: GPUDevice,
|
|
72
82
|
uniforms: Iterable<UniformData>,
|
|
73
|
-
textures: Iterable<TextureData
|
|
83
|
+
textures: Iterable<TextureData>,
|
|
84
|
+
storages: Iterable<StorageData> = []
|
|
74
85
|
) => {
|
|
75
86
|
const groups = new Map<number, { layouts: GPUBindGroupLayoutEntry[]; bindings: GPUBindGroupEntry[] }>()
|
|
76
87
|
const ret = { bindGroups: [] as GPUBindGroup[], bindGroupLayouts: [] as GPUBindGroupLayout[] }
|
|
@@ -81,7 +92,10 @@ export const createBindGroup = (
|
|
|
81
92
|
bindings.push(binding)
|
|
82
93
|
}
|
|
83
94
|
for (const { binding, buffer, group: i } of uniforms) {
|
|
84
|
-
add(i, { binding, visibility:
|
|
95
|
+
add(i, { binding, visibility: 7, buffer: { type: 'uniform' } }, { binding, resource: { buffer } })
|
|
96
|
+
}
|
|
97
|
+
for (const { binding, buffer, group: i } of storages) {
|
|
98
|
+
add(i, { binding, visibility: 6, buffer: { type: 'storage' } }, { binding, resource: { buffer } })
|
|
85
99
|
}
|
|
86
100
|
for (const { binding: b, group: i, sampler, view } of textures) {
|
|
87
101
|
add(i, { binding: b, visibility: 2, sampler: {} }, { binding: b, resource: sampler })
|
|
@@ -123,19 +137,34 @@ export const createPipeline = (
|
|
|
123
137
|
})
|
|
124
138
|
}
|
|
125
139
|
|
|
140
|
+
export const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBindGroupLayout[], cs: string) => {
|
|
141
|
+
return device.createComputePipeline({
|
|
142
|
+
compute: {
|
|
143
|
+
module: device.createShaderModule({ label: 'compute', code: cs }),
|
|
144
|
+
entryPoint: 'main',
|
|
145
|
+
},
|
|
146
|
+
layout: device.createPipelineLayout({ bindGroupLayouts }),
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
126
150
|
/**
|
|
127
151
|
* buffers
|
|
128
152
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return { array, buffer }
|
|
153
|
+
const bufferUsage = (type: 'uniform' | 'storage' | 'attrib') => {
|
|
154
|
+
if (type === 'uniform') return 72 // GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
155
|
+
if (type === 'attrib') return 40 // GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
156
|
+
return 140 // GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
|
|
134
157
|
}
|
|
135
158
|
|
|
136
|
-
export const
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
export const createArrayBuffer = (
|
|
160
|
+
device: GPUDevice,
|
|
161
|
+
array: number[] | Float32Array,
|
|
162
|
+
type: 'uniform' | 'storage' | 'attrib'
|
|
163
|
+
) => {
|
|
164
|
+
if (!isFloat32(array)) array = new Float32Array(array)
|
|
165
|
+
const usage = bufferUsage(type)
|
|
166
|
+
const size = type === 'uniform' ? Math.ceil(array.byteLength / 256) * 256 : array.byteLength
|
|
167
|
+
const buffer = device.createBuffer({ size, usage })
|
|
139
168
|
return { array, buffer }
|
|
140
169
|
}
|
|
141
170
|
|