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,3 +1,6 @@
1
+ import { is } from './helpers'
2
+ import type { GL } from '../types'
3
+
1
4
  const createShader = (c: WebGLRenderingContext, source: string, type: number, onError = console.warn) => {
2
5
  const shader = c.createShader(type)
3
6
  if (!shader) return onError('Failed to create shader')
@@ -9,21 +12,21 @@ const createShader = (c: WebGLRenderingContext, source: string, type: number, on
9
12
  onError(`Could not compile shader: ${error}`)
10
13
  }
11
14
 
12
- export const createProgram = (c: WebGLRenderingContext, vert: string, frag: string, onError = console.warn) => {
15
+ export const createProgram = (c: WebGLRenderingContext, frag: string, vert: string, gl: GL) => {
13
16
  const pg = c.createProgram()
14
- const fs = createShader(c, frag, c.FRAGMENT_SHADER, onError)
15
- const vs = createShader(c, vert, c.VERTEX_SHADER, onError)
17
+ const fs = createShader(c, frag, c.FRAGMENT_SHADER, gl.error)
18
+ const vs = createShader(c, vert, c.VERTEX_SHADER, gl.error)
16
19
  if (!fs || !vs) return
17
- c.attachShader(pg, vs!)
18
20
  c.attachShader(pg, fs!)
21
+ c.attachShader(pg, vs!)
19
22
  c.linkProgram(pg)
20
23
  if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
21
24
  const error = c.getProgramInfoLog(pg)
22
25
  c.deleteProgram(pg)
23
- onError(`Could not link program: ${error}`)
26
+ gl.error(`Could not link program: ${error}`)
24
27
  }
25
28
 
26
- export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
29
+ const createVbo = (c: WebGLRenderingContext, data: number[]) => {
27
30
  const buffer = c.createBuffer()
28
31
  c.bindBuffer(c.ARRAY_BUFFER, buffer)
29
32
  c.bufferData(c.ARRAY_BUFFER, new Float32Array(data), c.STATIC_DRAW)
@@ -31,7 +34,7 @@ export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
31
34
  return buffer
32
35
  }
33
36
 
34
- export const createIbo = (c: WebGLRenderingContext, data: number[]) => {
37
+ const createIbo = (c: WebGLRenderingContext, data: number[]) => {
35
38
  const buffer = c.createBuffer()
36
39
  c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, buffer)
37
40
  c.bufferData(c.ELEMENT_ARRAY_BUFFER, new Int16Array(data), c.STATIC_DRAW)
@@ -39,7 +42,7 @@ export const createIbo = (c: WebGLRenderingContext, data: number[]) => {
39
42
  return buffer
40
43
  }
41
44
 
42
- export const getStride = (count: number, value: number[], iboValue?: number[]) => {
45
+ const getStride = (count: number, value: number[], iboValue?: number[]) => {
43
46
  if (iboValue) count = Math.max(...iboValue) + 1
44
47
  const stride = value.length / count
45
48
  return Math.floor(stride)
@@ -47,17 +50,28 @@ export const getStride = (count: number, value: number[], iboValue?: number[]) =
47
50
 
48
51
  export const createAttrib = (
49
52
  c: WebGLRenderingContext,
50
- stride: number,
51
- loc: any,
52
- vbo: WebGLBuffer,
53
- ibo?: WebGLBuffer
53
+ loc: number,
54
+ count: number,
55
+ value: number[],
56
+ iboValue: number[]
54
57
  ) => {
58
+ const vbo = createVbo(c, value)
59
+ const ibo = createIbo(c, iboValue)
60
+ const str = getStride(count, value, iboValue)
55
61
  c.bindBuffer(c.ARRAY_BUFFER, vbo)
56
62
  c.enableVertexAttribArray(loc)
57
- c.vertexAttribPointer(loc, stride, c.FLOAT, false, 0, 0)
63
+ c.vertexAttribPointer(loc, str, c.FLOAT, false, 0, 0)
58
64
  if (ibo) c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, ibo)
59
65
  }
60
66
 
67
+ export const createUniform = (c: WebGLRenderingContext, loc: WebGLUniformLocation, value: number | number[]) => {
68
+ if (is.num(value)) return c.uniform1f(loc, value)
69
+ let l = value.length
70
+ if (l <= 4) return c[`uniform${l as 2}fv`](loc, value)
71
+ l = Math.sqrt(l) << 0
72
+ c[`uniformMatrix${l as 2}fv`](loc, false, value)
73
+ }
74
+
61
75
  export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement, loc: any, unit: number) => {
62
76
  const texture = c.createTexture()
63
77
  c.bindTexture(c.TEXTURE_2D, texture)
@@ -72,3 +86,71 @@ export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement, l
72
86
  c.activeTexture(c.TEXTURE0 + unit)
73
87
  c.bindTexture(c.TEXTURE_2D, texture)
74
88
  }
89
+
90
+ /**
91
+ * for gpgpu
92
+ */
93
+ interface TextureBuffer {
94
+ texture: WebGLTexture
95
+ buffer: WebGLFramebuffer
96
+ }
97
+
98
+ export const createStorage = (
99
+ c: WebGL2RenderingContext,
100
+ value: number[],
101
+ size: number,
102
+ ping: TextureBuffer,
103
+ pong: TextureBuffer,
104
+ unit: number,
105
+ array: Float32Array
106
+ ) => {
107
+ const particles = size * size
108
+ const vectorSize = value.length / particles
109
+ for (let i = 0; i < particles; i++) {
110
+ for (let j = 0; j < Math.min(vectorSize, 4); j++) {
111
+ array[4 * i + j] = value[i * vectorSize + j] || 0
112
+ }
113
+ }
114
+ c.activeTexture(c.TEXTURE0 + unit)
115
+ c.bindTexture(c.TEXTURE_2D, ping.texture)
116
+ c.texImage2D(c.TEXTURE_2D, 0, c.RGBA32F, size, size, 0, c.RGBA, c.FLOAT, array)
117
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.NEAREST)
118
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.NEAREST)
119
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
120
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
121
+ c.bindTexture(c.TEXTURE_2D, pong.texture)
122
+ c.texImage2D(c.TEXTURE_2D, 0, c.RGBA32F, size, size, 0, c.RGBA, c.FLOAT, array)
123
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.NEAREST)
124
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.NEAREST)
125
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
126
+ c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
127
+ }
128
+
129
+ export const cleanStorage = (
130
+ c: WebGL2RenderingContext,
131
+ map: Iterable<{ ping: TextureBuffer; pong: TextureBuffer }>
132
+ ) => {
133
+ for (const { ping, pong } of map) {
134
+ c.deleteTexture(ping.texture)
135
+ c.deleteTexture(pong.texture)
136
+ c.deleteFramebuffer(ping.buffer)
137
+ c.deleteFramebuffer(pong.buffer)
138
+ }
139
+ }
140
+
141
+ export const createAttachment = (
142
+ c: WebGL2RenderingContext,
143
+ i: TextureBuffer,
144
+ o: TextureBuffer,
145
+ loc: WebGLUniformLocation,
146
+ unit: number,
147
+ index: number
148
+ ) => {
149
+ c.activeTexture(c.TEXTURE0 + unit)
150
+ c.bindTexture(c.TEXTURE_2D, i.texture)
151
+ c.uniform1i(loc, unit)
152
+ if (index === 0) c.bindFramebuffer(c.FRAMEBUFFER, o.buffer)
153
+ const attachment = c.COLOR_ATTACHMENT0 + index
154
+ c.framebufferTexture2D(c.FRAMEBUFFER, attachment, c.TEXTURE_2D, o.texture, 0)
155
+ return attachment
156
+ }
package/src/webgl.ts CHANGED
@@ -1,65 +1,122 @@
1
1
  import { nested as cached } from 'reev'
