glre 0.24.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.
@@ -1,4 +1,7 @@
1
- import { GPUContext, GPUDevice, GPUPipeline } from '../types'
1
+ import { wgsl } from '../code/wgsl'
2
+ import { X } from '../node'
3
+ import { is } from './helpers'
4
+ import type { GPUContext, GPUDevice, GPUPipeline } from '../types'
2
5
 
3
6
  const defaultVertexWGSL = `
4
7
  @vertex
@@ -10,13 +13,7 @@ fn main(@builtin(vertex_index) vertex_index: u32) -> @builtin(position) vec4f {
10
13
  `
11
14
 
12
15
  const defaultFragmentWGSL = `
13
- struct Uniforms {
14
- iResolution: vec2f,
15
- iMouse: vec2f,
16
- iTime: f32,
17
- }
18
-
19
- @group(0) @binding(0) var<uniform> u: Uniforms;
16
+ @group(0) @binding(0) var<uniform> iResolution: vec2f;
20
17
 
21
18
  @fragment
22
19
  fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
@@ -24,13 +21,26 @@ fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
24
21
  }
25
22
  `
26
23
 
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
+
27
33
  export const createPipeline = (
28
34
  device: GPUDevice,
29
35
  format: string,
30
- vs = defaultVertexWGSL,
31
- fs = defaultFragmentWGSL,
32
- buffers: any[]
36
+ buffers: any[],
37
+ layouts: any[],
38
+ vs: string | X = defaultVertexWGSL,
39
+ fs: string | X = defaultFragmentWGSL
33
40
  ) => {
41
+ if (is.obj(fs)) fs = wgsl(fs)
42
+ if (is.obj(vs)) vs = wgsl(vs)
43
+ const layout = device.createPipelineLayout({ bindGroupLayouts: layouts })
34
44
  return device.createRenderPipeline({
35
45
  vertex: {
36
46
  module: device.createShaderModule({ code: vs.trim() }),
@@ -42,11 +52,30 @@ export const createPipeline = (
42
52
  entryPoint: 'main',
43
53
  targets: [{ format }],
44
54
  },
45
- layout: 'auto',
55
+ layout,
46
56
  primitive: { topology: 'triangle-list' },
47
57
  }) as GPUPipeline
48
58
  }
49
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
+
50
79
  export const createDescriptor = (c: GPUContext) => {
51
80
  return {
52
81
  colorAttachments: [
@@ -60,65 +89,24 @@ export const createDescriptor = (c: GPUContext) => {
60
89
  }
61
90
  }
62
91
 
63
- // export const alignTo256 = (size: number) => Math.ceil(size / 256) * 256
64
- //
65
- // export const createUniformBuffer = (device: GPUDevice, size: number) => {
66
- // return device.createBuffer({ size: alignTo256(size), usage: 0x40 | 0x4 }) as Buffer
67
- // }
68
- //
92
+ export const alignTo256 = (size: number) => Math.ceil(size / 256) * 256
93
+
94
+ export const createUniformBuffer = (device: GPUDevice, value: number[]) => {
95
+ const array = new Float32Array(value)
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 }
99
+ }
100
+
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
105
+ }
106
+
69
107
  // export const createVertexBuffer = (device: GPUDevice, value: number[]) => {
70
108
  // const array = new Float32Array(value)
71
109
  // const buffer = device.createBuffer({ size: array.byteLength, usage: 0x20 | 0x4 })
72
110
  // device.queue.writeBuffer(buffer, 0, array)
73
111
  // return buffer as Buffer
74
112
  // }
75
- //
76
- // export const createBindGroup = (device: GPUDevice, pipeline: GPUPipeline, entries: any[]) => {
77
- // const layout = pipeline.getBindGroupLayout(0)
78
- // return device.createBindGroup({ layout, entries })
79
- // }
80
- //
81
- // export const updateBindGroup = (
82
- // device: GPUDevice,
83
- // pipeline: GPUPipeline,
84
- // uniformBuffer: Buffer,
85
- // textures: any = {},
86
- // sampler: any = null
87
- // ) => {
88
- // const entries = [{ binding: 0, resource: { buffer: uniformBuffer } }]
89
- // let binding = 1
90
- // Object.values(textures).forEach((texture: any) => {
91
- // entries.push({ binding: binding++, resource: texture.createView() })
92
- // })
93
- // if (sampler && Object.keys(textures).length > 0) entries.push({ binding: binding++, resource: sampler })
94
- // return createBindGroup(device, pipeline, entries)
95
- // }
96
- //
97
- // export const createUniform = (device: GPUDevice, buffer: any, data: Float32Array, offset = 0) => {
98
- // device.queue.writeBuffer(buffer, offset, data)
99
- // }
100
- //
101
- // export const createDeviceTexture = (device: GPUDevice, image: HTMLImageElement) => {
102
- // const texture = device.createTexture({
103
- // size: { width: image.width, height: image.height },
104
- // format: 'rgba8unorm',
105
- // usage: 0x4 | 0x2,
106
- // })
107
- // device.queue.copyExternalImageToTexture(
108
- // { source: image },
109
- // { texture },
110
- // { width: image.width, height: image.height }
111
- // )
112
- // return texture
113
- // }
114
- //
115
- // export const createSampler = (device: GPUDevice) => {
116
- // return device.createSampler({
117
- // magFilter: 'linear',
118
- // minFilter: 'linear',
119
- // addressModeU: 'clamp-to-edge',
120
- // addressModeV: 'clamp-to-edge',
121
- // })
122
- // }
123
- //
124
- // export const getDefaultVertices = () => new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1])
@@ -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
- export const createShader = (c: WebGLRenderingContext, source: string, type: number) => {
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 = (c: WebGLRenderingContext, vs = defaultVertexGLSL, fs = defaultFragmentGLSL) => {
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
- location: any,
77
+ loc: any,
62
78
  vbo: WebGLBuffer,
63
79
  ibo?: WebGLBuffer
64
80
  ) => {
65
81
  c.bindBuffer(c.ARRAY_BUFFER, vbo)
66
- c.enableVertexAttribArray(location)
67
- c.vertexAttribPointer(location, stride, c.FLOAT, false, 0, 0)
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
- return texture
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,77 +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 type { X } from './node'
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
- let vs = gl.vs || gl.vert || gl.vertex
10
- let fs = gl.fs || gl.frag || gl.fragment
11
- if (is.obj(fs)) fs = glsl(fs as X)
12
- if (is.obj(vs)) vs = glsl(vs as X)
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 = nested(() => _activeUnit++)
13
+ const activeUnits = cached(() => _activeUnit++)
18
14
 
19
- const locations = nested((key, bool = false) => {
20
- if (bool) return c.getAttribLocation(pg, key)
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 strides = nested((_, count: number, value: number[], iboValue?: number[]) => {
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 uniforms = nested((key, value: number | number[]) => {
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)
20
+ const render = () => {
44
21
  c.clear(c.COLOR_BUFFER_BIT)
45
- c.viewport(0, 0, ...gl.size)
22
+ c.viewport(0, 0, ...gl.size!)
46
23
  c.drawArrays(c.TRIANGLES, 0, 3)
47
- })
24
+ }
48
25
 
49
- gl('_attribute', (key = '', value: number[], iboValue: number[]) => {
50
- const loc = locations(key, true)
26
+ const _attribute = (key = '', value: number[], iboValue: number[]) => {
27
+ const loc = attribLocations(key, true)
51
28
  const vbo = createVbo(c, value)
52
29
  const ibo = createIbo(c, iboValue)
53
- const str = strides(key, gl.count, value, iboValue)
30
+ const str = getStride(gl.count!, value, iboValue)
54
31
  createAttrib(c, str, loc, vbo, ibo)
55
- })
56
-
57
- gl('_uniform', (key: string, value: number | number[]) => {
58
- uniforms(key, value)(value)
59
- })
32
+ }
60
33
 
61
- const _loadFn = (image: HTMLImageElement) => {
62
- const loc = locations(image.alt)
63
- const unit = activeUnits(image.alt)
64
- const tex = createTexture(c, image)
65
- activeTexture(c, loc, unit, tex)
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)
66
41
  }
67
42
 
68
- gl('_texture', (alt: string, src: string) => {
43
+ const _texture = (alt: string, src: string) => {
69
44
  const image = new Image()
70
- image.addEventListener('load', _loadFn.bind(null, image), false)
71
45
  Object.assign(image, { src, alt, crossOrigin: 'anonymous' })
72
- })
73
-
74
- gl.webgl = { context: c, program: pg }
46
+ image.decode().then(() => {
47
+ const loc = uniformLocations(image.alt)
48
+ const unit = activeUnits(image.alt)
49
+ createTexture(c, image, loc, unit)
50
+ })
51
+ }
75
52
 
76
- return gl
53
+ return { webgl: state, render, clean, _attribute, _uniform, _texture }
77
54
  }
package/src/webgpu.ts CHANGED
@@ -1,102 +1,92 @@
1
- import { wgsl } from './code/wgsl'
2
1
  import { is } from './utils/helpers'
3
2
  import {
3
+ createDevive,
4
4
  createPipeline,
5
5
  createDescriptor,
6
- // createUniformBuffer,
7
- // updateBindGroup,
8
- // createVertexBuffer,
9
- // createUniform,
10
- // createDeviceTexture,
11
- // createSampler,
6
+ createUniformBuffer,
7
+ createBindGroup,
8
+ createTextureSampler,
12
9
  } from './utils/pipeline'
13
- import type { X } from './node'
14
- import type { GL, GPUBindGroup, GPUPipeline } from './types'
15
- import { nested } from 'reev'
10
+ import type { GL, WebGPUState } from './types'
16
11
 
17
- const quadVertexCount = 3
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
18
24
 
19
- export const webgpu = async (gl: GL) => {
20
- let vs = gl.vs || gl.vert || gl.vertex
21
- let fs = gl.fs || gl.frag || gl.fragment
22
- if (is.obj(vs)) vs = wgsl(vs as X)
23
- if (is.obj(fs)) fs = wgsl(fs as X)
24
- const c = gl.el.getContext('webgpu') as any
25
- const gpu = (navigator as any).gpu
26
- const adapter = await gpu.requestAdapter()
27
- const device = await adapter.requestDevice()
28
- const format = gpu.getPreferredCanvasFormat()
29
- 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
+ }
30
31
 
31
- // Uniform buffer setup
32
- // WebGPU 16-byte alignment requirement: iTime(4) + padding(12) + iMouse(8) + iPrevTime(4) + iDeltaTime(4) + iResolution(8) = 40 bytes, rounded to 48
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
+ }
33
39
 
34
- const buffer = device.createBuffer({
35
- size: 64, // @ts-ignore
36
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
37
- })
38
-
39
- gl('clean', () => {})
40
-
41
- const activeUnit = nested((_, size) => (_activeUnit += size))
42
- let _activeUnit = 0
43
- let pipeline: GPUPipeline
44
- let bindGroup: GPUBindGroup
45
- let uniformData = new Float32Array(0)
46
-
47
- gl('render', () => {
48
- if (!pipeline) {
49
- pipeline = createPipeline(device, format, vs, fs, [])
50
- bindGroup = device.createBindGroup({
51
- layout: pipeline.getBindGroupLayout(0),
52
- entries: [
53
- {
54
- binding: 0,
55
- resource: { buffer },
56
- },
57
- ],
58
- })
59
- }
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
+ }
60
51
 
52
+ const render = () => {
53
+ if (state.loadingImg) return // ignore if loading img
54
+ if (state.needsUpdate) update() // first rendering
55
+ state.needsUpdate = false
61
56
  const encoder = device.createCommandEncoder()
62
57
  const pass = encoder.beginRenderPass(createDescriptor(c))
63
- pass.setPipeline(pipeline)
64
- pass.setBindGroup(0, bindGroup)
65
- pass.draw(quadVertexCount, 1, 0, 0)
58
+ pass.setPipeline(state.pipeline)
59
+ state.groups.forEach((v, i) => pass.setBindGroup(i, v))
60
+ pass.draw(gl.count, 1, 0, 0)
66
61
  pass.end()
67
62
  device.queue.submit([encoder.finish()])
68
- return true
69
- })
63
+ }
70
64
 
71
- gl('_attribute', (key = '', value: number[]) => {
65
+ const clean = () => {}
66
+
67
+ const _attribute = (key = '', value: number[]) => {
72
68
  // @TODO FIX
73
69
  // vertexBuffers(key, value)
74
- })
70
+ }
75
71
 
76
- gl('_uniform', (key: string, value: number | number[]) => {
72
+ const _uniform = (key: string, value: number | number[]) => {
77
73
  if (is.num(value)) value = [value]
78
- const size = value.length
79
- const unit = activeUnit(key, size)
80
- if (unit === _activeUnit) {
81
- const array = new Float32Array(_activeUnit)
82
- if (uniformData) array.set(uniformData)
83
- uniformData = array
84
- }
85
- for (let i = 0; i < size; i++) uniformData[unit - size + i] = value[i]
86
- device.queue.writeBuffer(buffer, 0, uniformData)
87
- })
88
-
89
- // const _loadFun = (image: HTMLImageElement, gl: GL) => {
90
- // const texture = createDeviceTexture(device, image)
91
- // // bindGroup = updateBindGroup(device, pipeline, buffer, textures, sampler)
92
- // }
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
+ }
93
79
 
94
- gl('_texture', (alt: string, src: string) => {
95
- // @TODO FIX
96
- // const image = new Image()
97
- // image.addEventListener('load', _loadFun.bind(null, image, gl), false)
98
- // Object.assign(image, { src, alt, crossOrigin: 'anonymous' })
99
- })
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
+ }
100
90
 
101
- return gl
91
+ return { webgpu: state, render, clean, _attribute, _uniform, _texture }
102
92
  }