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.
@@ -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: '', // IGNORED
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',
@@ -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') return `${getConversions(x, c)}(${parseArray(children.slice(1), c)})`
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)
@@ -1,5 +1,5 @@
1
1
  import { isConstants, isElement, isX, isSwizzle } from './utils'
2
- import { BUILTIN_TYPES, COMPONENT_COUNT_TO_TYPE, FUNCTION_RETURN_TYPES, getOperatorResultType, validateOperatorTypes } from './const'
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: Number.isInteger(x) ? 'int' : 'float'
19
- if (is.arr(x) || isFloat32(x)) return COMPONENT_COUNT_TO_TYPE[x.length as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
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 = COMPONENT_COUNT_TO_TYPE[count as keyof typeof COMPONENT_COUNT_TO_TYPE] as T
26
- if (!ret) throw `glre node system error: Cannot infer type from array length ${count}. Check your data size. Supported: 1(float), 2(vec2), 3(vec3), 4(vec4), 9(mat3), 16(mat4)`
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' && is.arr(x)) {
58
- const stride = getStride(x.length, c.gl?.count, c.gl?.error)
59
- return inferFromCount(stride)
60
- }
61
- if (type === 'instance' && is.arr(x)) {
62
- const stride = getStride(x.length, c.gl?.instanceCount, c.gl?.error)
63
- return inferFromCount(stride)
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 (isSwizzle(y)) return inferFromCount(y.length)
67
- if (isX(x)) {
68
- const structType = infer(x, c)
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 'float' as T
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)
@@ -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
- encoder: GPUCommandEncoder
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 {
@@ -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
- if (!gl.cs) return
8
- const c = gl.gl
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
- const cs = is.str(gl.cs) ? gl.cs : gl.cs!.compute({ isWebGL: true, gl, units })
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(gl.particleCount)
17
+ const size = storageSize(particleCount)
18
18
 
19
19
  const uniforms = nested((key) => c.getUniformLocation(pg, key)!)
20
20
  const storages = nested((key) => {
@@ -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
- const c = gl.gl
9
- const fs = gl.fs ? (is.str(gl.fs) ? gl.fs : gl.fs.fragment(config)) : GLSL_FS
10
- const vs = gl.vs ? (is.str(gl.vs) ? gl.vs : gl.vs.vertex(config)) : GLSL_VS
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.triangleCount, gl.error)
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.triangleCount, gl.instanceCount)
60
- } else c.drawArrays(c.TRIANGLES, 0, gl.triangleCount)
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
  }
@@ -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, bindings: Binding) => {
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 { ...bindings.storage(key), ...createBuffer(gl.device, value, 'storage') }
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.encoder.beginComputePass()
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(gl.particleCount)
24
+ const { x, y, z } = workgroupCount(particleCount)
25
25
  pass.dispatchWorkgroups(x, y, z)
26
26
  pass.end()
27
27
  })
@@ -1,28 +1,27 @@
1
1
  import { nested } from 'reev'
2
- import { is, getStride, loadingTexture } from '../helpers'
3
- import { createBuffer, createDepthTexture, createDescriptor, createTextureSampler, updateBuffer } from './utils'
4
- import type { Binding } from './utils'
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, bindings: Binding, update = () => {}) => {
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 ? gl.instanceCount : gl.triangleCount)) => {
12
+ const attributes = nested((key, value: number[], isInstance = false, stride = getStride(value.length, isInstance ? instanceCount : count, gl.error, key)) => {
14
13
  update()
15
- return { ...bindings.attrib(key), ...createBuffer(gl.device, value, 'attrib'), isInstance, stride }
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 { ...bindings.uniform(key), ...createBuffer(gl.device, value, 'uniform') }
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 { ...bindings.texture(key), ...createTextureSampler(gl.device, width, height) }
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
- if (t.texture.width !== width || t.texture.height !== height) {
49
- t.texture.destroy()
50
- Object.assign(t, createTextureSampler(gl.device, width, height))
51
- update() // Rebuilding BindGroups because the texture size has changed
52
- }
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
- const pass = gl.encoder.beginRenderPass(createDescriptor(gl.gpu, depthTexture))
62
- pass.setPipeline(pipeline)
63
- bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
64
- vertexBuffers.forEach((v, i) => pass.setVertexBuffer(i, v))
65
- pass.draw(gl.triangleCount, gl.instanceCount, 0, 0)
66
- pass.end()
67
- })
68
-
69
- gl('resize', () => {
70
- const canvas = gl.el as HTMLCanvasElement
71
- depthTexture?.destroy()
72
- depthTexture = createDepthTexture(gl.device, canvas.width, canvas.height)
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()
@@ -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 isUpdate = false
9
- const isInit = !gl.gl
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
- const binding = createBinding()
22
- const c = compute(gl, binding)
23
- const g = graphic(gl, binding, () => (isUpdate = true))
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, binding }
27
- const fs = gl.fs ? (is.str(gl.fs) ? gl.fs : gl.fs.fragment(config)) : WGSL_FS
28
- const vs = gl.vs ? (is.str(gl.vs) ? gl.vs : gl.vs.vertex(config)) : WGSL_VS
29
- const cs = gl.cs ? (is.str(gl.cs) ? gl.cs : gl.cs.compute(config)) : ''
30
- const p = updatePipeline(gl.device, gl.format, g.attributes.map.values(), g.uniforms.map.values(), g.textures.map.values(), c.storages.map.values(), fs, cs, vs)
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
- gl('render', () => {
36
- if (gl.encoder) gl.device.queue.submit([gl.encoder.finish()])
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
  }
@@ -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: i } of uniforms) {
108
- add(i, { binding, visibility: 7, buffer: { type: 'uniform' } }, { binding, resource: { buffer } })
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: i } of storages) {
111
- add(i, { binding, visibility: 6, buffer: { type: 'storage' } }, { binding, resource: { buffer } })
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: i, sampler, view } of textures) {
114
- add(i, { binding: b, visibility: 2, sampler: {} }, { binding: b, resource: sampler })
115
- add(i, { binding: b + 1, visibility: 2, texture: {} }, { binding: b + 1, resource: view })
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
- return device.createRenderPipeline({
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
- layout: device.createPipelineLayout({ bindGroupLayouts }),
137
- primitive: { topology: 'triangle-list' },
138
- depthStencil: {
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: GPUTexture) => {
188
- return {
189
- colorAttachments: [{ view: c.getCurrentTexture().createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }],
190
- depthStencilAttachment: { view: depthTexture.createView(), depthClearValue: 1.0, depthLoadOp: 'clear', depthStoreOp: 'store' },
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
  /**