2
- import { fragment, vertex } from './node'
3
- import { is } from './utils/helpers'
4
- import { createAttrib, createIbo, createProgram, createTexture, createVbo, getStride } from './utils/program'
2
+ import { loadingImage } from './utils/helpers'
3
+ import {
4
+ cleanStorage,
5
+ createAttachment,
6
+ createAttrib,
7
+ createProgram,
8
+ createStorage,
9
+ createTexture,
10
+ createUniform,
11
+ } from './utils/program'
12
+ import { compute, fragment, vertex } from './node'
5
13
  import type { GL, WebGLState } from './types'
6
14
 
7
- export const webgl = async (gl: GL) => {
8
- const c = gl.el!.getContext('webgl2')!
9
- const config = { isWebGL: true, gl }
10
- const fs = fragment(gl.fs, config)
11
- const vs = vertex(gl.vs, config)
12
- const pg = createProgram(c, vs, fs, gl.error)!
13
- c.useProgram(pg)
15
+ const vert = /* cpp */ `
16
+ #version 300 es
17
+ void main() {
18
+ float x = float(gl_VertexID % 2) * 4.0 - 1.0;
19
+ float y = float(gl_VertexID / 2) * 4.0 - 1.0;
20
+ gl_Position = vec4(x, y, 0.0, 1.0);
21
+ }`.trim()
22
+
23
+ const computeProgram = (gl: GL, c: WebGL2RenderingContext) => {
24
+ if (!gl.cs) return null // ignore if no compute shader
25
+ c.getExtension('EXT_color_buffer_float')
26
+
27
+ let activeUnit = 0 // for texture units
28
+ let currentNum = 0 // for storage buffers
14
29
 
15
- let activeUnit = 0
16
- const uniforms = cached((key) => c.getUniformLocation(pg, key))
17
- const attribs = cached((key) => c.getAttribLocation(pg, key))
18
30
  const units = cached(() => activeUnit++)
31
+ const config = { isWebGL: true, gl, units }
32
+
33
+ const pg = createProgram(c, compute(gl.cs, config), vert, gl)!
34
+ const size = Math.ceil(Math.sqrt(gl.particles))
35
+
36
+ const uniforms = cached((key) => c.getUniformLocation(pg, key)!)
37
+ const storages = cached((key) => {
38
+ const array = new Float32Array(size * size * 4) // RGBA texture data
39
+ const ping = { texture: c.createTexture(), buffer: c.createFramebuffer() }
40
+ const pong = { texture: c.createTexture(), buffer: c.createFramebuffer() }
41
+ return { ping, pong, array, loc: uniforms(key), unit: units(key) }
42
+ })
43
+
44
+ const _uniform = (key: string, value: number | number[]) => {
45
+ c.useProgram(pg)
46
+ createUniform(c, uniforms(key), value)
47
+ }
48
+
49
+ const _storage = (key: string, value: number[]) => {
50
+ const { ping, pong, unit, array } = storages(key)
51
+ createStorage(c, value, size, ping, pong, unit, array)
52
+ }
19
53
 
20
54
  const clean = () => {
21
55
  c.deleteProgram(pg)
22
- c.getExtension('WEBGL_lose_context')?.loseContext()
23
- gl.el.width = 1
24
- gl.el.height = 1
56
+ cleanStorage(c, storages.map.values())
25
57
  }
26
58
 
27
59
  const render = () => {
28
- c.clear(c.COLOR_BUFFER_BIT)
29
- c.viewport(0, 0, ...gl.size)
60
+ c.useProgram(pg)
61
+ const attachments = storages.map.values().map(({ ping, pong, loc, unit }, index) => {
62
+ const [i, o] = currentNum % 2 ? [ping, pong] : [pong, ping]
63
+ return createAttachment(c, i, o, loc, unit, index)
64
+ })
65
+ c.drawBuffers(attachments)
30
66
  c.drawArrays(c.TRIANGLES, 0, 3)
67
+ c.bindFramebuffer(c.FRAMEBUFFER, null)
68
+ currentNum++
31
69
  }
32
70
 
71
+ return { render, clean, _uniform, _storage, storages }
72
+ }
73
+
74
+ export const webgl = async (gl: GL) => {
75
+ const config = { isWebGL: true, gl }
76
+ const c = gl.el!.getContext('webgl2')!
77
+ const cp = computeProgram(gl, c)
78
+ const pg = createProgram(c, fragment(gl.fs, config), vertex(gl.vs, config), gl)!
79
+ c.useProgram(pg)
80
+
81
+ let activeUnit = 0 // for texture units
82
+
83
+ const units = cached(() => activeUnit++)
84
+ const attribs = cached((key) => c.getAttribLocation(pg, key))
85
+ const uniforms = cached((key) => c.getUniformLocation(pg, key))
86
+
33
87
  const _attribute = (key = '', value: number[], iboValue: number[]) => {
34
88
  const loc = attribs(key, true)
35
- const vbo = createVbo(c, value)
36
- const ibo = createIbo(c, iboValue)
37
- const str = getStride(gl.count, value, iboValue)
38
- createAttrib(c, str, loc, vbo, ibo)
89
+ createAttrib(c, loc, gl.count, value, iboValue)
39
90
  }
40
91
 
41
92
  const _uniform = (key: string, value: number | number[]) => {
42
- const loc = uniforms(key)
43
- if (is.num(value)) return c.uniform1f(loc, value)
44
- let l = value.length
45
- if (l <= 4) return c[`uniform${l as 2}fv`](loc, value)
46
- l = Math.sqrt(l) << 0
47
- c[`uniformMatrix${l as 2}fv`](loc, false, value)
93
+ c.useProgram(pg)
94
+ createUniform(c, uniforms(key)!, value)
95
+ cp?._uniform(key, value)
48
96
  }
49
97
 
50
98
  const _texture = (key: string, src: string) => {
51
- gl.loading++
52
- const image = new Image()
53
- Object.assign(image, { src, crossOrigin: 'anonymous' })
54
- image.decode().then(() => {
55
- const loc = uniforms(key)
56
- const unit = units(key)
57
- createTexture(c, image, loc, unit)
58
- gl.loading--
99
+ c.useProgram(pg)
100
+ loadingImage(gl, src, (source) => {
101
+ createTexture(c, source, uniforms(key), units(key))
59
102
  })
60
103
  }
61
104
 
62
- const webgl: WebGLState = { context: c, program: pg }
105
+ const clean = () => {
106
+ cp?.clean()
107
+ c.deleteProgram(pg)
108
+ c.getExtension('WEBGL_lose_context')?.loseContext()
109
+ }
110
+
111
+ const render = () => {
112
+ cp?.render()
113
+ c.useProgram(pg)
114
+ c.viewport(0, 0, ...gl.size)
115
+ c.drawArrays(c.TRIANGLES, 0, gl.count)
116
+ c.bindFramebuffer(c.FRAMEBUFFER, null)
117
+ }
118
+
119
+ const webgl: WebGLState = { context: c, program: pg, storages: cp?.storages }
63
120
 
64
- return { webgl, render, clean, _attribute, _uniform, _texture }
121
+ return { webgl, render, clean, _attribute, _uniform, _texture, _storage: cp?._storage }
65
122
  }
package/src/webgpu.ts CHANGED
@@ -1,59 +1,119 @@
1
1
  import { nested as cached } from 'reev'
2
- import { is } from './utils/helpers'
2
+ import { is, loadingImage } from './utils/helpers'
3
3
  import {
4
- createAttribBuffer,
5
- createBindings,
4
+ createArrayBuffer,
6
5
  createBindGroup,
6
+ createBindings,
7
+ createComputePipeline,
7
8
  createDepthTexture,
8
9
  createDescriptor,
9
10
  createDevice,
10
11
  createPipeline,
11
12
  createTextureSampler,
12
- createUniformBuffer,
13
13
  createVertexBuffers,
14
14
  } from './utils/pipeline'
15
15
  import type { GL, WebGPUState } from './types'
16
- import { fragment, vertex } from './node'
16
+ import { compute, fragment, vertex } from './node'
17
+
18
+ const WORKING_GROUP_SIZE = 32
19
+
20
+ const computeProgram = (gl: GL, device: GPUDevice, bindings: any) => {
21
+ let flush = (_pass: GPUComputePassEncoder) => {}
22
+
23
+ const storages = cached((_key, value: number[] | Float32Array) => {
24
+ const { array, buffer } = createArrayBuffer(device, value, 'storage')
25
+ const { binding, group } = bindings.storage()
26
+ return { array, buffer, binding, group }
27
+ })
28
+
29
+ const _storage = (key: string, value: number[] | Float32Array) => {
30
+ const { array, buffer } = storages(key, value)
31
+ device.queue.writeBuffer(buffer, 0, array as any)
32
+ }
33
+
34
+ const update = (bindGroups: GPUBindGroup[], bindGroupLayouts: GPUBindGroupLayout[], comp: string) => {
35
+ const pipeline = createComputePipeline(device, bindGroupLayouts, comp!)
36
+ flush = (pass) => {
37
+ pass.setPipeline(pipeline)
38
+ bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
39
+ const workgroupCount = Math.ceil(gl.particles / WORKING_GROUP_SIZE)
40
+ pass.dispatchWorkgroups(workgroupCount, 1, 1)
41
+ pass.end()
42
+ }
43
+ }
44
+
45
+ const render = (pass: GPUComputePassEncoder) => {
46
+ flush(pass)
47
+ }
48
+
49
+ const clean = () => {
50
+ for (const { buffer } of storages.map.values()) buffer.destroy()
51
+ }
52
+
53
+ return { storages, _storage, update, render, clean }
54
+ }
17
55
 
18
56
  export const webgpu = async (gl: GL) => {
19
57
  const context = gl.el!.getContext('webgpu') as GPUCanvasContext
20
- const { device, format } = await createDevice(context)
21
- device.onuncapturederror = (e) => gl.error(e.error.message)
58
+ const { device, format } = await createDevice(context, gl.error)
22
59
  const bindings = createBindings()
60
+ const cp = computeProgram(gl, device, bindings)
23
61
  let frag: string
62
+ let comp: string
24
63
  let vert: string
25
64
  let flush = (_pass: GPURenderPassEncoder) => {}
26
65
  let needsUpdate = true
27
66
  let depthTexture: GPUTexture
28
67
 
68
+ const attribs = cached((_key, value: number[]) => {
69
+ needsUpdate = true
70
+ const stride = value.length / gl.count
71
+ const { location } = bindings.attrib()
72
+ const { array, buffer } = createArrayBuffer(device, value, 'attrib')
73
+ return { array, buffer, location, stride }
74
+ })
75
+
29
76
  const uniforms = cached((_key, value: number[]) => {
30
77
  needsUpdate = true
31
- const { array, buffer } = createUniformBuffer(device, value)
32
78
  const { binding, group } = bindings.uniform()
33
- return { binding, group, array, buffer }
79
+ const { array, buffer } = createArrayBuffer(device, value, 'uniform')
80
+ return { array, buffer, binding, group }
34
81
  })
35
82
 
36
83
  const textures = cached((_key, width = 0, height = 0) => {
37
84
  needsUpdate = true
38
- const { texture, sampler } = createTextureSampler(device, width, height)
39
85
  const { binding, group } = bindings.texture()
40
- return { binding, group, texture, sampler, view: texture.createView() }
86
+ const { texture, sampler } = createTextureSampler(device, width, height)
87
+ return { texture, sampler, binding, group, view: texture.createView() }
41
88
  })
42
89
 
43
- const attribs = cached((_key, value: number[]) => {
44
- needsUpdate = true
45
- const stride = value.length / gl.count
46
- const { location } = bindings.attrib()
47
- const { array, buffer } = createAttribBuffer(device, value)
48
- return { array, buffer, location, stride }
49
- })
90
+ const _attribute = (key = '', value: number[]) => {
91
+ const { array, buffer } = attribs(key, value)
92
+ device.queue.writeBuffer(buffer, 0, array as any)
93
+ }
94
+
95
+ const _uniform = (key: string, value: number | number[]) => {
96
+ if (is.num(value)) value = [value]
97
+ const { array, buffer } = uniforms(key, value)
98
+ array.set(value) // needs to set leatest value
99
+ device.queue.writeBuffer(buffer, 0, array as any)
100
+ }
101
+
102
+ const _texture = (key: string, src: string) => {
103
+ loadingImage(gl, src, (source) => {
104
+ const { width, height } = source
105
+ const { texture } = textures(key, width, height)
106
+ device.queue.copyExternalImageToTexture({ source }, { texture }, { width, height })
107
+ })
108
+ }
50
109
 
51
110
  const update = () => {
52
111
  const { vertexBuffers, bufferLayouts } = createVertexBuffers(attribs.map.values())
53
112
  const { bindGroups, bindGroupLayouts } = createBindGroup(
54
113
  device,
55
114
  uniforms.map.values(),
56
- textures.map.values()
115
+ textures.map.values(),
116
+ cp.storages.map.values()
57
117
  )
58
118
  const pipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, vert, frag)
59
119
  flush = (pass) => {
@@ -63,18 +123,21 @@ export const webgpu = async (gl: GL) => {
63
123
  pass.draw(gl.count, 1, 0, 0)
64
124
  pass.end()
65
125
  }
126
+ if (gl.cs) cp.update(bindGroups, bindGroupLayouts, comp)
66
127
  }
67
128
 
68
129
  const render = () => {
69
130
  if (!frag || !vert) {
70
131
  const config = { isWebGL: false, gl }
71
- frag = fragment(gl.fs, config)
132
+ frag = fragment(gl.fs, config) // needs to be before vertex
72
133
  vert = vertex(gl.vs, config)
134
+ comp = compute(gl.cs, config)
73
135
  }
74
136
  if (gl.loading) return // MEMO: loading after build node
75
137
  if (needsUpdate) update()
76
138
  needsUpdate = false
77
139
  const encoder = device.createCommandEncoder()
140
+ if (gl.cs) cp.render(encoder.beginComputePass())
78
141
  flush(encoder.beginRenderPass(createDescriptor(context, depthTexture)))
79
142
  device.queue.submit([encoder.finish()])
80
143
  }
@@ -91,41 +154,12 @@ export const webgpu = async (gl: GL) => {
91
154
  for (const { texture } of textures.map.values()) texture.destroy()
92
155
  for (const { buffer } of uniforms.map.values()) buffer.destroy()
93
156
  for (const { buffer } of attribs.map.values()) buffer.destroy()
94
- }
95
-
96
- const _attribute = (key = '', value: number[]) => {
97
- const { array, buffer } = attribs(key, value)
98
- array.set(value)
99
- device.queue.writeBuffer(buffer, 0, array)
100
- }
101
-
102
- const _uniform = (key: string, value: number | number[]) => {
103
- if (is.num(value)) value = [value]
104
- const { array, buffer } = uniforms(key, value)
105
- array.set(value)
106
- device.queue.writeBuffer(buffer, 0, array)
107
- }
108
-
109
- const _texture = (key: string, src: string) => {
110
- gl.loading++
111
- const source = Object.assign(new Image(), { src, crossOrigin: 'anonymous' })
112
- source.decode().then(() => {
113
- const { width, height } = source
114
- const { texture } = textures(key, width, height)
115
- device.queue.copyExternalImageToTexture({ source }, { texture }, { width, height })
116
- gl.loading--
117
- })
157
+ cp.clean()
118
158
  }
119
159
 
120
160
  resize()
121
161
 
122
- return {
123
- webgpu: { device, uniforms, textures, attribs } as WebGPUState,
124
- render,
125
- resize,
126
- clean,
127
- _attribute,
128
- _uniform,
129
- _texture,
130
- }
162
+ const webgpu = { device, uniforms, textures, attribs, storages: cp.storages } as WebGPUState
163
+
164
+ return { webgpu, render, resize, clean, _attribute, _uniform, _texture, _storage: cp._storage }
131
165
  }