glre 0.25.0 → 0.27.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/types.ts CHANGED
@@ -1,101 +1,66 @@
1
- import type { NodeType, Operator, MathFunction, Swillzes } from './const'
1
+ import { FUNCTIONS, NODE_TYPES, OPERATOR_KEYS } from './const'
2
2
 
3
- // ノードの基本インターフェース
4
- export interface Node {
5
- id: string
6
- type: NodeType
7
- value?: any
8
- property?: string
9
- parent?: Node
10
- children?: Node[]
11
- operator?: Operator
12
- mathFunction?: MathFunction
13
- }
3
+ export type NodeType = (typeof NODE_TYPES)[number]
14
4
 
15
- // Proxyハンドラーのコールバック型
16
- export interface ProxyCallback {
17
- path: string[]
18
- args: any[]
19
- }
5
+ export type Functions = (typeof FUNCTIONS)[number]
20
6
 
21
- // ノード作成関数の型
22
- export type NodeCreator = (value?: any) => X
7
+ export type Operators = (typeof OPERATOR_KEYS)[number]
23
8
 
24
- // 演算子メソッドの型
25
- export interface OperatorMethods {
26
- add(x: X | number): X
27
- sub(x: X | number): X
28
- mul(x: X | number): X
29
- div(x: X | number): X
30
- mod(x: X | number): X
31
- equal(x: X | number): X
32
- notEqual(x: X | number): X
33
- lessThan(x: X | number): X
34
- lessThanEqual(x: X | number): X
35
- greaterThan(x: X | number): X
36
- greaterThanEqual(x: X | number): X
37
- and(x: X): X
38
- or(x: X): X
39
- not(): X
9
+ export interface NodeProps {
10
+ id?: string
11
+ children?: X[]
12
+ defaultValue?: number | number[]
40
13
  }
41
14
 
42
- // 数学関数メソッドの型
43
- export interface MathMethods {
44
- abs(): X
45
- acos(): X
46
- asin(): X
47
- atan(): X
48
- ceil(): X
49
- cos(): X
50
- floor(): X
51
- fract(): X
52
- length(): X
53
- normalize(): X
54
- sin(): X
55
- sqrt(): X
56
- tan(): X
57
- toVar(): X
15
+ export interface NodeConfig {
16
+ isWebGL?: boolean
17
+ uniforms?: Set<string>
18
+ onUniform?: (name: string, value: any) => void
58
19
  }
59
20
 
60
- // 全てのswizzleパターンをまとめる型
21
+ type _Swizzles<T extends string> = T | `${T}${T}` | `${T}${T}${T}` | `${T}${T}${T}${T}`
61
22
 
62
- // スウィズルプロパティの型
63
- export type SwizzleProperties = {
64
- [k in Swillzes]: X
65
- }
23
+ export type Swizzles =
24
+ | _Swizzles<'x' | 'y' | 'z' | 'w'>
25
+ | _Swizzles<'r' | 'g' | 'b' | 'a'>
26
+ | _Swizzles<'p' | 'q'>
27
+ | _Swizzles<'s' | 't'>
66
28
 
67
- // ノードProxy型
68
- export interface X extends MathMethods, OperatorMethods, SwizzleProperties {
69
- readonly id: string
70
- readonly type: NodeType
71
- readonly value: any
72
- readonly property: string
73
- (...args: any[]): X
74
- }
29
+ export type NodeTypes =
30
+ | 'uniform'
31
+ | 'variable'
32
+ | 'swizzle'
33
+ | 'operator'
34
+ | 'node_type'
35
+ | 'math_fun'
36
+ | 'declare'
37
+ | 'assign'
38
+ | 'fn'
39
+ | 'if'
40
+ | 'loop'
41
+ | 'scope'
75
42
 
76
- // ユニフォーム変数の型
77
- export interface UniformNode extends X {
78
- set(value: any): void
79
- onObjectUpdate(callback: (context: any) => any): UniformNode
80
- onRenderUpdate(callback: (context: any) => any): UniformNode
43
+ export interface NodeProxy extends Record<Swizzles, NodeProxy> {
44
+ add(n: X): NodeProxy
45
+ sub(n: X): NodeProxy
46
+ mul(n: X): NodeProxy
47
+ div(n: X): NodeProxy
48
+ mod(n: X): NodeProxy
49
+ equal(n: X): NodeProxy
50
+ notEqual(n: X): NodeProxy
51
+ lessThan(n: X): NodeProxy
52
+ lessThanEqual(n: X): NodeProxy
53
+ greaterThan(n: X): NodeProxy
54
+ greaterThanEqual(n: X): NodeProxy
55
+ and(n: X): NodeProxy
56
+ or(n: X): NodeProxy
57
+ not(): NodeProxy
58
+ assign(n: X): NodeProxy
59
+ toVar(name?: string): NodeProxy
60
+ toString(c?: NodeConfig): string
61
+ type: NodeTypes
62
+ props: NodeProps
63
+ isProxy: true
81
64
  }
82
65
 
83
- // 関数定義の型
84
- export interface FunctionNode {
85
- (...args: any[]): X
86
- call(x: X[]): X
87
- }
88
-
89
- // 条件分岐の型
90
- export interface ConditionalNode {
91
- ElseIf(condition: X, callback: () => void): ConditionalNode
92
- Else(callback: () => void): void
93
- }
94
-
95
- // WebGL/WebGPU変換コンテキスト
96
- export interface ConversionContext {
97
- target: 'webgl' | 'webgpu'
98
- nodes: Map<string, Node>
99
- variables: Map<string, string>
100
- functions: Map<string, string>
101
- }
66
+ export type X = NodeProxy | number | string | null | undefined
@@ -0,0 +1,102 @@
1
+ import { is } from '../utils/helpers'
2
+ import { code } from './code'
3
+ import { FUNCTIONS, NODE_TYPES, OPERATOR_KEYS } from './const'
4
+ import type { Functions, NodeConfig, NodeType, Operators, Swizzles, X } from './types'
5
+
6
+ export const isSwizzle = (key: unknown): key is Swizzles => {
7
+ return is.str(key) && /^[xyzwrgbastpq]{1,4}$/.test(key)
8
+ }
9
+
10
+ export const isOperator = (key: unknown): key is Operators => {
11
+ return OPERATOR_KEYS.includes(key as Operators)
12
+ }
13
+
14
+ export const isNodeType = (key: unknown): key is NodeType => {
15
+ return NODE_TYPES.includes(key as NodeType)
16
+ }
17
+
18
+ export const isFunction = (key: unknown): key is Functions => {
19
+ return FUNCTIONS.includes(key as Functions)
20
+ }
21
+
22
+ let count = 0
23
+
24
+ export const getId = () => `i${count++}`
25
+
26
+ export const joins = (children: X[], c: NodeConfig) => {
27
+ return children
28
+ .filter((x) => !is.und(x) && !is.nul(x))
29
+ .map((x) => code(x, c))
30
+ .join(', ')
31
+ }
32
+
33
+ export const inferType = (target: X, c: NodeConfig): string => {
34
+ if (!target || typeof target !== 'object') return 'float'
35
+ const { type, props } = target
36
+ const { children = [] } = props
37
+ const [x, y, z] = children
38
+ if (type === 'node_type') return x as string
39
+ if (type === 'operator') {
40
+ const left = inferType(y, c)
41
+ const right = inferType(z, c)
42
+ if (left === right) return left
43
+ if (left.includes('vec')) return left
44
+ if (right.includes('vec')) return right
45
+ return 'float'
46
+ }
47
+ if (type === 'math_fun') {
48
+ if (['normalize', 'cross', 'reflect'].includes(x as string)) return inferType(children[1], c)
49
+ if (['dot', 'distance', 'length'].includes(x as string)) return 'float'
50
+ return 'float'
51
+ }
52
+ return 'float'
53
+ }
54
+
55
+ const generateUniforms = (c: NodeConfig): string => {
56
+ if (!c.uniforms || c.uniforms.size === 0) return ''
57
+ const uniformList = Array.from(c.uniforms)
58
+ return (
59
+ uniformList
60
+ .map((name, i) => {
61
+ if (c.isWebGL) return `uniform vec2 ${name};`
62
+ else return `@group(0) @binding(${i}) var<uniform> ${name}: vec2f;`
63
+ })
64
+ .join('\n') + '\n'
65
+ )
66
+ }
67
+
68
+ const generateFragmentMain = (body: string, uniforms: string, isWebGL = true) => {
69
+ if (isWebGL)
70
+ return `
71
+ ${uniforms}
72
+ #version 300 es
73
+ precision mediump float;
74
+ uniform vec2 iResolution;
75
+ uniform vec2 iMouse;
76
+ uniform float iTime;
77
+ out vec4 fragColor;
78
+ void main() {
79
+ ${body}
80
+ }`.trim()
81
+ return `
82
+ @group(0) @binding(0) var<uniform> iResolution: vec2f;
83
+ @group(0) @binding(1) var<uniform> iMouse: vec2f;
84
+ @group(0) @binding(2) var<uniform> iTime: f32;
85
+ ${uniforms}
86
+ @fragment
87
+ fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
88
+ ${body}
89
+ }`.trim()
90
+ }
91
+
92
+ export const fragment = (x: X, c: NodeConfig) => {
93
+ const body = code(x, c)
94
+ const uniforms = generateUniforms(c)
95
+ return generateFragmentMain(body, uniforms, c.isWebGL)
96
+ }
97
+
98
+ export const vertex = (x: X, c: NodeConfig) => {
99
+ const body = code(x, c)
100
+ const uniforms = generateUniforms(c)
101
+ return generateFragmentMain(body, uniforms, c.isWebGL)
102
+ }
package/src/types.ts CHANGED
@@ -29,12 +29,10 @@ export interface WebGLState {
29
29
  }
30
30
 
31
31
  export interface WebGPUState {
32
- uniforms: any
33
- textures: any
34
32
  device: GPUDevice
35
33
  context: GPUContext
36
- groups: any[]
37
34
  pipeline: GPUPipeline
35
+ groups: any[]
38
36
  resources: any[]
39
37
  loadingImg: number
40
38
  needsUpdate: boolean
@@ -1,5 +1,4 @@
1
- import { wgsl } from '../code/wgsl'
2
- import { X } from '../node'
1
+ import { fragment, vertex, X } from '../node'
3
2
  import { is } from './helpers'
4
3
  import type { GPUContext, GPUDevice, GPUPipeline } from '../types'
5
4
 
@@ -21,7 +20,7 @@ fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
21
20
  }
22
21
  `
23
22
 
24
- export const createDevive = async (c: GPUContext) => {
23
+ export const createDevice = async (c: GPUContext) => {
25
24
  const gpu = (navigator as any).gpu
26
25
  const format = gpu.getPreferredCanvasFormat()
27
26
  const adapter = await gpu.requestAdapter()
@@ -33,19 +32,19 @@ export const createDevive = async (c: GPUContext) => {
33
32
  export const createPipeline = (
34
33
  device: GPUDevice,
35
34
  format: string,
36
- buffers: any[],
37
- layouts: any[],
35
+ bufferLayouts: any[],
36
+ bindGroupLayouts: any[],
38
37
  vs: string | X = defaultVertexWGSL,
39
38
  fs: string | X = defaultFragmentWGSL
40
39
  ) => {
41
- if (is.obj(fs)) fs = wgsl(fs)
42
- if (is.obj(vs)) vs = wgsl(vs)
43
- const layout = device.createPipelineLayout({ bindGroupLayouts: layouts })
40
+ if (!is.str(fs)) fs = fragment(fs, { isWebGL: false })
41
+ if (!is.str(vs)) vs = vertex(vs, { isWebGL: false })
42
+ const layout = device.createPipelineLayout({ bindGroupLayouts })
44
43
  return device.createRenderPipeline({
45
44
  vertex: {
46
45
  module: device.createShaderModule({ code: vs.trim() }),
47
46
  entryPoint: 'main',
48
- buffers,
47
+ buffers: bufferLayouts,
49
48
  },
50
49
  fragment: {
51
50
  module: device.createShaderModule({ code: fs.trim() }),
@@ -73,7 +72,7 @@ export const createBindGroup = (device: GPUDevice, resources: any[]) => {
73
72
  })
74
73
  const layout = device.createBindGroupLayout({ entries: entries0 })
75
74
  const bindGroup = device.createBindGroup({ layout, entries: entries1 })
76
- return [layout, bindGroup]
75
+ return { layout, bindGroup }
77
76
  }
78
77
 
79
78
  export const createDescriptor = (c: GPUContext) => {
@@ -91,6 +90,12 @@ export const createDescriptor = (c: GPUContext) => {
91
90
 
92
91
  export const alignTo256 = (size: number) => Math.ceil(size / 256) * 256
93
92
 
93
+ export const createVertexBuffer = (device: GPUDevice, value: number[]) => {
94
+ const array = new Float32Array(value)
95
+ const buffer = device.createBuffer({ size: array.byteLength, usage: 40 }) // 40 === // GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
96
+ return { array, buffer }
97
+ }
98
+
94
99
  export const createUniformBuffer = (device: GPUDevice, value: number[]) => {
95
100
  const array = new Float32Array(value)
96
101
  const size = alignTo256(array.byteLength)
@@ -101,12 +106,30 @@ export const createUniformBuffer = (device: GPUDevice, value: number[]) => {
101
106
  export const createTextureSampler = (device: GPUDevice, width = 1280, height = 800) => {
102
107
  const texture = device.createTexture({ size: [width, height], format: 'rgba8unorm', usage: 22 })
103
108
  const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' })
104
- return [texture, sampler] as const
109
+ return { texture, sampler }
110
+ }
111
+
112
+ const getVertexStride = (dataLength: number, vertexCount: number) => {
113
+ return dataLength / vertexCount
114
+ }
115
+
116
+ const getVertexFormat = (stride: number) => {
117
+ if (stride === 2) return 'float32x2'
118
+ if (stride === 3) return 'float32x3'
119
+ if (stride === 4) return 'float32x4'
120
+ return 'float32'
105
121
  }
106
122
 
107
- // export const createVertexBuffer = (device: GPUDevice, value: number[]) => {
108
- // const array = new Float32Array(value)
109
- // const buffer = device.createBuffer({ size: array.byteLength, usage: 0x20 | 0x4 })
110
- // device.queue.writeBuffer(buffer, 0, array)
111
- // return buffer as Buffer
112
- // }
123
+ export const createBufferLayout = (shaderLocation: number, dataLength: number, count = 6) => {
124
+ const stride = getVertexStride(dataLength, count)
125
+ return {
126
+ arrayStride: stride * 4,
127
+ attributes: [
128
+ {
129
+ shaderLocation,
130
+ offset: 0,
131
+ format: getVertexFormat(stride),
132
+ },
133
+ ],
134
+ }
135
+ }
@@ -1,5 +1,4 @@
1
- import { glsl } from '../code/glsl'
2
- import { X } from '../node'
1
+ import { fragment, vertex, X } from '../node'
3
2
  import { is } from './helpers'
4
3
 
5
4
  export const defaultVertexGLSL = /* cpp */ `
@@ -29,7 +28,7 @@ const createShader = (c: WebGLRenderingContext, source: string, type: number) =>
29
28
  if (c.getShaderParameter(shader, c.COMPILE_STATUS)) return shader
30
29
  const error = c.getShaderInfoLog(shader)
31
30
  c.deleteShader(shader)
32
- throw new Error(`Could not compile shader: ${error}`)
31
+ console.warn(`Could not compile shader: ${error}`)
33
32
  }
34
33
 
35
34
  export const createProgram = (
@@ -37,16 +36,19 @@ export const createProgram = (
37
36
  vs: string | X = defaultVertexGLSL,
38
37
  fs: string | X = defaultFragmentGLSL
39
38
  ) => {
40
- if (is.obj(fs)) fs = glsl(fs as X)
41
- if (is.obj(vs)) vs = glsl(vs as X)
39
+ if (!is.str(fs)) fs = fragment(fs, { isWebGL: true })
40
+ if (!is.str(vs)) vs = vertex(fs, { isWebGL: true })
42
41
  const pg = c.createProgram()
43
- c.attachShader(pg, createShader(c, vs, c.VERTEX_SHADER))
44
- c.attachShader(pg, createShader(c, fs, c.FRAGMENT_SHADER))
42
+ const _vs = createShader(c, vs, c.VERTEX_SHADER)
43
+ const _fs = createShader(c, fs, c.FRAGMENT_SHADER)
44
+ if (!_vs || !_fs) return
45
+ c.attachShader(pg, _vs)
46
+ c.attachShader(pg, _fs)
45
47
  c.linkProgram(pg)
46
48
  if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
47
49
  const error = c.getProgramInfoLog(pg)
48
50
  c.deleteProgram(pg)
49
- throw new Error(`Could not link pg: ${error}`)
51
+ console.warn(`Could not link pg: ${error}`)
50
52
  }
51
53
 
52
54
  export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
package/src/webgl.ts CHANGED
@@ -5,15 +5,14 @@ import type { GL, WebGLState } from './types'
5
5
 
6
6
  export const webgl = async (gl: Partial<GL>) => {
7
7
  const c = gl.el!.getContext('webgl2')!
8
- const pg = createProgram(c, gl.vs, gl.fs)
8
+ const pg = createProgram(c, gl.vs, gl.fs)!
9
9
  const state = { context: c, program: pg } as WebGLState
10
10
  c.useProgram(pg)
11
11
 
12
12
  let _activeUnit = 0
13
- const activeUnits = cached(() => _activeUnit++)
14
-
15
- const uniformLocations = cached((key) => c.getUniformLocation(pg, key))
16
- const attribLocations = cached((key) => c.getAttribLocation(pg, key))
13
+ const uniforms = cached((key) => c.getUniformLocation(pg, key))
14
+ const attribs = cached((key) => c.getAttribLocation(pg, key))
15
+ const units = cached(() => _activeUnit++)
17
16
 
18
17
  const clean = () => c.deleteProgram(pg)
19
18
 
@@ -24,7 +23,7 @@ export const webgl = async (gl: Partial<GL>) => {
24
23
  }
25
24
 
26
25
  const _attribute = (key = '', value: number[], iboValue: number[]) => {
27
- const loc = attribLocations(key, true)
26
+ const loc = attribs(key, true)
28
27
  const vbo = createVbo(c, value)
29
28
  const ibo = createIbo(c, iboValue)
30
29
  const str = getStride(gl.count!, value, iboValue)
@@ -32,7 +31,7 @@ export const webgl = async (gl: Partial<GL>) => {
32
31
  }
33
32
 
34
33
  const _uniform = (key: string, value: number | number[]) => {
35
- const loc = uniformLocations(key)
34
+ const loc = uniforms(key)
36
35
  if (is.num(value)) return c.uniform1f(loc, value)
37
36
  let l = value.length
38
37
  if (l <= 4) return c[`uniform${l as 2}fv`](loc, value)
@@ -40,12 +39,12 @@ export const webgl = async (gl: Partial<GL>) => {
40
39
  c[`uniformMatrix${l as 2}fv`](loc, false, value)
41
40
  }
42
41
 
43
- const _texture = (alt: string, src: string) => {
42
+ const _texture = (key: string, src: string) => {
44
43
  const image = new Image()
45
- Object.assign(image, { src, alt, crossOrigin: 'anonymous' })
44
+ Object.assign(image, { src, crossOrigin: 'anonymous' })
46
45
  image.decode().then(() => {
47
- const loc = uniformLocations(image.alt)
48
- const unit = activeUnits(image.alt)
46
+ const loc = uniforms(key)
47
+ const unit = units(key)
49
48
  createTexture(c, image, loc, unit)
50
49
  })
51
50
  }
package/src/webgpu.ts CHANGED
@@ -1,52 +1,64 @@
1
+ import { nested as cached } from 'reev'
1
2
  import { is } from './utils/helpers'
2
3
  import {
3
- createDevive,
4
+ createDevice,
4
5
  createPipeline,
5
6
  createDescriptor,
6
7
  createUniformBuffer,
7
8
  createBindGroup,
8
9
  createTextureSampler,
10
+ createVertexBuffer,
11
+ createBufferLayout,
9
12
  } from './utils/pipeline'
10
13
  import type { GL, WebGPUState } from './types'
11
14
 
12
15
  export const webgpu = async (gl: Partial<GL>) => {
13
16
  const c = gl.el!.getContext('webgpu') as any
14
- const { device, format } = await createDevive(c)
17
+ const { device, format } = await createDevice(c)
15
18
  const state = {
16
19
  device,
17
20
  context: c,
18
- uniforms: {},
19
- textures: {},
20
21
  resources: [[], []],
21
22
  loadingImg: 0,
22
23
  needsUpdate: true,
23
24
  } as WebGPUState
24
25
 
25
- const initUniform = (value: number[]) => {
26
+ const bindGroups = [] as any[]
27
+ const vertexBuffers = [] as any[]
28
+ const bufferLayouts = [] as any[]
29
+
30
+ const attributes = cached((_, value: number[]) => {
31
+ const { array, buffer } = createVertexBuffer(device, value)
32
+ vertexBuffers.push(buffer)
33
+ bufferLayouts.push(createBufferLayout(bufferLayouts.length, array.length, gl.count))
34
+ state.needsUpdate = true
35
+ return { array, buffer }
36
+ })
37
+
38
+ const uniforms = cached((_, value: number[]) => {
26
39
  const { array, buffer } = createUniformBuffer(device, value)
27
40
  state.resources[0].push({ buffer })
28
41
  state.needsUpdate = true
29
42
  return { array, buffer }
30
- }
43
+ })
31
44
 
32
- const initTexutre = (source: HTMLImageElement) => {
33
- const { width, height } = source
34
- const [texture, sampler] = createTextureSampler(device, width, height)
45
+ const textures = cached((_, { width, height }: HTMLImageElement) => {
46
+ const { texture, sampler } = createTextureSampler(device, width, height)
35
47
  state.resources[1].push(sampler, texture.createView())
36
48
  state.needsUpdate = true
37
49
  return { texture, width, height }
38
- }
50
+ })
39
51
 
40
52
  const update = () => {
41
- const layouts = [] as any
42
- state.groups = []
53
+ const bindGroupLayouts = [] as any
54
+ bindGroups.length = 0
43
55
  state.resources.forEach((resource) => {
44
56
  if (!resource.length) return
45
- const [layout, group] = createBindGroup(device, resource)
46
- layouts.push(layout)
47
- state.groups.push(group)
57
+ const { layout, bindGroup } = createBindGroup(device, resource)
58
+ bindGroupLayouts.push(layout)
59
+ bindGroups.push(bindGroup)
48
60
  })
49
- state.pipeline = createPipeline(device, format, [], layouts, gl.vs, gl.fs)
61
+ state.pipeline = createPipeline(device, format, bufferLayouts, bindGroupLayouts, gl.vs, gl.fs)
50
62
  }
51
63
 
52
64
  const render = () => {
@@ -56,7 +68,8 @@ export const webgpu = async (gl: Partial<GL>) => {
56
68
  const encoder = device.createCommandEncoder()
57
69
  const pass = encoder.beginRenderPass(createDescriptor(c))
58
70
  pass.setPipeline(state.pipeline)
59
- state.groups.forEach((v, i) => pass.setBindGroup(i, v))
71
+ bindGroups.forEach((v, i) => pass.setBindGroup(i, v))
72
+ vertexBuffers.forEach((v, i) => pass.setVertexBuffer(i, v))
60
73
  pass.draw(gl.count, 1, 0, 0)
61
74
  pass.end()
62
75
  device.queue.submit([encoder.finish()])
@@ -65,14 +78,13 @@ export const webgpu = async (gl: Partial<GL>) => {
65
78
  const clean = () => {}
66
79
 
67
80
  const _attribute = (key = '', value: number[]) => {
68
- // @TODO FIX
69
- // vertexBuffers(key, value)
81
+ const { array, buffer } = attributes(key, value)
82
+ device.queue.writeBuffer(buffer, 0, array)
70
83
  }
71
84
 
72
85
  const _uniform = (key: string, value: number | number[]) => {
73
86
  if (is.num(value)) value = [value]
74
- if (!state.uniforms[key]) state.uniforms[key] = initUniform(value)
75
- const { array, buffer } = state.uniforms[key]
87
+ const { array, buffer } = uniforms(key, value)
76
88
  array.set(value)
77
89
  device.queue.writeBuffer(buffer, 0, array)
78
90
  }
@@ -81,8 +93,7 @@ export const webgpu = async (gl: Partial<GL>) => {
81
93
  state.loadingImg++
82
94
  const source = Object.assign(new Image(), { src, crossOrigin: 'anonymous' })
83
95
  source.decode().then(() => {
84
- if (!state.textures[key]) state.textures[key] = initTexutre(source)
85
- const { texture, width, height } = state.textures[key]
96
+ const { texture, width, height } = textures(key, source)
86
97
  device.queue.copyExternalImageToTexture({ source }, { texture }, { width, height })
87
98
  state.loadingImg--
88
99
  })