glre 0.30.0 → 0.32.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/src/node/code.ts CHANGED
@@ -1,11 +1,32 @@
1
1
  import { is } from '../utils/helpers'
2
2
  import { infer } from './infer'
3
- import { getBluiltin, getOperator, generateDefine, formatConversions, joins } from './utils'
4
- import type { NodeConfig, X } from './types'
3
+ import { getBluiltin, getOperator, formatConversions, joins } from './utils'
4
+ import type { NodeContext, X } from './types'
5
+ import {
6
+ parseAttribHead,
7
+ parseConstantHead,
8
+ parseUniformHead,
9
+ parseDeclare,
10
+ parseDefine,
11
+ parseIf,
12
+ parseSwitch,
13
+ parseTexture,
14
+ parseVaryingHead,
15
+ } from './parse'
5
16
 
6
- export const code = (target: X, c?: NodeConfig | null): string => {
17
+ export const code = (target: X, c?: NodeContext | null): string => {
7
18
  if (!c) c = {}
8
19
  if (!c.headers) c.headers = new Map()
20
+ if (!c.vertVaryings) c.vertVaryings = new Map()
21
+ if (!c.fragInputs) c.fragInputs = new Map()
22
+ if (!c.vertInputs) c.vertInputs = new Map()
23
+ if (!c.vertOutputs) {
24
+ c.vertOutputs = new Map()
25
+ if (!c.isWebGL) {
26
+ c.fragInputs.set('position', '@builtin(position) position: vec4f')
27
+ c.vertOutputs.set('position', '@builtin(position) position: vec4f')
28
+ }
29
+ }
9
30
  if (is.str(target)) return target
10
31
  if (is.num(target)) {
11
32
  const ret = `${target}`
@@ -13,50 +34,19 @@ export const code = (target: X, c?: NodeConfig | null): string => {
13
34
  return ret + '.0'
14
35
  }
15
36
  if (is.bol(target)) return target ? 'true' : 'false'
16
- if (!target) return '' // ignore if no target
37
+ if (!target) return ''
17
38
  const { type, props } = target
18
39
  const { id = '', children = [] } = props
19
- const [x, y, z] = children
20
- /**
21
- * headers
22
- */
23
- let head = ''
24
- if (type === 'attribute') {
25
- if (c.headers.has(id)) return id
26
- head = `${infer(target, c)} ${id}`
27
- }
28
- if (type === 'uniform') {
29
- if (c.headers.has(id)) return id
30
- if (!c.binding) c.binding = 0
31
- const varType = infer(target, c)
32
- head = c.isWebGL
33
- ? `uniform ${varType} ${id};`
34
- : `@group(0) @binding(${c.binding++}) var<uniform> ${id}: ${formatConversions(varType, c)};`
35
- }
36
- if (type === 'constant') {
37
- if (c.headers.has(id)) return id
38
- const varType = infer(target, c)
39
- const value = code(x, c)
40
- head = c.isWebGL
41
- ? `const ${varType} ${id} = ${value};`
42
- : `const ${id}: ${formatConversions(varType, c)} = ${value};`
43
- }
44
- if (type === 'varying') {
45
- if (c.headers.has(id)) return id
46
- head = `${infer(target, c)} ${id}`
47
- }
48
- if (head) {
49
- c.headers.set(id, head)
50
- c.onMount?.(id)
51
- return id
52
- }
40
+ const [x, y, z, w] = children
53
41
  /**
54
42
  * variables
55
43
  */
56
44
  if (type === 'variable') return id
57
45
  if (type === 'swizzle') return `${code(y, c)}.${code(x, c)}`
58
- if (type === 'ternary') return `(${code(x, c)} ? ${code(y, c)} : ${code(z, c)})`
59
- if (type === 'builtin') return c?.isWebGL ? getBluiltin(id) : id
46
+ if (type === 'ternary')
47
+ return c.isWebGL
48
+ ? `(${code(x, c)} ? ${code(y, c)} : ${code(z, c)})`
49
+ : `select(${code(x, c)}, ${code(y, c)}, ${code(z, c)})`
60
50
  if (type === 'conversion') return `${formatConversions(x, c)}(${joins(children.slice(1), c)})`
61
51
  if (type === 'operator') {
62
52
  if (x === 'not' || x === 'bitNot') return `!${code(y, c)}`
@@ -64,6 +54,7 @@ export const code = (target: X, c?: NodeConfig | null): string => {
64
54
  }
65
55
  if (type === 'function') {
66
56
  if (x === 'negate') return `(-${joins(children.slice(1), c)})`
57
+ if (x === 'texture') return parseTexture(c, y, z, w)
67
58
  return `${x}(${joins(children.slice(1), c)})`
68
59
  }
69
60
  /**
@@ -71,45 +62,51 @@ export const code = (target: X, c?: NodeConfig | null): string => {
71
62
  */
72
63
  if (type === 'scope') return children.map((child: any) => code(child, c)).join('\n')
73
64
  if (type === 'assign') return `${code(x, c)} = ${code(y, c)};`
65
+ if (type === 'return') return `return ${code(x, c)};`
74
66
  if (type === 'loop')
75
67
  return c.isWebGL
76
68
  ? `for (int i = 0; i < ${x}; i += 1) {\n${code(y, c)}\n}`
77
69
  : `for (var i: i32 = 0; i < ${x}; i++) {\n${code(y, c)}\n}`
70
+ if (type === 'if') return parseIf(c, x, y, children)
71
+ if (type === 'switch') return parseSwitch(c, x, children)
72
+ if (type === 'declare') return parseDeclare(c, x, y)
78
73
  if (type === 'define') {
79
- const args = children.slice(2)
80
- const ret = `${id}(${args.map((arg) => code(arg, c))})`
74
+ const ret = `${id}(${joins(children.slice(1), c)})`
81
75
  if (c.headers.has(id)) return ret
82
- c.headers.set(id, generateDefine(props, c))
76
+ c.headers.set(id, parseDefine(c, props, infer(target, c)))
83
77
  return ret
84
78
  }
85
- if (type === 'if') {
86
- let ret = `if (${code(x, c)}) {\n${code(y, c)}\n}`
87
- for (let i = 2; i < children.length; i += 2) {
88
- const isElseIf = i >= children.length - 1
89
- ret += !isElseIf
90
- ? ` else if (${code(children[i], c)}) {\n${code(children[i + 1], c)}\n}`
91
- : ` else {\n${code(children[i], c)}\n}`
92
- }
93
- return ret
79
+ /**
80
+ * headers
81
+ */
82
+ if (type === 'varying') {
83
+ if (c.vertOutputs.has(id)) return c.isWebGL ? `${id}` : `out.${id}`
84
+ const field = parseVaryingHead(c, id, infer(target, c))
85
+ c.fragInputs.set(id, field)
86
+ c.vertOutputs.set(id, field)
87
+ c.vertVaryings.set(id, code(x, c))
88
+ return c.isWebGL ? `${id}` : `out.${id}`
94
89
  }
95
- if (type === 'switch') {
96
- let ret = `switch (${code(x, c)}) {\n`
97
- for (let i = 1; i < children.length; i += 2) {
98
- const isDefault = i >= children.length - 1
99
- if (isDefault && children.length % 2 === 0) {
100
- ret += `default:\n${code(children[i], c)}\nbreak;\n`
101
- } else if (i + 1 < children.length)
102
- ret += `case ${code(children[i], c)}:\n${code(children[i + 1], c)}\nbreak;\n`
103
- }
104
- ret += '}'
105
- return ret
90
+ if (type === 'builtin') {
91
+ if (c.isWebGL) return getBluiltin(id)
92
+ if (id === 'position') return 'out.position'
93
+ const field = `@builtin(${id}) ${id}: ${formatConversions(infer(target, c), c)}`
94
+ if (c.isFrag) {
95
+ c.fragInputs.set(id, field)
96
+ } else c.vertInputs.set(id, field)
97
+ return `in.${id}`
98
+ }
99
+ if (type === 'attribute') {
100
+ c.vertInputs.set(id, parseAttribHead(c, id, infer(target, c)))
101
+ return c.isWebGL ? `${id}` : `in.${id}`
106
102
  }
107
- if (type === 'declare') {
108
- const varType = infer(x, c)
109
- const varName = (y as any)?.props?.id
110
- if (c.isWebGL) return `${varType} ${varName} = ${code(x, c)};`
111
- const wgslType = formatConversions(varType)
112
- return `var ${varName}: ${wgslType} = ${code(x, c)};`
103
+ if (c.headers.has(id)) return id // must last
104
+ let head = ''
105
+ if (type === 'uniform') head = parseUniformHead(c, id, infer(target, c))
106
+ if (type === 'constant') head = parseConstantHead(c, id, infer(target, c), code(x, c))
107
+ if (head) {
108
+ c.headers.set(id, head)
109
+ return id
113
110
  }
114
111
  return code(x, c)
115
112
  }
package/src/node/const.ts CHANGED
@@ -21,6 +21,8 @@ export const CONSTANTS = [
21
21
  'mat2',
22
22
  'mat3',
23
23
  'mat4',
24
+ 'texture',
25
+ 'sampler2D',
24
26
  ] as const
25
27
 
26
28
  export const CONVERSIONS = [
package/src/node/index.ts CHANGED
@@ -1,15 +1,82 @@
1
- import { builtin, conversion as c, function_ as f, uniform } from './node'
2
- import { hex2rgb } from './utils'
3
1
  import { is } from '../utils/helpers'
4
- import type { X } from './types'
2
+ import { code } from './code'
3
+ import { builtin, conversion as c, function_ as f, uniform as u } from './node'
4
+ import { hex2rgb } from './utils'
5
+ import type { NodeContext, X } from './types'
5
6
  export * from './code'
6
- export * from './const'
7
- export * from './infer'
8
7
  export * from './node'
9
8
  export * from './scope'
10
9
  export * from './types'
11
10
  export * from './utils'
12
11
 
12
+ const GLSL_FRAGMENT_HEAD = `
13
+ #version 300 es
14
+ precision mediump float;
15
+ out vec4 fragColor;
16
+ `.trim()
17
+
18
+ const generateHead = (x: X, c: NodeContext) => {
19
+ const body = code(x, c)
20
+ const head = Array.from(c.headers!.values()).join('\n')
21
+ return [head, body]
22
+ }
23
+
24
+ const generateStruct = (id: string, map: Map<string, string>) => {
25
+ return `struct ${id} {\n ${Array.from(map.values()).join(',\n ')}\n}`
26
+ }
27
+
28
+ export const vertex = (x: X, c: NodeContext) => {
29
+ c.headers?.clear()
30
+ c.isFrag = false // for varying inputs or outputs
31
+ const [head, body] = generateHead(x, c)
32
+ const ret = []
33
+ if (c.isWebGL) {
34
+ ret.push('#version 300 es')
35
+ for (const code of c.vertInputs!.values()) ret.push(`in ${code}`)
36
+ for (const code of c.vertOutputs!.values()) ret.push(`out ${code}`)
37
+ ret.push(head)
38
+ ret.push('void main() {')
39
+ ret.push(` gl_Position = ${body};`)
40
+ for (const [id, code] of c.vertVaryings!.entries()) ret.push(` ${id} = ${code};`)
41
+ } else {
42
+ if (c.vertInputs?.size) ret.push(generateStruct('In', c.vertInputs))
43
+ if (c.vertOutputs?.size) ret.push(generateStruct('Out', c.vertOutputs))
44
+ ret.push(head)
45
+ ret.push('@vertex')
46
+ ret.push(`fn main(${c.vertInputs?.size ? 'in: In' : ''}) -> Out {`)
47
+ ret.push(' var out: Out;')
48
+ ret.push(` out.position = ${body};`)
49
+ for (const [id, code] of c.vertVaryings!.entries()) ret.push(` out.${id} = ${code};`)
50
+ ret.push(' return out;')
51
+ }
52
+ ret.push('}')
53
+ const main = ret.filter(Boolean).join('\n')
54
+ console.log(`↓↓↓generated↓↓↓\n${main}`)
55
+ return main
56
+ }
57
+
58
+ export const fragment = (x: X, c: NodeContext) => {
59
+ c.headers?.clear()
60
+ c.isFrag = true // for varying inputs or outputs
61
+ const [head, body] = generateHead(x, c)
62
+ const ret = []
63
+ if (c.isWebGL) {
64
+ ret.push(GLSL_FRAGMENT_HEAD)
65
+ for (const code of c.fragInputs!.values()) ret.push(`in ${code}`)
66
+ ret.push(head)
67
+ ret.push(`void main() {\n fragColor = ${body};`)
68
+ } else {
69
+ if (c.fragInputs?.size) ret.push(generateStruct('Out', c.fragInputs))
70
+ ret.push(head)
71
+ ret.push(`@fragment\nfn main(out: Out) -> @location(0) vec4f {`)
72
+ ret.push(` return ${body};`)
73
+ }
74
+ ret.push('}')
75
+ const main = ret.filter(Boolean).join('\n')
76
+ console.log(`↓↓↓generated↓↓↓\n${main}`)
77
+ return main
78
+ }
79
+
13
80
  // Builtin Variables
14
81
  export const position = builtin('position')
15
82
  export const vertexIndex = builtin('vertex_index')
@@ -50,15 +117,18 @@ export const uvec4 = (x?: X, y?: X, z?: X, w?: X) => c('uvec4', x, y, z, w)
50
117
  export const bvec2 = (x?: X, y?: X) => c('bvec2', x, y)
51
118
  export const bvec3 = (x?: X, y?: X, z?: X) => c('bvec3', x, y, z)
52
119
  export const bvec4 = (x?: X, y?: X, z?: X, w?: X) => c('bvec4', x, y, z, w)
120
+ export const texture2D = () => c('texture')
121
+ export const sampler2D = () => c('sampler2D')
53
122
  export const color = (r?: X, g?: X, b?: X) => {
54
123
  if (is.num(r) && is.und(g) && is.und(b)) return vec3(...hex2rgb(r))
55
124
  return vec3(r, g, b)
56
125
  }
57
126
 
58
127
  // Default uniforms
59
- export const iResolution = uniform(vec2(1280, 800), 'iResolution')
60
- export const iMouse = uniform(vec2(0, 0), 'iMouse')
61
- export const iTime = uniform(float(0), 'iTime')
128
+ export const iResolution = u(vec2(1280, 800), 'iResolution')
129
+ export const iMouse = u(vec2(0, 0), 'iMouse')
130
+ export const iTime = u(float(0), 'iTime')
131
+ export const uv = () => position.xy.div(iResolution)
62
132
 
63
133
  // Texture Functions
64
134
  export const texture = (x: X, y: X, z?: X) => f('texture', x, y, z)
package/src/node/infer.ts CHANGED
@@ -13,10 +13,10 @@ import {
13
13
  VEC3_RETURN_FUNCTIONS,
14
14
  VEC4_RETURN_FUNCTIONS,
15
15
  } from './const'
16
- import { isNodeProxy } from './utils'
17
- import type { Constants, NodeConfig, NodeProxy, X } from './types'
16
+ import { isConstantsType, isNodeProxy } from './utils'
17
+ import type { Constants, NodeContext, NodeProxy, X } from './types'
18
18
 
19
- const getHighestPriorityType = (args: X[], c: NodeConfig) => {
19
+ const getHighestPriorityType = (args: X[], c: NodeContext) => {
20
20
  return args.reduce((highest, current) => {
21
21
  const currentType = infer(current, c)
22
22
  const highestPriority = CONSTANTS.indexOf(highest as any)
@@ -29,7 +29,7 @@ const inferBuiltin = (id: string | undefined): Constants => {
29
29
  return BUILTIN_TYPES[id as keyof typeof BUILTIN_TYPES]!
30
30
  }
31
31
 
32
- const inferFunction = (funcName: string, args: X[], c: NodeConfig): Constants => {
32
+ const inferFunction = (funcName: string, args: X[], c: NodeContext): Constants => {
33
33
  const firstArgType = args.length > 0 ? infer(args[0], c) : 'float'
34
34
  if (FIRST_ARG_TYPE_FUNCTIONS.includes(funcName as any)) return firstArgType
35
35
  if (SCALAR_RETURN_FUNCTIONS.includes(funcName as any)) return 'float'
@@ -52,9 +52,9 @@ const inferOperator = (leftType: string, rightType: string, op: string): Constan
52
52
  return (leftPriority >= rightPriority ? leftType : rightType) as Constants
53
53
  }
54
54
 
55
- const inferPrimitiveType = (x: any): Constants => {
55
+ export const inferPrimitiveType = (x: any): Constants => {
56
56
  if (is.bol(x)) return 'bool'
57
- if (is.num(x)) return 'float' // Number.isInteger(x) ? 'int' : 'float' // @TODO FIX
57
+ if (is.num(x)) return 'float'
58
58
  if (is.arr(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] || 'float'
59
59
  return 'float'
60
60
  }
@@ -63,25 +63,30 @@ const inferSwizzle = (count: number): Constants => {
63
63
  return COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE]!
64
64
  }
65
65
 
66
- export const inferImpl = (target: NodeProxy, c: NodeConfig): Constants => {
66
+ const inferFromArray = (arr: X[], c: NodeContext): Constants => {
67
+ if (arr.length === 0) return 'void'
68
+ const ret = infer(arr[0], c)
69
+ for (const x of arr.slice(1))
70
+ if (ret !== infer(x)) throw new Error(`glre node system error: defined scope return mismatch`)
71
+ return ret
72
+ }
73
+
74
+ export const inferImpl = (target: NodeProxy, c: NodeContext): Constants => {
67
75
  const { type, props } = target
68
- const { id, children = [], inferFrom } = props
76
+ const { id, children = [], layout, inferFrom } = props
69
77
  const [x, y, z] = children
70
- if (inferFrom) return infer(inferFrom, c)
71
78
  if (type === 'conversion') return x as Constants
72
79
  if (type === 'operator') return inferOperator(infer(y, c), infer(z, c), x as string)
73
80
  if (type === 'function') return inferFunction(x as string, children.slice(1), c)
74
81
  if (type === 'swizzle') return inferSwizzle((x as string).length)
75
82
  if (type === 'ternary') return inferOperator(infer(y, c), infer(z, c), 'add')
76
83
  if (type === 'builtin') return inferBuiltin(id)
77
- if (type === 'define') {
78
- if (props.layout?.type && props.layout?.type !== 'auto') return props.layout.type as Constants
79
- return y ? infer(y, c) : 'void'
80
- }
84
+ if (type === 'define' && isConstantsType(layout?.type)) return layout?.type
85
+ if (inferFrom) return inferFromArray(inferFrom, c)
81
86
  return infer(x, c)
82
87
  }
83
88
 
84
- export const infer = (target: X, c?: NodeConfig | null): Constants => {
89
+ export const infer = (target: X, c?: NodeContext | null): Constants => {
85
90
  if (!c) c = {}
86
91
  if (!isNodeProxy(target)) return inferPrimitiveType(target)
87
92
  if (!c.infers) c.infers = new WeakMap<NodeProxy, Constants>()
package/src/node/node.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { code } from './code'
2
2
  import { assign, toVar } from './scope'
3
- import { conversionToConstant, isConversion, isFunction, isOperator, isSwizzle } from './utils'
3
+ import { conversionToConstant, isConversion, isFunction, isOperator, isSwizzle, getId, isNodeProxy } from './utils'
4
4
  import type { Functions, NodeProps, NodeProxy, NodeTypes, Operators, Swizzles, X } from './types'
5
5
 
6
6
  const toPrimitive = (x: X, hint: string) => {
@@ -35,18 +35,16 @@ export const node = (type: NodeTypes, props?: NodeProps | null, ...args: X[]) =>
35
35
  }
36
36
 
37
37
  // headers
38
- export const attribute = (x: X, id?: string) => node('varying', { id }, x)
39
- export const uniform = (x: X, id?: string) => node('uniform', { id }, x)
40
- export const varying = (x: X, id?: string) => node('varying', { id }, x)
41
- export const constant = (x: X, id?: string) => node('constant', { id }, x)
42
- export const variable = (id: string) => node('variable', { id })
43
- export const builtin = (id: string) => node('builtin', { id })
38
+ export const attribute = (x: X, id = getId()) => node('attribute', { id }, x)
39
+ export const constant = (x: X, id = getId()) => node('constant', { id }, x)
40
+ export const uniform = (x: X, id = getId()) => node('uniform', { id }, x)
41
+ export const variable = (id = getId()) => node('variable', { id })
42
+ export const builtin = (id = getId()) => node('builtin', { id })
43
+ export const vertexStage = (x: X, id = getId()) => node('varying', { id, inferFrom: [x] }, x)
44
44
 
45
45
  // Node shorthands
46
- export const swizzle = (key: Swizzles, arg: X) => node('swizzle', null, key, arg)
47
- export const operator = (key: Operators, ...args: X[]) => node('operator', null, key, ...args)
48
- export const function_ = (key: Functions, ...args: X[]) => node('function', null, key, ...args)
49
- export const conversion = (key: string, ...args: X[]) => node('conversion', null, key, ...args)
50
-
51
- // x ? y : z
52
- export const select = (x: X, y: X, z: X) => node('ternary', null, x, y, z)
46
+ export const swizzle = (key: Swizzles, x: X) => node('swizzle', null, key, x)
47
+ export const operator = (key: Operators, ...x: X[]) => node('operator', null, key, ...x)
48
+ export const function_ = (key: Functions, ...x: X[]) => node('function', null, key, ...x)
49
+ export const conversion = (key: string, ...x: X[]) => node('conversion', null, key, ...x)
50
+ export const select = (x: X, y: X, z: X) => node('ternary', null, x, y, z) // x ? y : z
@@ -0,0 +1,118 @@
1
+ import { infer } from './infer'
2
+ import { formatConversions, joins } from './utils'
3
+ import { code } from './code'
4
+ import type { Constants, NodeContext, NodeProps, X } from './types'
5
+
6
+ export const parseTexture = (c: NodeContext, y: X, z: X, w: X) => {
7
+ if (c.isWebGL) {
8
+ const args = w ? [y, z, w] : [y, z]
9
+ return `texture(${joins(args, c)})`
10
+ }
11
+ const _y = code(y, c)
12
+ const args = [_y, _y + 'Sampler', code(z, c)]
13
+ if (!w) return `textureSample(${args})`
14
+ args.push(code(w, c))
15
+ return `textureSampleLevel(${args})`
16
+ }
17
+
18
+ /**
19
+ * scopes
20
+ */
21
+ export const parseIf = (c: NodeContext, x: X, y: X, children: X[]) => {
22
+ let ret = `if (${code(x, c)}) {\n${code(y, c)}\n}`
23
+ for (let i = 2; i < children.length; i += 2) {
24
+ const isElse = i >= children.length - 1
25
+ ret += !isElse
26
+ ? ` else if (${code(children[i], c)}) {\n${code(children[i + 1], c)}\n}`
27
+ : ` else {\n${code(children[i], c)}\n}`
28
+ }
29
+ return ret
30
+ }
31
+
32
+ export const parseSwitch = (c: NodeContext, x: X, children: X[]) => {
33
+ let ret = `switch (${code(x, c)}) {\n`
34
+ for (let i = 1; i < children.length; i += 2) {
35
+ const isDefault = i >= children.length - 1
36
+ if (isDefault && children.length % 2 === 0) {
37
+ ret += `default:\n${code(children[i], c)}\nbreak;\n`
38
+ } else if (i + 1 < children.length)
39
+ ret += `case ${code(children[i], c)}:\n${code(children[i + 1], c)}\nbreak;\n`
40
+ }
41
+ ret += '}'
42
+ return ret
43
+ }
44
+
45
+ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
46
+ const varType = infer(x, c)
47
+ const varName = (y as any)?.props?.id
48
+ if (c.isWebGL) return `${varType} ${varName} = ${code(x, c)};`
49
+ const wgslType = formatConversions(varType)
50
+ return `var ${varName}: ${wgslType} = ${code(x, c)};`
51
+ }
52
+
53
+ export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
54
+ const { id, children = [], layout } = props
55
+ const [x, ...args] = children
56
+ const argParams: [name: string, type: string][] = []
57
+ const params: string[] = []
58
+ if (layout?.inputs)
59
+ for (const input of layout.inputs) {
60
+ argParams.push([input.name, input.type])
61
+ }
62
+ else
63
+ for (let i = 0; i < args.length; i++) {
64
+ argParams.push([`p${i}`, infer(args[i], c)])
65
+ }
66
+ const ret = []
67
+ if (c?.isWebGL) {
68
+ for (const [id, type] of argParams) params.push(`${type} ${id}`)
69
+ ret.push(`${returnType} ${id}(${params}) {`)
70
+ } else {
71
+ for (const [id, type] of argParams) params.push(`${id}: ${formatConversions(type, c)}`)
72
+ ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
73
+ }
74
+ const scopeCode = code(x, c)
75
+ if (scopeCode) ret.push(scopeCode)
76
+ ret.push('}')
77
+ return ret.join('\n')
78
+ }
79
+
80
+ /**
81
+ * headers
82
+ */
83
+ export const parseVaryingHead = (c: NodeContext, id: string, varType: string) => {
84
+ return c.isWebGL
85
+ ? `${varType} ${id};`
86
+ : `@location(${c.vertVaryings!.size}) ${id}: ${formatConversions(varType, c)}`
87
+ }
88
+
89
+ export const parseUniformHead = (c: NodeContext, id: string, varType: Constants) => {
90
+ const isTexture = varType === 'sampler2D' || varType === 'texture'
91
+ if (c.isWebGL)
92
+ return isTexture //
93
+ ? `uniform sampler2D ${id};`
94
+ : `uniform ${varType} ${id};`
95
+ if (isTexture) {
96
+ const { group = 1, binding = 0 } = c.webgpu?.textures.map.get(id) || {}
97
+ return (
98
+ `@group(${group}) @binding(${binding}) var ${id}Sampler: sampler;\n` +
99
+ `@group(${group}) @binding(${binding + 1}) var ${id}: texture_2d<f32>;`
100
+ )
101
+ }
102
+ const { group = 0, binding = 0 } = c.webgpu?.uniforms.map.get(id) || {}
103
+ const wgslType = formatConversions(varType, c)
104
+ return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
105
+ }
106
+
107
+ export const parseAttribHead = (c: NodeContext, id: string, varType: Constants) => {
108
+ if (c.isWebGL) return `${varType} ${id};`
109
+ const { location = 0 } = c.webgpu?.attribs.map.get(id) || {}
110
+ const wgslType = formatConversions(varType, c)
111
+ return `@location(${location}) ${id}: ${wgslType}`
112
+ }
113
+
114
+ export const parseConstantHead = (c: NodeContext, id: string, varType: Constants, value: string) => {
115
+ return c.isWebGL
116
+ ? `const ${varType} ${id} = ${value};`
117
+ : `const ${id}: ${formatConversions(varType, c)} = ${value};`
118
+ }
package/src/node/scope.ts CHANGED
@@ -3,24 +3,22 @@ import { conversion, node } from './node'
3
3
  import { getId } from './utils'
4
4
  import type { FnLayout, NodeProps, NodeProxy, X } from './types'
5
5
 
6
- let _scope: NodeProxy | null = null
7
-
8
- const scoped = (x: NodeProxy | null, fun = () => {}) => {
9
- const prev = _scope
10
- _scope = x
11
- fun()
12
- _scope = prev
13
- }
6
+ let scope: NodeProxy | null = null
7
+ let define: NodeProxy | null = null
14
8
 
15
9
  const addToScope = (x: NodeProxy) => {
16
- if (!_scope) return // ignore
17
- if (!_scope.props.children) _scope.props.children = []
18
- _scope.props.children.push(x)
10
+ if (!scope) return
11
+ if (!scope.props.children) scope.props.children = []
12
+ scope.props.children.push(x)
13
+ if (x.type !== 'return' || !define) return
14
+ const { props } = define
15
+ if (!props.inferFrom) props.inferFrom = []
16
+ props.inferFrom.push(x)
19
17
  }
20
18
 
21
19
  export const toVar = (x: X, id?: string) => {
22
20
  if (!id) id = getId()
23
- const y = node('variable', { id, inferFrom: x })
21
+ const y = node('variable', { id, inferFrom: [x] })
24
22
  const z = node('declare', null, x, y)
25
23
  addToScope(z)
26
24
  return y
@@ -32,6 +30,27 @@ export const assign = (x: X, y: X) => {
32
30
  return x
33
31
  }
34
32
 
33
+ export const Return = (x: X) => {
34
+ const y = node('return', { inferFrom: [x] }, x)
35
+ addToScope(y)
36
+ return y
37
+ }
38
+
39
+ const scoped = (x: NodeProxy, fun: () => NodeProxy | void, y = define) => {
40
+ // cache to revert
41
+ const _scope = scope
42
+ const _define = define
43
+ // update
44
+ scope = x
45
+ define = y
46
+ if (_scope) x.props.parent = _scope
47
+ const z = fun()
48
+ if (z) Return(z)
49
+ // revert
50
+ scope = _scope
51
+ define = _define
52
+ }
53
+
35
54
  export const If = (x: X, fun: () => void) => {
36
55
  const y = node('scope')
37
56
  scoped(y, fun)
@@ -55,7 +74,7 @@ export const If = (x: X, fun: () => void) => {
55
74
 
56
75
  export const Loop = (x: X, fun: (params: { i: NodeProxy }) => void) => {
57
76
  const y = node('scope')
58
- scoped(y, () => fun({ i: node('variable', { id: 'i', inferFrom: int(0) }) }))
77
+ scoped(y, () => fun({ i: node('variable', { id: 'i', inferFrom: [int(0)] }) }))
59
78
  const ret = node('loop', null, x, y)
60
79
  addToScope(ret)
61
80
  return ret
@@ -88,20 +107,20 @@ export const Fn = (fun: (paramVars: NodeProxy[]) => NodeProxy) => {
88
107
  const ret = (...args: X[]) => {
89
108
  const id = layout?.name || getId()
90
109
  const x = node('scope')
91
- let y: NodeProxy | undefined
92
110
  const paramVars: NodeProxy[] = []
93
111
  const paramDefs: NodeProps[] = []
94
112
  if (layout?.inputs)
95
113
  for (const input of layout.inputs) {
96
- paramDefs.push({ id: input.name, inferFrom: conversion(input.type) })
114
+ paramDefs.push({ id: input.name, inferFrom: [conversion(input.type)] })
97
115
  }
98
116
  else
99
117
  for (let i = 0; i < args.length; i++) {
100
- paramDefs.push({ id: `p${i}`, inferFrom: args[i] })
118
+ paramDefs.push({ id: `p${i}`, inferFrom: [args[i]] })
101
119
  }
102
120
  for (const props of paramDefs) paramVars.push(node('variable', props))
103
- scoped(x, () => (y = fun(paramVars)))
104
- return node('define', { id, layout }, x, y, ...args)
121
+ const y = node('define', { id, layout }, x, ...args)
122
+ scoped(x, () => fun(paramVars), y)
123
+ return y
105
124
  }
106
125
  ret.setLayout = (newLayout: FnLayout) => {
107
126
  layout = newLayout