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.
@@ -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, formatConversions, safeEventCall, getEventFun, initNodeContext } from './utils'
18
- import type { Constants, NodeContext, X } from './types'
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(y, c)}.${code(x, c)}`
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 `${formatConversions(x, c)}(${parseArray(children.slice(1), c)})`
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}: ${formatConversions(infer(target, c), c)}`
94
- if (c.isFrag) {
95
- c.code?.fragInputs.set(id, field)
96
- } else c.code?.vertInputs.set(id, field)
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 { is } from '../utils/helpers'
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 { isConstants, isNodeProxy, isSwizzle } from './utils'
10
- import type { Constants as C, NodeContext, NodeProxy, X } from './types'
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' && isConstants(layout?.type)) return layout?.type as T
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(x)) return inferFromCount(x.length)
73
- if (isNodeProxy(y) && is.str(x)) {
74
- const field = (y as any).props.fields?.[x] // for variable node of struct member
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 { is } from '../utils/helpers'
2
- import { code } from './code'
1
+ import { code } from '.'
3
2
  import { infer } from './infer'
4
- import { formatConversions, addDependency } from './utils'
5
- import type { Constants, NodeContext, NodeProps, NodeProxy, X } from './types'
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 = formatConversions(type)
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}: ${formatConversions(type, c)},`)
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}: ${formatConversions(type, c)}`)
121
- ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
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}: ${formatConversions(type, c)}`
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 = formatConversions(type, c)
191
+ const wgslType = getConversions(type, c)
153
192
  return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
154
193
  }
155
194
 
156
- export const parseAttribHead = (c: NodeContext, id: string, type: Constants) => {
157
- if (c.isWebGL) return `${type} ${id};`
158
- const { location = 0 } = c.gl?.webgpu?.attribs.map.get(id) || {}
159
- const wgslType = formatConversions(type, c)
160
- return `@location(${location}) ${id}: ${wgslType}`
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}: ${formatConversions(type, c)} = ${value};`
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 type { Constants, Conversions, Functions, NodeContext, NodeProxy, Operators, Swizzles, X } from './types'
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 formatConversions = <T extends Constants>(x: X<T>, c?: NodeContext) => {
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 getBluiltin = (id: string) => {
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 (!c.code) {
92
- c.code = {
93
- headers: new Map(),
94
- fragInputs: new Map(),
95
- vertInputs: new Map(),
96
- vertOutputs: new Map(),
97
- vertVaryings: new Map(),
98
- dependencies: new Map(),
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
+ }
@@ -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
+ }
@@ -1,13 +1,15 @@
1
- import type { AttribData, TextureData, UniformData } from '../types'
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: 3, buffer: { type: 'uniform' } }, { binding, resource: { buffer } })
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
- export const createUniformBuffer = (device: GPUDevice, value: number[]) => {
130
- const array = new Float32Array(value)
131
- const size = Math.ceil(array.byteLength / 256) * 256
132
- const buffer = device.createBuffer({ size, usage: 72 }) // 72 is GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
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 createAttribBuffer = (device: GPUDevice, value: number[]) => {
137
- const array = new Float32Array(value)
138
- const buffer = device.createBuffer({ size: array.byteLength, usage: 40 }) // 40 is GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
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