glre 0.23.0 → 0.25.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/README.md +11 -13
- package/dist/index.d.ts +39 -22
- package/dist/index.js +17 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +17 -15
- package/dist/index.mjs.map +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.js +17 -15
- package/dist/native.js.map +1 -1
- package/dist/native.mjs +17 -15
- package/dist/native.mjs.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +17 -15
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +17 -15
- package/dist/react.mjs.map +1 -1
- package/dist/solid.d.ts +1 -1
- package/dist/solid.js +17 -15
- package/dist/solid.js.map +1 -1
- package/dist/solid.mjs +17 -15
- package/dist/solid.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +14 -14
- package/src/node/index.ts +2 -2
- package/src/types.ts +7 -0
- package/src/utils/pipeline.ts +59 -75
- package/src/utils/program.ts +23 -17
- package/src/webgl.ts +34 -58
- package/src/webgpu.ts +72 -50
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const isGL = (a: unknown): a is EventState<GL> => {
|
|
|
20
20
|
if ('isGL' in a) return true
|
|
21
21
|
return false
|
|
22
22
|
}
|
|
23
|
+
|
|
23
24
|
export const isServer = () => {
|
|
24
25
|
return typeof window === 'undefined'
|
|
25
26
|
}
|
|
@@ -29,9 +30,7 @@ export const isWebGPUSupported = () => {
|
|
|
29
30
|
return 'gpu' in navigator
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
let iTime = performance.now()
|
|
33
|
-
iPrevTime = 0,
|
|
34
|
-
iDeltaTime = 0
|
|
33
|
+
let iTime = performance.now()
|
|
35
34
|
|
|
36
35
|
export const createGL = (props?: Partial<GL>) => {
|
|
37
36
|
const gl = event<Partial<GL>>({
|
|
@@ -50,17 +49,21 @@ export const createGL = (props?: Partial<GL>) => {
|
|
|
50
49
|
gl.frame = createFrame()
|
|
51
50
|
|
|
52
51
|
gl.attribute = durable((k, v, i) => gl.queue(() => gl._attribute?.(k, v, i)))
|
|
53
|
-
gl.uniform = durable((k, v, i) => gl.queue(() => gl._uniform?.(k, v, i)))
|
|
54
52
|
gl.texture = durable((k, v) => gl.queue(() => gl._texture?.(k, v)))
|
|
53
|
+
gl.uniform = durable((k, v, i) => gl.queue(() => gl._uniform?.(k, v, i)))
|
|
54
|
+
gl.uniform({ iResolution: gl.size, iMouse: [0, 0], iTime })
|
|
55
55
|
|
|
56
56
|
gl('mount', async () => {
|
|
57
|
+
gl.vs = gl.vs || gl.vert || gl.vertex
|
|
58
|
+
gl.fs = gl.fs || gl.frag || gl.fragment
|
|
57
59
|
if (!isWebGPUSupported()) gl.isWebGL = true
|
|
58
60
|
if (gl.isWebGL) {
|
|
59
|
-
await webgl(gl)
|
|
60
|
-
} else await webgpu(gl)
|
|
61
|
+
gl((await webgl(gl)) as GL)
|
|
62
|
+
} else gl((await webgpu(gl)) as GL)
|
|
61
63
|
gl.resize()
|
|
62
64
|
gl.frame(() => {
|
|
63
65
|
gl.loop()
|
|
66
|
+
gl.queue.flush()
|
|
64
67
|
gl.render()
|
|
65
68
|
return gl.isLoop
|
|
66
69
|
})
|
|
@@ -77,14 +80,6 @@ export const createGL = (props?: Partial<GL>) => {
|
|
|
77
80
|
gl.el.removeEventListener('mousemove', gl.mousemove)
|
|
78
81
|
})
|
|
79
82
|
|
|
80
|
-
gl('loop', () => {
|
|
81
|
-
iPrevTime = iTime
|
|
82
|
-
iTime = performance.now() / 1000
|
|
83
|
-
iDeltaTime = iTime - iPrevTime
|
|
84
|
-
gl.uniform({ iPrevTime, iTime, iDeltaTime })
|
|
85
|
-
// if (gl.fragmentNode) updateUniforms({ time: iTime }) // @TODO FIX
|
|
86
|
-
})
|
|
87
|
-
|
|
88
83
|
gl('resize', () => {
|
|
89
84
|
const w = gl.width || window.innerWidth
|
|
90
85
|
const h = gl.height || window.innerHeight
|
|
@@ -101,6 +96,11 @@ export const createGL = (props?: Partial<GL>) => {
|
|
|
101
96
|
gl.uniform('iMouse', gl.mouse)
|
|
102
97
|
})
|
|
103
98
|
|
|
99
|
+
gl('loop', () => {
|
|
100
|
+
iTime = performance.now() / 1000
|
|
101
|
+
gl.uniform('iTime', iTime)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
104
|
return gl(props)
|
|
105
105
|
}
|
|
106
106
|
|
package/src/node/index.ts
CHANGED
|
@@ -43,8 +43,8 @@ export const If = (condition: X, callback: () => void): ConditionalNode => {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// 組み込み変数
|
|
46
|
-
export const
|
|
47
|
-
export const
|
|
46
|
+
export const fragCoord = node('vec4', undefined)
|
|
47
|
+
export const position = node('vec4', undefined)
|
|
48
48
|
export const iTime = uniform(0.0)
|
|
49
49
|
export const iResolution = uniform([1920, 1080])
|
|
50
50
|
export const iMouse = uniform([0, 0])
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type GPUContext = any // GPUCanvasContext https://developer.mozilla.org/e
|
|
|
6
6
|
export type GPUDevice = any //
|
|
7
7
|
export type GPUBuffer = any //
|
|
8
8
|
export type GPUPipeline = any //
|
|
9
|
+
export type GPUBindGroup = any
|
|
9
10
|
export type Uniform = number | number[]
|
|
10
11
|
export type Attribute = number[]
|
|
11
12
|
export type Attributes = Record<string, Attribute>
|
|
@@ -28,9 +29,15 @@ export interface WebGLState {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export interface WebGPUState {
|
|
32
|
+
uniforms: any
|
|
33
|
+
textures: any
|
|
31
34
|
device: GPUDevice
|
|
32
35
|
context: GPUContext
|
|
36
|
+
groups: any[]
|
|
33
37
|
pipeline: GPUPipeline
|
|
38
|
+
resources: any[]
|
|
39
|
+
loadingImg: number
|
|
40
|
+
needsUpdate: boolean
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
export type GL = EventState<{
|
package/src/utils/pipeline.ts
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!gpu) return null
|
|
6
|
-
const adapter = await gpu.requestAdapter()
|
|
7
|
-
if (!adapter) return null
|
|
8
|
-
const device = await adapter.requestDevice()
|
|
9
|
-
const context = el.getContext('webgpu') as GPUContext
|
|
10
|
-
if (!context) return null
|
|
11
|
-
const format = gpu.getPreferredCanvasFormat()
|
|
12
|
-
context.configure({ device, format })
|
|
13
|
-
return { device, context, format }
|
|
14
|
-
}
|
|
1
|
+
import { wgsl } from '../code/wgsl'
|
|
2
|
+
import { X } from '../node'
|
|
3
|
+
import { is } from './helpers'
|
|
4
|
+
import type { GPUContext, GPUDevice, GPUPipeline } from '../types'
|
|
15
5
|
|
|
16
6
|
const defaultVertexWGSL = `
|
|
17
7
|
@vertex
|
|
@@ -23,19 +13,34 @@ fn main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {
|
|
|
23
13
|
`
|
|
24
14
|
|
|
25
15
|
const defaultFragmentWGSL = `
|
|
16
|
+
@group(0) @binding(0) var<uniform> iResolution: vec2f;
|
|
17
|
+
|
|
26
18
|
@fragment
|
|
27
19
|
fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
|
|
28
|
-
return vec4f(position.xy /
|
|
20
|
+
return vec4f(position.xy / iResolution, 0.0, 1.0);
|
|
29
21
|
}
|
|
30
22
|
`
|
|
31
23
|
|
|
32
|
-
export const
|
|
24
|
+
export const createDevive = async (c: GPUContext) => {
|
|
25
|
+
const gpu = (navigator as any).gpu
|
|
26
|
+
const format = gpu.getPreferredCanvasFormat()
|
|
27
|
+
const adapter = await gpu.requestAdapter()
|
|
28
|
+
const device = await adapter.requestDevice()
|
|
29
|
+
c.configure({ device, format, alphaMode: 'opaque' })
|
|
30
|
+
return { device, format }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const createPipeline = (
|
|
33
34
|
device: GPUDevice,
|
|
34
35
|
format: string,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
buffers: any[],
|
|
37
|
+
layouts: any[],
|
|
38
|
+
vs: string | X = defaultVertexWGSL,
|
|
39
|
+
fs: string | X = defaultFragmentWGSL
|
|
38
40
|
) => {
|
|
41
|
+
if (is.obj(fs)) fs = wgsl(fs)
|
|
42
|
+
if (is.obj(vs)) vs = wgsl(vs)
|
|
43
|
+
const layout = device.createPipelineLayout({ bindGroupLayouts: layouts })
|
|
39
44
|
return device.createRenderPipeline({
|
|
40
45
|
vertex: {
|
|
41
46
|
module: device.createShaderModule({ code: vs.trim() }),
|
|
@@ -47,11 +52,30 @@ export const createRenderPipeline = (
|
|
|
47
52
|
entryPoint: 'main',
|
|
48
53
|
targets: [{ format }],
|
|
49
54
|
},
|
|
50
|
-
layout
|
|
55
|
+
layout,
|
|
51
56
|
primitive: { topology: 'triangle-list' },
|
|
52
57
|
}) as GPUPipeline
|
|
53
58
|
}
|
|
54
59
|
|
|
60
|
+
export const createBindGroup = (device: GPUDevice, resources: any[]) => {
|
|
61
|
+
const entries0 = [] as any[]
|
|
62
|
+
const entries1 = [] as any[]
|
|
63
|
+
resources.forEach((resource, binding) => {
|
|
64
|
+
if (!resource) return
|
|
65
|
+
const isUniform = 'buffer' in resource // @ts-ignore
|
|
66
|
+
const isTexture = resource instanceof GPUTextureView // @ts-ignore
|
|
67
|
+
const isSampler = resource instanceof GPUSampler // @ts-ignore
|
|
68
|
+
if (isUniform) entries0.push({ binding, visibility: 3, buffer: { type: 'uniform' } })
|
|
69
|
+
else if (isTexture) entries0.push({ binding, visibility: 2, texture: {} })
|
|
70
|
+
else if (isSampler) entries0.push({ binding, visibility: 2, sampler: {} })
|
|
71
|
+
else return
|
|
72
|
+
entries1.push({ binding, resource })
|
|
73
|
+
})
|
|
74
|
+
const layout = device.createBindGroupLayout({ entries: entries0 })
|
|
75
|
+
const bindGroup = device.createBindGroup({ layout, entries: entries1 })
|
|
76
|
+
return [layout, bindGroup]
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
export const createDescriptor = (c: GPUContext) => {
|
|
56
80
|
return {
|
|
57
81
|
colorAttachments: [
|
|
@@ -64,65 +88,25 @@ export const createDescriptor = (c: GPUContext) => {
|
|
|
64
88
|
],
|
|
65
89
|
}
|
|
66
90
|
}
|
|
67
|
-
export const alignTo256 = (size: number) => Math.ceil(size / 256) * 256
|
|
68
91
|
|
|
69
|
-
export const
|
|
70
|
-
return device.createBuffer({ size: alignTo256(size), usage: 0x40 | 0x4 }) as Buffer
|
|
71
|
-
}
|
|
92
|
+
export const alignTo256 = (size: number) => Math.ceil(size / 256) * 256
|
|
72
93
|
|
|
73
|
-
export const
|
|
94
|
+
export const createUniformBuffer = (device: GPUDevice, value: number[]) => {
|
|
74
95
|
const array = new Float32Array(value)
|
|
75
|
-
const
|
|
76
|
-
device.
|
|
77
|
-
return buffer
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const createBindGroup = (device: GPUDevice, pipeline: GPUPipeline, entries: any[]) => {
|
|
81
|
-
const layout = pipeline.getBindGroupLayout(0)
|
|
82
|
-
return device.createBindGroup({ layout, entries })
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export const updateBindGroup = (
|
|
86
|
-
device: GPUDevice,
|
|
87
|
-
pipeline: GPUPipeline,
|
|
88
|
-
uniformBuffer: Buffer,
|
|
89
|
-
textures: any = {},
|
|
90
|
-
sampler: any = null
|
|
91
|
-
) => {
|
|
92
|
-
const entries = [{ binding: 0, resource: { buffer: uniformBuffer } }]
|
|
93
|
-
let binding = 1
|
|
94
|
-
Object.values(textures).forEach((texture: any) => {
|
|
95
|
-
entries.push({ binding: binding++, resource: texture.createView() })
|
|
96
|
-
})
|
|
97
|
-
if (sampler && Object.keys(textures).length > 0) entries.push({ binding: binding++, resource: sampler })
|
|
98
|
-
return createBindGroup(device, pipeline, entries)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const createUniform = (device: GPUDevice, buffer: any, data: Float32Array, offset = 0) => {
|
|
102
|
-
device.queue.writeBuffer(buffer, offset, data)
|
|
96
|
+
const size = alignTo256(array.byteLength)
|
|
97
|
+
const buffer = device.createBuffer({ size, usage: 72 }) as Buffer // 72 === GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
98
|
+
return { array, buffer }
|
|
103
99
|
}
|
|
104
100
|
|
|
105
|
-
export const
|
|
106
|
-
const texture = device.createTexture({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
usage: 0x4 | 0x2,
|
|
110
|
-
})
|
|
111
|
-
device.queue.copyExternalImageToTexture(
|
|
112
|
-
{ source: image },
|
|
113
|
-
{ texture },
|
|
114
|
-
{ width: image.width, height: image.height }
|
|
115
|
-
)
|
|
116
|
-
return texture
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export const createSampler = (device: GPUDevice) => {
|
|
120
|
-
return device.createSampler({
|
|
121
|
-
magFilter: 'linear',
|
|
122
|
-
minFilter: 'linear',
|
|
123
|
-
addressModeU: 'clamp-to-edge',
|
|
124
|
-
addressModeV: 'clamp-to-edge',
|
|
125
|
-
})
|
|
101
|
+
export const createTextureSampler = (device: GPUDevice, width = 1280, height = 800) => {
|
|
102
|
+
const texture = device.createTexture({ size: [width, height], format: 'rgba8unorm', usage: 22 })
|
|
103
|
+
const sampler = device.createSampler({ magFilter: 'linear', minFilter: 'linear' })
|
|
104
|
+
return [texture, sampler] as const
|
|
126
105
|
}
|
|
127
106
|
|
|
128
|
-
export const
|
|
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
|
+
// }
|
package/src/utils/program.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { glsl } from '../code/glsl'
|
|
2
|
+
import { X } from '../node'
|
|
3
|
+
import { is } from './helpers'
|
|
4
|
+
|
|
1
5
|
export const defaultVertexGLSL = /* cpp */ `
|
|
2
6
|
#version 300 es
|
|
3
7
|
void main() {
|
|
@@ -17,7 +21,7 @@ void main() {
|
|
|
17
21
|
}
|
|
18
22
|
`
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
const createShader = (c: WebGLRenderingContext, source: string, type: number) => {
|
|
21
25
|
const shader = c.createShader(type)
|
|
22
26
|
if (!shader) throw new Error('Failed to create shader')
|
|
23
27
|
c.shaderSource(shader, source.trim())
|
|
@@ -28,7 +32,13 @@ export const createShader = (c: WebGLRenderingContext, source: string, type: num
|
|
|
28
32
|
throw new Error(`Could not compile shader: ${error}`)
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
export const createProgram = (
|
|
35
|
+
export const createProgram = (
|
|
36
|
+
c: WebGLRenderingContext,
|
|
37
|
+
vs: string | X = defaultVertexGLSL,
|
|
38
|
+
fs: string | X = defaultFragmentGLSL
|
|
39
|
+
) => {
|
|
40
|
+
if (is.obj(fs)) fs = glsl(fs as X)
|
|
41
|
+
if (is.obj(vs)) vs = glsl(vs as X)
|
|
32
42
|
const pg = c.createProgram()
|
|
33
43
|
c.attachShader(pg, createShader(c, vs, c.VERTEX_SHADER))
|
|
34
44
|
c.attachShader(pg, createShader(c, fs, c.FRAGMENT_SHADER))
|
|
@@ -55,20 +65,26 @@ export const createIbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
|
55
65
|
return buffer
|
|
56
66
|
}
|
|
57
67
|
|
|
68
|
+
export const getStride = (count: number, value: number[], iboValue?: number[]) => {
|
|
69
|
+
if (iboValue) count = Math.max(...iboValue) + 1
|
|
70
|
+
const stride = value.length / count
|
|
71
|
+
return Math.floor(stride)
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
export const createAttrib = (
|
|
59
75
|
c: WebGLRenderingContext,
|
|
60
76
|
stride: number,
|
|
61
|
-
|
|
77
|
+
loc: any,
|
|
62
78
|
vbo: WebGLBuffer,
|
|
63
79
|
ibo?: WebGLBuffer
|
|
64
80
|
) => {
|
|
65
81
|
c.bindBuffer(c.ARRAY_BUFFER, vbo)
|
|
66
|
-
c.enableVertexAttribArray(
|
|
67
|
-
c.vertexAttribPointer(
|
|
82
|
+
c.enableVertexAttribArray(loc)
|
|
83
|
+
c.vertexAttribPointer(loc, stride, c.FLOAT, false, 0, 0)
|
|
68
84
|
if (ibo) c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, ibo)
|
|
69
85
|
}
|
|
70
86
|
|
|
71
|
-
export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement) => {
|
|
87
|
+
export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement, loc: any, unit: number) => {
|
|
72
88
|
const texture = c.createTexture()
|
|
73
89
|
c.bindTexture(c.TEXTURE_2D, texture)
|
|
74
90
|
c.texImage2D(c.TEXTURE_2D, 0, c.RGBA, c.RGBA, c.UNSIGNED_BYTE, img)
|
|
@@ -78,17 +94,7 @@ export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement) =
|
|
|
78
94
|
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
79
95
|
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
80
96
|
c.bindTexture(c.TEXTURE_2D, null)
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export const activeTexture = (
|
|
85
|
-
c: WebGLRenderingContext,
|
|
86
|
-
location: WebGLUniformLocation | null,
|
|
87
|
-
unit: number,
|
|
88
|
-
texture: WebGLTexture
|
|
89
|
-
) => {
|
|
90
|
-
if (!location) return
|
|
91
|
-
c.uniform1i(location, unit)
|
|
97
|
+
c.uniform1i(loc, unit)
|
|
92
98
|
c.activeTexture(c.TEXTURE0 + unit)
|
|
93
99
|
c.bindTexture(c.TEXTURE_2D, texture)
|
|
94
100
|
}
|
package/src/webgl.ts
CHANGED
|
@@ -1,78 +1,54 @@
|
|
|
1
|
-
import { nested } from 'reev'
|
|
2
|
-
import { glsl } from './code/glsl'
|
|
1
|
+
import { nested as cached } from 'reev'
|
|
3
2
|
import { is } from './utils/helpers'
|
|
4
|
-
import
|
|
5
|
-
import type { GL } from './types'
|
|
6
|
-
import { activeTexture, createAttrib, createIbo, createProgram, createTexture, createVbo } from './utils/program'
|
|
3
|
+
import { createAttrib, createIbo, createProgram, createTexture, createVbo, getStride } from './utils/program'
|
|
4
|
+
import type { GL, WebGLState } from './types'
|
|
7
5
|
|
|
8
|
-
export const webgl = async (gl: GL) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const c = gl.el.getContext('webgl2')!
|
|
14
|
-
const pg = createProgram(c, vs, fs)
|
|
6
|
+
export const webgl = async (gl: Partial<GL>) => {
|
|
7
|
+
const c = gl.el!.getContext('webgl2')!
|
|
8
|
+
const pg = createProgram(c, gl.vs, gl.fs)
|
|
9
|
+
const state = { context: c, program: pg } as WebGLState
|
|
10
|
+
c.useProgram(pg)
|
|
15
11
|
|
|
16
12
|
let _activeUnit = 0
|
|
17
|
-
const activeUnits =
|
|
13
|
+
const activeUnits = cached(() => _activeUnit++)
|
|
18
14
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
return c.getUniformLocation(pg, key) as WebGLUniformLocation
|
|
22
|
-
})
|
|
15
|
+
const uniformLocations = cached((key) => c.getUniformLocation(pg, key))
|
|
16
|
+
const attribLocations = cached((key) => c.getAttribLocation(pg, key))
|
|
23
17
|
|
|
24
|
-
const
|
|
25
|
-
if (iboValue) count = Math.max(...iboValue) + 1
|
|
26
|
-
const stride = value.length / count
|
|
27
|
-
if (stride !== Math.floor(stride)) console.warn(`Vertex Stride Error: count ${count} is mismatch`)
|
|
28
|
-
return Math.floor(stride)
|
|
29
|
-
})
|
|
18
|
+
const clean = () => c.deleteProgram(pg)
|
|
30
19
|
|
|
31
|
-
const
|
|
32
|
-
const loc = locations(key)
|
|
33
|
-
if (is.num(value)) return (value: any) => c.uniform1f(loc, value)
|
|
34
|
-
let l = value.length as 3
|
|
35
|
-
if (l <= 4) return (value: any) => c[`uniform${l}fv`](loc, value)
|
|
36
|
-
l = (Math.sqrt(l) << 0) as 3
|
|
37
|
-
return (value: any) => c[`uniformMatrix${l}fv`](loc, false, value)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
gl('clean', () => c.deleteProgram(pg))
|
|
41
|
-
|
|
42
|
-
gl('render', () => {
|
|
43
|
-
c.useProgram(pg)
|
|
44
|
-
gl.queue.flush()
|
|
20
|
+
const render = () => {
|
|
45
21
|
c.clear(c.COLOR_BUFFER_BIT)
|
|
46
|
-
c.viewport(0, 0, ...gl.size)
|
|
22
|
+
c.viewport(0, 0, ...gl.size!)
|
|
47
23
|
c.drawArrays(c.TRIANGLES, 0, 3)
|
|
48
|
-
}
|
|
24
|
+
}
|
|
49
25
|
|
|
50
|
-
|
|
51
|
-
const loc =
|
|
26
|
+
const _attribute = (key = '', value: number[], iboValue: number[]) => {
|
|
27
|
+
const loc = attribLocations(key, true)
|
|
52
28
|
const vbo = createVbo(c, value)
|
|
53
29
|
const ibo = createIbo(c, iboValue)
|
|
54
|
-
const str =
|
|
30
|
+
const str = getStride(gl.count!, value, iboValue)
|
|
55
31
|
createAttrib(c, str, loc, vbo, ibo)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
gl('_uniform', (key: string, value: number | number[]) => {
|
|
59
|
-
uniforms(key, value)(value)
|
|
60
|
-
})
|
|
32
|
+
}
|
|
61
33
|
|
|
62
|
-
const
|
|
63
|
-
const loc =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
34
|
+
const _uniform = (key: string, value: number | number[]) => {
|
|
35
|
+
const loc = uniformLocations(key)
|
|
36
|
+
if (is.num(value)) return c.uniform1f(loc, value)
|
|
37
|
+
let l = value.length
|
|
38
|
+
if (l <= 4) return c[`uniform${l as 2}fv`](loc, value)
|
|
39
|
+
l = Math.sqrt(l) << 0
|
|
40
|
+
c[`uniformMatrix${l as 2}fv`](loc, false, value)
|
|
67
41
|
}
|
|
68
42
|
|
|
69
|
-
|
|
43
|
+
const _texture = (alt: string, src: string) => {
|
|
70
44
|
const image = new Image()
|
|
71
|
-
image.addEventListener('load', _loadFn.bind(null, image), false)
|
|
72
45
|
Object.assign(image, { src, alt, crossOrigin: 'anonymous' })
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
46
|
+
image.decode().then(() => {
|
|
47
|
+
const loc = uniformLocations(image.alt)
|
|
48
|
+
const unit = activeUnits(image.alt)
|
|
49
|
+
createTexture(c, image, loc, unit)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
76
52
|
|
|
77
|
-
return
|
|
53
|
+
return { webgl: state, render, clean, _attribute, _uniform, _texture }
|
|
78
54
|
}
|
package/src/webgpu.ts
CHANGED
|
@@ -1,70 +1,92 @@
|
|
|
1
|
-
import { wgsl } from './code/wgsl'
|
|
2
1
|
import { is } from './utils/helpers'
|
|
3
2
|
import {
|
|
4
|
-
|
|
3
|
+
createDevive,
|
|
4
|
+
createPipeline,
|
|
5
5
|
createDescriptor,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// createUniform,
|
|
10
|
-
// createDeviceTexture,
|
|
11
|
-
// createSampler,
|
|
6
|
+
createUniformBuffer,
|
|
7
|
+
createBindGroup,
|
|
8
|
+
createTextureSampler,
|
|
12
9
|
} from './utils/pipeline'
|
|
13
|
-
import type {
|
|
14
|
-
import type { GL, GPUPipeline } from './types'
|
|
10
|
+
import type { GL, WebGPUState } from './types'
|
|
15
11
|
|
|
16
|
-
const
|
|
12
|
+
export const webgpu = async (gl: Partial<GL>) => {
|
|
13
|
+
const c = gl.el!.getContext('webgpu') as any
|
|
14
|
+
const { device, format } = await createDevive(c)
|
|
15
|
+
const state = {
|
|
16
|
+
device,
|
|
17
|
+
context: c,
|
|
18
|
+
uniforms: {},
|
|
19
|
+
textures: {},
|
|
20
|
+
resources: [[], []],
|
|
21
|
+
loadingImg: 0,
|
|
22
|
+
needsUpdate: true,
|
|
23
|
+
} as WebGPUState
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const gpu = (navigator as any).gpu
|
|
25
|
-
const adapter = await gpu.requestAdapter()
|
|
26
|
-
const device = await adapter.requestDevice()
|
|
27
|
-
const format = gpu.getPreferredCanvasFormat()
|
|
28
|
-
c.configure({ device, format, alphaMode: 'opaque' })
|
|
25
|
+
const initUniform = (value: number[]) => {
|
|
26
|
+
const { array, buffer } = createUniformBuffer(device, value)
|
|
27
|
+
state.resources[0].push({ buffer })
|
|
28
|
+
state.needsUpdate = true
|
|
29
|
+
return { array, buffer }
|
|
30
|
+
}
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
const initTexutre = (source: HTMLImageElement) => {
|
|
33
|
+
const { width, height } = source
|
|
34
|
+
const [texture, sampler] = createTextureSampler(device, width, height)
|
|
35
|
+
state.resources[1].push(sampler, texture.createView())
|
|
36
|
+
state.needsUpdate = true
|
|
37
|
+
return { texture, width, height }
|
|
38
|
+
}
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
const update = () => {
|
|
41
|
+
const layouts = [] as any
|
|
42
|
+
state.groups = []
|
|
43
|
+
state.resources.forEach((resource) => {
|
|
44
|
+
if (!resource.length) return
|
|
45
|
+
const [layout, group] = createBindGroup(device, resource)
|
|
46
|
+
layouts.push(layout)
|
|
47
|
+
state.groups.push(group)
|
|
48
|
+
})
|
|
49
|
+
state.pipeline = createPipeline(device, format, [], layouts, gl.vs, gl.fs)
|
|
50
|
+
}
|
|
33
51
|
|
|
34
|
-
|
|
35
|
-
if (
|
|
52
|
+
const render = () => {
|
|
53
|
+
if (state.loadingImg) return // ignore if loading img
|
|
54
|
+
if (state.needsUpdate) update() // first rendering
|
|
55
|
+
state.needsUpdate = false
|
|
36
56
|
const encoder = device.createCommandEncoder()
|
|
37
57
|
const pass = encoder.beginRenderPass(createDescriptor(c))
|
|
38
|
-
pass.setPipeline(pipeline)
|
|
39
|
-
|
|
58
|
+
pass.setPipeline(state.pipeline)
|
|
59
|
+
state.groups.forEach((v, i) => pass.setBindGroup(i, v))
|
|
60
|
+
pass.draw(gl.count, 1, 0, 0)
|
|
40
61
|
pass.end()
|
|
41
62
|
device.queue.submit([encoder.finish()])
|
|
42
|
-
}
|
|
63
|
+
}
|
|
43
64
|
|
|
44
|
-
|
|
45
|
-
// @TODO FIX
|
|
46
|
-
// vertexBuffers(key, value)
|
|
47
|
-
})
|
|
65
|
+
const clean = () => {}
|
|
48
66
|
|
|
49
|
-
|
|
67
|
+
const _attribute = (key = '', value: number[]) => {
|
|
50
68
|
// @TODO FIX
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
// const uniformData = new Float32Array(Object.values(uniforms))
|
|
54
|
-
// createUniform(device, uniformBuffer, uniformData)
|
|
55
|
-
})
|
|
69
|
+
// vertexBuffers(key, value)
|
|
70
|
+
}
|
|
56
71
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
const _uniform = (key: string, value: number | number[]) => {
|
|
73
|
+
if (is.num(value)) value = [value]
|
|
74
|
+
if (!state.uniforms[key]) state.uniforms[key] = initUniform(value)
|
|
75
|
+
const { array, buffer } = state.uniforms[key]
|
|
76
|
+
array.set(value)
|
|
77
|
+
device.queue.writeBuffer(buffer, 0, array)
|
|
78
|
+
}
|
|
61
79
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
const _texture = (key: string, src: string) => {
|
|
81
|
+
state.loadingImg++
|
|
82
|
+
const source = Object.assign(new Image(), { src, crossOrigin: 'anonymous' })
|
|
83
|
+
source.decode().then(() => {
|
|
84
|
+
if (!state.textures[key]) state.textures[key] = initTexutre(source)
|
|
85
|
+
const { texture, width, height } = state.textures[key]
|
|
86
|
+
device.queue.copyExternalImageToTexture({ source }, { texture }, { width, height })
|
|
87
|
+
state.loadingImg--
|
|
88
|
+
})
|
|
89
|
+
}
|
|
68
90
|
|
|
69
|
-
return
|
|
91
|
+
return { webgpu: state, render, clean, _attribute, _uniform, _texture }
|
|
70
92
|
}
|