glre 0.34.0 → 0.36.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 +96 -98
- package/dist/index.cjs +34 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +104 -178
- package/dist/index.js +35 -31
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +34 -30
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.ts +29 -19
- package/dist/native.js +35 -31
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +34 -30
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +35 -31
- package/dist/react.js.map +1 -1
- package/dist/solid.cjs +34 -30
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.ts +1 -1
- package/dist/solid.js +35 -31
- package/dist/solid.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +13 -6
- package/src/node/core.ts +100 -0
- package/src/node/index.ts +22 -97
- package/src/node/node.ts +28 -20
- package/src/node/scope.ts +7 -13
- package/src/node/types.ts +28 -20
- package/src/node/{code.ts → utils/index.ts} +16 -11
- package/src/node/{infer.ts → utils/infer.ts} +15 -6
- package/src/node/{parse.ts → utils/parse.ts} +44 -41
- package/src/node/{utils.ts → utils/utils.ts} +25 -41
- package/src/types.ts +70 -60
- package/src/utils/helpers.ts +16 -0
- package/src/utils/pipeline.ts +41 -12
- package/src/utils/program.ts +56 -19
- package/src/webgl.ts +91 -37
- package/src/webgpu.ts +77 -47
- /package/src/node/{const.ts → utils/const.ts} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { code } from './code'
|
|
1
|
+
import { code } from '.'
|
|
3
2
|
import { infer } from './infer'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
3
|
+
import { getConversions, addDependency } from './utils'
|
|
4
|
+
import { is } from '../../utils/helpers'
|
|
5
|
+
import type { Constants, NodeContext, NodeProps, NodeProxy, X } from '../types'
|
|
6
6
|
|
|
7
7
|
export const parseArray = (children: X[], c: NodeContext) => {
|
|
8
8
|
return children
|
|
@@ -54,48 +54,17 @@ export const parseDeclare = (c: NodeContext, x: X, y: X) => {
|
|
|
54
54
|
const type = infer(x, c)
|
|
55
55
|
const varName = (y as any)?.props?.id
|
|
56
56
|
if (c.isWebGL) return `${type} ${varName} = ${code(x, c)};`
|
|
57
|
-
const wgslType =
|
|
57
|
+
const wgslType = getConversions(type)
|
|
58
58
|
return `var ${varName}: ${wgslType} = ${code(x, c)};`
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
|
|
62
|
-
const { id, children = [], layout } = props
|
|
63
|
-
const [x, ...args] = children
|
|
64
|
-
const argParams: [name: string, type: string][] = []
|
|
65
|
-
const params: string[] = []
|
|
66
|
-
if (layout?.inputs)
|
|
67
|
-
for (const input of layout.inputs) {
|
|
68
|
-
argParams.push([input.name, input.type])
|
|
69
|
-
}
|
|
70
|
-
else
|
|
71
|
-
for (let i = 0; i < args.length; i++) {
|
|
72
|
-
argParams.push([`p${i}`, infer(args[i], c)])
|
|
73
|
-
}
|
|
74
|
-
const ret = []
|
|
75
|
-
if (c?.isWebGL) {
|
|
76
|
-
for (const [paramId, type] of argParams) {
|
|
77
|
-
addDependency(c, id!, type)
|
|
78
|
-
params.push(`${type} ${paramId}`)
|
|
79
|
-
}
|
|
80
|
-
addDependency(c, id!, returnType)
|
|
81
|
-
ret.push(`${returnType} ${id}(${params}) {`)
|
|
82
|
-
} else {
|
|
83
|
-
for (const [paramId, type] of argParams) params.push(`${paramId}: ${formatConversions(type, c)}`)
|
|
84
|
-
ret.push(`fn ${id}(${params}) -> ${formatConversions(returnType, c)} {`)
|
|
85
|
-
}
|
|
86
|
-
const scopeCode = code(x, c)
|
|
87
|
-
if (scopeCode) ret.push(scopeCode)
|
|
88
|
-
ret.push('}')
|
|
89
|
-
return ret.join('\n')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
61
|
export const parseStructHead = (c: NodeContext, id: string, fields: Record<string, NodeProxy> = {}) => {
|
|
93
62
|
const lines: string[] = []
|
|
94
63
|
for (const key in fields) {
|
|
95
64
|
const fieldType = fields[key]
|
|
96
65
|
const type = infer(fieldType, c)
|
|
97
66
|
if (c.isWebGL) addDependency(c, id, type)
|
|
98
|
-
lines.push(c.isWebGL ? `${type} ${key};` : `${key}: ${
|
|
67
|
+
lines.push(c.isWebGL ? `${type} ${key};` : `${key}: ${getConversions(type, c)},`)
|
|
99
68
|
}
|
|
100
69
|
const ret = lines.join('\n ')
|
|
101
70
|
return `struct ${id} {\n ${ret}\n};`
|
|
@@ -123,13 +92,47 @@ export const parseStruct = (
|
|
|
123
92
|
}
|
|
124
93
|
}
|
|
125
94
|
|
|
95
|
+
/**
|
|
96
|
+
* define
|
|
97
|
+
*/
|
|
98
|
+
export const parseDefine = (c: NodeContext, props: NodeProps, returnType: Constants) => {
|
|
99
|
+
const { id, children = [], layout } = props
|
|
100
|
+
const [x, ...args] = children
|
|
101
|
+
const argParams: [name: string, type: string][] = []
|
|
102
|
+
const params: string[] = []
|
|
103
|
+
if (layout?.inputs)
|
|
104
|
+
for (const input of layout.inputs) {
|
|
105
|
+
argParams.push([input.name, input.type])
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
for (let i = 0; i < args.length; i++) {
|
|
109
|
+
argParams.push([`p${i}`, infer(args[i], c)])
|
|
110
|
+
}
|
|
111
|
+
const ret = []
|
|
112
|
+
if (c?.isWebGL) {
|
|
113
|
+
for (const [paramId, type] of argParams) {
|
|
114
|
+
addDependency(c, id!, type)
|
|
115
|
+
params.push(`${type} ${paramId}`)
|
|
116
|
+
}
|
|
117
|
+
addDependency(c, id!, returnType)
|
|
118
|
+
ret.push(`${returnType} ${id}(${params}) {`)
|
|
119
|
+
} else {
|
|
120
|
+
for (const [paramId, type] of argParams) params.push(`${paramId}: ${getConversions(type, c)}`)
|
|
121
|
+
ret.push(`fn ${id}(${params}) -> ${getConversions(returnType, c)} {`)
|
|
122
|
+
}
|
|
123
|
+
const scopeCode = code(x, c)
|
|
124
|
+
if (scopeCode) ret.push(scopeCode)
|
|
125
|
+
ret.push('}')
|
|
126
|
+
return ret.join('\n')
|
|
127
|
+
}
|
|
128
|
+
|
|
126
129
|
/**
|
|
127
130
|
* headers
|
|
128
131
|
*/
|
|
129
132
|
export const parseVaryingHead = (c: NodeContext, id: string, type: string) => {
|
|
130
133
|
return c.isWebGL
|
|
131
134
|
? `${type} ${id};`
|
|
132
|
-
: `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${
|
|
135
|
+
: `@location(${c.code?.vertVaryings?.size || 0}) ${id}: ${getConversions(type, c)}`
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
export const parseUniformHead = (c: NodeContext, id: string, type: Constants) => {
|
|
@@ -146,17 +149,17 @@ export const parseUniformHead = (c: NodeContext, id: string, type: Constants) =>
|
|
|
146
149
|
)
|
|
147
150
|
}
|
|
148
151
|
const { group = 0, binding = 0 } = c.gl?.webgpu?.uniforms.map.get(id) || {}
|
|
149
|
-
const wgslType =
|
|
152
|
+
const wgslType = getConversions(type, c)
|
|
150
153
|
return `@group(${group}) @binding(${binding}) var<uniform> ${id}: ${wgslType};`
|
|
151
154
|
}
|
|
152
155
|
|
|
153
156
|
export const parseAttribHead = (c: NodeContext, id: string, type: Constants) => {
|
|
154
157
|
if (c.isWebGL) return `${type} ${id};`
|
|
155
158
|
const { location = 0 } = c.gl?.webgpu?.attribs.map.get(id) || {}
|
|
156
|
-
const wgslType =
|
|
159
|
+
const wgslType = getConversions(type, c)
|
|
157
160
|
return `@location(${location}) ${id}: ${wgslType}`
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
export const parseConstantHead = (c: NodeContext, id: string, type: Constants, value: string) => {
|
|
161
|
-
return c.isWebGL ? `const ${type} ${id} = ${value};` : `const ${id}: ${
|
|
164
|
+
return c.isWebGL ? `const ${type} ${id} = ${value};` : `const ${id}: ${getConversions(type, c)} = ${value};`
|
|
162
165
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { is } from '../utils/helpers'
|
|
2
1
|
import {
|
|
3
2
|
CONSTANTS,
|
|
4
3
|
CONVERSIONS,
|
|
@@ -8,7 +7,8 @@ import {
|
|
|
8
7
|
TYPE_MAPPING,
|
|
9
8
|
WGSL_TO_GLSL_BUILTIN,
|
|
10
9
|
} from './const'
|
|
11
|
-
import
|
|
10
|
+
import { is } from '../../utils/helpers'
|
|
11
|
+
import type { Constants, Conversions, Functions, NodeContext, NodeProxy, Operators, Swizzles, X } from '../types'
|
|
12
12
|
|
|
13
13
|
export const isSwizzle = (key: unknown): key is Swizzles => {
|
|
14
14
|
return is.str(key) && /^[xyzwrgbastpq]{1,4}$/.test(key)
|
|
@@ -46,9 +46,13 @@ export const hex2rgb = (hex: number) => {
|
|
|
46
46
|
|
|
47
47
|
let count = 0
|
|
48
48
|
|
|
49
|
-
export const getId = () => `
|
|
49
|
+
export const getId = () => `x${count++}`
|
|
50
50
|
|
|
51
|
-
export const
|
|
51
|
+
export const getBluiltin = (id: string) => {
|
|
52
|
+
return WGSL_TO_GLSL_BUILTIN[id as keyof typeof WGSL_TO_GLSL_BUILTIN]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const getConversions = <T extends Constants>(x: X<T>, c?: NodeContext) => {
|
|
52
56
|
if (!is.str(x)) return ''
|
|
53
57
|
if (c?.isWebGL) return x
|
|
54
58
|
return TYPE_MAPPING[x as keyof typeof TYPE_MAPPING] || x // for struct type
|
|
@@ -58,11 +62,7 @@ export const getOperator = (op: X<string>) => {
|
|
|
58
62
|
return OPERATORS[op as keyof typeof OPERATORS] || op
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
export const
|
|
62
|
-
return WGSL_TO_GLSL_BUILTIN[id as keyof typeof WGSL_TO_GLSL_BUILTIN]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export const conversionToConstant = (conversionKey: string): Constants => {
|
|
65
|
+
export const getConstant = (conversionKey: string): Constants => {
|
|
66
66
|
const index = CONVERSIONS.indexOf(conversionKey as Conversions)
|
|
67
67
|
return index !== -1 ? CONSTANTS[index] : 'float'
|
|
68
68
|
}
|
|
@@ -79,7 +79,7 @@ export const getEventFun = (c: NodeContext, id: string, isAttribute = false, isT
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export const safeEventCall = <T extends Constants>(x: X<T>, fun: (value: unknown) => void) => {
|
|
82
|
-
if (
|
|
82
|
+
if (is.und(x)) return
|
|
83
83
|
if (!isNodeProxy(x)) return fun(x) // for uniform(1)
|
|
84
84
|
if (x.type !== 'conversion') return
|
|
85
85
|
const value = x.props.children?.slice(1).filter(Boolean)
|
|
@@ -88,42 +88,26 @@ export const safeEventCall = <T extends Constants>(x: X<T>, fun: (value: unknown
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
export const initNodeContext = (c: NodeContext) => {
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
if (!c.isWebGL) {
|
|
101
|
-
c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
|
|
102
|
-
c.code.vertOutputs.set('position', '@builtin(position) position: vec4f')
|
|
103
|
-
}
|
|
91
|
+
if (c.code) return c
|
|
92
|
+
c.code = {
|
|
93
|
+
headers: new Map(),
|
|
94
|
+
fragInputs: new Map(),
|
|
95
|
+
vertInputs: new Map(),
|
|
96
|
+
vertOutputs: new Map(),
|
|
97
|
+
vertVaryings: new Map(),
|
|
98
|
+
dependencies: new Map(),
|
|
104
99
|
}
|
|
100
|
+
if (c.isWebGL) return c
|
|
101
|
+
c.code.fragInputs.set('position', '@builtin(position) position: vec4f')
|
|
102
|
+
c.code.vertOutputs.set('position', '@builtin(position) position: vec4f')
|
|
105
103
|
return c
|
|
106
104
|
}
|
|
107
105
|
|
|
106
|
+
export const isArrayAccess = (key: unknown): boolean => {
|
|
107
|
+
return is.num(key) || (is.str(key) && /^\d+$/.test(key))
|
|
108
|
+
}
|
|
109
|
+
|
|
108
110
|
export const addDependency = (c: NodeContext, id = '', type: string) => {
|
|
109
111
|
if (!c.code?.dependencies?.has(id)) c.code!.dependencies.set(id, new Set())
|
|
110
112
|
if (!isConstants(type)) c.code!.dependencies.get(id)!.add(type)
|
|
111
113
|
}
|
|
112
|
-
|
|
113
|
-
export const sortHeadersByDependencies = (headers: Map<string, string>, dependencies: Map<string, Set<string>>) => {
|
|
114
|
-
const sorted: [string, string][] = []
|
|
115
|
-
const visited = new Set<string>()
|
|
116
|
-
const visiting = new Set<string>()
|
|
117
|
-
const visit = (id: string) => {
|
|
118
|
-
if (visiting.has(id)) return
|
|
119
|
-
if (visited.has(id)) return
|
|
120
|
-
visiting.add(id)
|
|
121
|
-
const deps = dependencies.get(id) || new Set()
|
|
122
|
-
for (const dep of deps) if (headers.has(dep)) visit(dep)
|
|
123
|
-
visiting.delete(id)
|
|
124
|
-
visited.add(id)
|
|
125
|
-
if (headers.has(id)) sorted.push([id, headers.get(id)!])
|
|
126
|
-
}
|
|
127
|
-
for (const [id] of headers) visit(id)
|
|
128
|
-
return sorted
|
|
129
|
-
}
|
package/src/types.ts
CHANGED
|
@@ -2,56 +2,6 @@ import type { EventState, Nested } from 'reev'
|
|
|
2
2
|
import type { Fun, Queue, Frame } from 'refr'
|
|
3
3
|
import type { NodeProxy, Vec4 } from './node'
|
|
4
4
|
export type { Fun, Queue, Frame }
|
|
5
|
-
export type PrecisionMode = 'highp' | 'mediump' | 'lowp'
|
|
6
|
-
export type GLClearMode = 'COLOR_BUFFER_BIT' | 'DEPTH_BUFFER_BIT' | 'STENCIL_BUFFER_BIT'
|
|
7
|
-
export type GLDrawType = 'UNSIGNED_BYTE' | 'UNSIGNED_SHORT' | 'UNSIGNED_INT'
|
|
8
|
-
export type GLDrawMode =
|
|
9
|
-
| 'POINTS'
|
|
10
|
-
| 'LINE_STRIP'
|
|
11
|
-
| 'LINE_LOOP'
|
|
12
|
-
| 'LINES'
|
|
13
|
-
| 'TRIANGLE_STRIP'
|
|
14
|
-
| 'TRIANGLE_FAN'
|
|
15
|
-
| 'TRIANGLES'
|
|
16
|
-
|
|
17
|
-
export interface UniformData {
|
|
18
|
-
array: Float32Array
|
|
19
|
-
buffer: GPUBuffer
|
|
20
|
-
binding: number
|
|
21
|
-
group: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface TextureData {
|
|
25
|
-
binding: number
|
|
26
|
-
group: number
|
|
27
|
-
texture: GPUTexture
|
|
28
|
-
sampler: GPUSampler
|
|
29
|
-
view: GPUTextureView
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface AttribData {
|
|
33
|
-
array: Float32Array
|
|
34
|
-
buffer: GPUBuffer
|
|
35
|
-
location: number
|
|
36
|
-
stride: number
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface WebGLState {
|
|
40
|
-
context: WebGLRenderingContext
|
|
41
|
-
program: WebGLProgram
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface WebGPUState {
|
|
45
|
-
device: GPUDevice
|
|
46
|
-
uniforms: Nested<UniformData>
|
|
47
|
-
textures: Nested<TextureData>
|
|
48
|
-
attribs: Nested<AttribData>
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type Uniform = number | number[]
|
|
52
|
-
export type Attribute = number[]
|
|
53
|
-
export type Attributes = Record<string, Attribute>
|
|
54
|
-
export type Uniforms = Record<string, Uniform>
|
|
55
5
|
|
|
56
6
|
export type GL = EventState<{
|
|
57
7
|
/**
|
|
@@ -59,20 +9,25 @@ export type GL = EventState<{
|
|
|
59
9
|
*/
|
|
60
10
|
isNative: boolean
|
|
61
11
|
isWebGL: boolean
|
|
12
|
+
isError: boolean
|
|
62
13
|
isLoop: boolean
|
|
63
14
|
isGL: true
|
|
64
|
-
width
|
|
65
|
-
height
|
|
15
|
+
width?: number
|
|
16
|
+
height?: number
|
|
66
17
|
size: [number, number]
|
|
67
18
|
mouse: [number, number]
|
|
68
19
|
count: number
|
|
20
|
+
loading: number
|
|
69
21
|
el: HTMLCanvasElement
|
|
70
|
-
vs
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
22
|
+
vs?: string | Vec4
|
|
23
|
+
cs?: string | Vec4
|
|
24
|
+
fs?: string | Vec4
|
|
25
|
+
vert?: string | Vec4
|
|
26
|
+
comp?: string | Vec4
|
|
27
|
+
frag?: string | Vec4
|
|
28
|
+
vertex?: string | Vec4
|
|
29
|
+
compute?: string | Vec4
|
|
30
|
+
fragment?: string | Vec4
|
|
76
31
|
|
|
77
32
|
/**
|
|
78
33
|
* core state
|
|
@@ -86,13 +41,13 @@ export type GL = EventState<{
|
|
|
86
41
|
* events
|
|
87
42
|
*/
|
|
88
43
|
ref?: any
|
|
89
|
-
init(): void
|
|
90
|
-
loop(): void
|
|
91
44
|
mount(): void
|
|
92
45
|
clean(): void
|
|
46
|
+
error(e?: string): void
|
|
93
47
|
render(): void
|
|
94
48
|
resize(e?: Event): void
|
|
95
49
|
mousemove(e: Event): void
|
|
50
|
+
loop(): void
|
|
96
51
|
|
|
97
52
|
/**
|
|
98
53
|
* setter
|
|
@@ -107,4 +62,59 @@ export type GL = EventState<{
|
|
|
107
62
|
_attribute?(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
108
63
|
attribute(key: string, value: Attribute, iboValue?: Attribute): GL
|
|
109
64
|
attribute(target: { [key: string]: Attribute }): GL
|
|
65
|
+
_storage?(key: string, value: Storage): GL
|
|
66
|
+
storage(key: string, value: Storage): GL
|
|
67
|
+
storage(target: { [key: string]: Storage }): GL
|
|
110
68
|
}>
|
|
69
|
+
|
|
70
|
+
type Uniform = number | number[] | Float32Array
|
|
71
|
+
type Attribute = number[] | Float32Array
|
|
72
|
+
type Storage = number[] | Float32Array
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* for webgpu
|
|
76
|
+
*/
|
|
77
|
+
export interface UniformData {
|
|
78
|
+
array: Float32Array
|
|
79
|
+
buffer: GPUBuffer
|
|
80
|
+
binding: number
|
|
81
|
+
group: number
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface TextureData {
|
|
85
|
+
binding: number
|
|
86
|
+
group: number
|
|
87
|
+
texture: GPUTexture
|
|
88
|
+
sampler: GPUSampler
|
|
89
|
+
view: GPUTextureView
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface AttribData {
|
|
93
|
+
array: Float32Array
|
|
94
|
+
buffer: GPUBuffer
|
|
95
|
+
location: number
|
|
96
|
+
stride: number
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface StorageData {
|
|
100
|
+
array: Float32Array
|
|
101
|
+
buffer: GPUBuffer
|
|
102
|
+
binding: number
|
|
103
|
+
group: number
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface WebGPUState {
|
|
107
|
+
device: GPUDevice
|
|
108
|
+
uniforms: Nested<UniformData>
|
|
109
|
+
textures: Nested<TextureData>
|
|
110
|
+
attribs: Nested<AttribData>
|
|
111
|
+
storages: Nested<StorageData>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* for webgl
|
|
116
|
+
*/
|
|
117
|
+
export interface WebGLState {
|
|
118
|
+
context: WebGLRenderingContext
|
|
119
|
+
program: WebGLProgram
|
|
120
|
+
}
|
package/src/utils/helpers.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { GL } from './../types'
|
|
2
|
+
|
|
1
3
|
export const is = {
|
|
2
4
|
arr: Array.isArray,
|
|
3
5
|
bol: (a: unknown): a is boolean => typeof a === 'boolean',
|
|
@@ -42,3 +44,17 @@ export const sig = (value = 0, digit = -2) => {
|
|
|
42
44
|
value /= digit
|
|
43
45
|
return value
|
|
44
46
|
}
|
|
47
|
+
|
|
48
|
+
export const isFloat32 = (value: unknown): value is Float32Array => {
|
|
49
|
+
return value instanceof Float32Array
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const loadingImage = (gl: GL, src: string, fun: (source: HTMLImageElement) => void) => {
|
|
53
|
+
gl.loading++
|
|
54
|
+
const source = new Image()
|
|
55
|
+
Object.assign(source, { src, crossOrigin: 'anonymous' })
|
|
56
|
+
source.decode().then(() => {
|
|
57
|
+
fun(source)
|
|
58
|
+
gl.loading--
|
|
59
|
+
})
|
|
60
|
+
}
|
package/src/utils/pipeline.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isFloat32 } from './helpers'
|
|
2
|
+
import type { AttribData, TextureData, UniformData, StorageData } from '../types'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* initialize
|
|
5
6
|
*/
|
|
6
|
-
export const createDevice = async (c: GPUCanvasContext) => {
|
|
7
|
+
export const createDevice = async (c: GPUCanvasContext, log = console.log) => {
|
|
7
8
|
const gpu = navigator.gpu
|
|
8
9
|
const format = gpu.getPreferredCanvasFormat()
|
|
9
10
|
const adapter = await gpu.requestAdapter()
|
|
10
11
|
const device = await adapter!.requestDevice()
|
|
12
|
+
device.onuncapturederror = (e) => log(e.error.message)
|
|
11
13
|
c.configure({ device, format, alphaMode: 'opaque' })
|
|
12
14
|
return { device, format }
|
|
13
15
|
}
|
|
@@ -15,6 +17,7 @@ export const createDevice = async (c: GPUCanvasContext) => {
|
|
|
15
17
|
export const createBindings = () => {
|
|
16
18
|
let uniform = 0
|
|
17
19
|
let texture = 0
|
|
20
|
+
let storage = 0
|
|
18
21
|
let attrib = 0
|
|
19
22
|
return {
|
|
20
23
|
uniform: () => {
|
|
@@ -30,6 +33,13 @@ export const createBindings = () => {
|
|
|
30
33
|
texture++
|
|
31
34
|
return { group, binding }
|
|
32
35
|
},
|
|
36
|
+
storage: () => {
|
|
37
|
+
const baseGroup = Math.floor(uniform / 12) + Math.floor(texture / 6) + 2
|
|
38
|
+
const group = baseGroup + Math.floor(storage / 12)
|
|
39
|
+
const binding = storage % 12
|
|
40
|
+
storage++
|
|
41
|
+
return { group, binding }
|
|
42
|
+
},
|
|
33
43
|
attrib: () => {
|
|
34
44
|
const location = attrib
|
|
35
45
|
attrib++
|
|
@@ -70,7 +80,8 @@ export const createVertexBuffers = (attribs: Iterable<AttribData>) => {
|
|
|
70
80
|
export const createBindGroup = (
|
|
71
81
|
device: GPUDevice,
|
|
72
82
|
uniforms: Iterable<UniformData>,
|
|
73
|
-
textures: Iterable<TextureData
|
|
83
|
+
textures: Iterable<TextureData>,
|
|
84
|
+
storages: Iterable<StorageData> = []
|
|
74
85
|
) => {
|
|
75
86
|
const groups = new Map<number, { layouts: GPUBindGroupLayoutEntry[]; bindings: GPUBindGroupEntry[] }>()
|
|
76
87
|
const ret = { bindGroups: [] as GPUBindGroup[], bindGroupLayouts: [] as GPUBindGroupLayout[] }
|
|
@@ -81,7 +92,10 @@ export const createBindGroup = (
|
|
|
81
92
|
bindings.push(binding)
|
|
82
93
|
}
|
|
83
94
|
for (const { binding, buffer, group: i } of uniforms) {
|
|
84
|
-
add(i, { binding, visibility:
|
|
95
|
+
add(i, { binding, visibility: 7, buffer: { type: 'uniform' } }, { binding, resource: { buffer } })
|
|
96
|
+
}
|
|
97
|
+
for (const { binding, buffer, group: i } of storages) {
|
|
98
|
+
add(i, { binding, visibility: 6, buffer: { type: 'storage' } }, { binding, resource: { buffer } })
|
|
85
99
|
}
|
|
86
100
|
for (const { binding: b, group: i, sampler, view } of textures) {
|
|
87
101
|
add(i, { binding: b, visibility: 2, sampler: {} }, { binding: b, resource: sampler })
|
|
@@ -123,19 +137,34 @@ export const createPipeline = (
|
|
|
123
137
|
})
|
|
124
138
|
}
|
|
125
139
|
|
|
140
|
+
export const createComputePipeline = (device: GPUDevice, bindGroupLayouts: GPUBindGroupLayout[], cs: string) => {
|
|
141
|
+
return device.createComputePipeline({
|
|
142
|
+
compute: {
|
|
143
|
+
module: device.createShaderModule({ label: 'compute', code: cs }),
|
|
144
|
+
entryPoint: 'main',
|
|
145
|
+
},
|
|
146
|
+
layout: device.createPipelineLayout({ bindGroupLayouts }),
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
126
150
|
/**
|
|
127
151
|
* buffers
|
|
128
152
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return { array, buffer }
|
|
153
|
+
const bufferUsage = (type: 'uniform' | 'storage' | 'attrib') => {
|
|
154
|
+
if (type === 'uniform') return 72 // GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
155
|
+
if (type === 'attrib') return 40 // GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
|
|
156
|
+
return 140 // GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST
|
|
134
157
|
}
|
|
135
158
|
|
|
136
|
-
export const
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
export const createArrayBuffer = (
|
|
160
|
+
device: GPUDevice,
|
|
161
|
+
array: number[] | Float32Array,
|
|
162
|
+
type: 'uniform' | 'storage' | 'attrib'
|
|
163
|
+
) => {
|
|
164
|
+
if (!isFloat32(array)) array = new Float32Array(array)
|
|
165
|
+
const usage = bufferUsage(type)
|
|
166
|
+
const size = type === 'uniform' ? Math.ceil(array.byteLength / 256) * 256 : array.byteLength
|
|
167
|
+
const buffer = device.createBuffer({ size, usage })
|
|
139
168
|
return { array, buffer }
|
|
140
169
|
}
|
|
141
170
|
|
package/src/utils/program.ts
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
import { fragment, vertex } from '../node'
|
|
2
|
+
import { is } from './helpers'
|
|
3
|
+
import type { X } from '../node'
|
|
4
|
+
import type { GL } from '../types'
|
|
5
|
+
|
|
6
|
+
const createShader = (c: WebGLRenderingContext, source: string, type: number, onError = console.warn) => {
|
|
2
7
|
const shader = c.createShader(type)
|
|
3
|
-
if (!shader)
|
|
8
|
+
if (!shader) return onError('Failed to create shader')
|
|
4
9
|
c.shaderSource(shader, source.trim())
|
|
5
10
|
c.compileShader(shader)
|
|
6
11
|
if (c.getShaderParameter(shader, c.COMPILE_STATUS)) return shader
|
|
7
12
|
const error = c.getShaderInfoLog(shader)
|
|
8
13
|
c.deleteShader(shader)
|
|
9
|
-
|
|
14
|
+
onError(`Could not compile shader: ${error}`)
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
export const createProgram = (c: WebGLRenderingContext, vert:
|
|
17
|
+
export const createProgram = (c: WebGLRenderingContext, vert: X, frag: X, gl: GL) => {
|
|
18
|
+
if (!vert || !frag) return
|
|
19
|
+
const config = { isWebGL: true, gl }
|
|
20
|
+
frag = fragment(frag, config) // needs to be before vertex
|
|
21
|
+
vert = vertex(vert, config)
|
|
13
22
|
const pg = c.createProgram()
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
if (!fs || !vs) return
|
|
17
|
-
c.attachShader(pg, vs)
|
|
18
|
-
c.attachShader(pg, fs)
|
|
23
|
+
const vs = createShader(c, vert, c.VERTEX_SHADER, gl.error)
|
|
24
|
+
const fs = createShader(c, frag, c.FRAGMENT_SHADER, gl.error)
|
|
25
|
+
if (!fs || !vs) return
|
|
26
|
+
c.attachShader(pg, vs!)
|
|
27
|
+
c.attachShader(pg, fs!)
|
|
19
28
|
c.linkProgram(pg)
|
|
20
29
|
if (c.getProgramParameter(pg, c.LINK_STATUS)) return pg
|
|
21
30
|
const error = c.getProgramInfoLog(pg)
|
|
22
31
|
c.deleteProgram(pg)
|
|
23
|
-
|
|
24
|
-
console.warn(`Could not link program: ${error}`)
|
|
32
|
+
gl.error(`Could not link program: ${error}`)
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
const createVbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
28
36
|
const buffer = c.createBuffer()
|
|
29
37
|
c.bindBuffer(c.ARRAY_BUFFER, buffer)
|
|
30
38
|
c.bufferData(c.ARRAY_BUFFER, new Float32Array(data), c.STATIC_DRAW)
|
|
@@ -32,7 +40,7 @@ export const createVbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
|
32
40
|
return buffer
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
|
|
43
|
+
const createIbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
36
44
|
const buffer = c.createBuffer()
|
|
37
45
|
c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, buffer)
|
|
38
46
|
c.bufferData(c.ELEMENT_ARRAY_BUFFER, new Int16Array(data), c.STATIC_DRAW)
|
|
@@ -40,7 +48,7 @@ export const createIbo = (c: WebGLRenderingContext, data: number[]) => {
|
|
|
40
48
|
return buffer
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
const getStride = (count: number, value: number[], iboValue?: number[]) => {
|
|
44
52
|
if (iboValue) count = Math.max(...iboValue) + 1
|
|
45
53
|
const stride = value.length / count
|
|
46
54
|
return Math.floor(stride)
|
|
@@ -48,17 +56,28 @@ export const getStride = (count: number, value: number[], iboValue?: number[]) =
|
|
|
48
56
|
|
|
49
57
|
export const createAttrib = (
|
|
50
58
|
c: WebGLRenderingContext,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
loc: number,
|
|
60
|
+
count: number,
|
|
61
|
+
value: number[],
|
|
62
|
+
iboValue: number[]
|
|
55
63
|
) => {
|
|
64
|
+
const vbo = createVbo(c, value)
|
|
65
|
+
const ibo = createIbo(c, iboValue)
|
|
66
|
+
const str = getStride(count, value, iboValue)
|
|
56
67
|
c.bindBuffer(c.ARRAY_BUFFER, vbo)
|
|
57
68
|
c.enableVertexAttribArray(loc)
|
|
58
|
-
c.vertexAttribPointer(loc,
|
|
69
|
+
c.vertexAttribPointer(loc, str, c.FLOAT, false, 0, 0)
|
|
59
70
|
if (ibo) c.bindBuffer(c.ELEMENT_ARRAY_BUFFER, ibo)
|
|
60
71
|
}
|
|
61
72
|
|
|
73
|
+
export const createUniform = (c: WebGLRenderingContext, loc: WebGLUniformLocation, value: number | number[]) => {
|
|
74
|
+
if (is.num(value)) return c.uniform1f(loc, value)
|
|
75
|
+
let l = value.length
|
|
76
|
+
if (l <= 4) return c[`uniform${l as 2}fv`](loc, value)
|
|
77
|
+
l = Math.sqrt(l) << 0
|
|
78
|
+
c[`uniformMatrix${l as 2}fv`](loc, false, value)
|
|
79
|
+
}
|
|
80
|
+
|
|
62
81
|
export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement, loc: any, unit: number) => {
|
|
63
82
|
const texture = c.createTexture()
|
|
64
83
|
c.bindTexture(c.TEXTURE_2D, texture)
|
|
@@ -73,3 +92,21 @@ export const createTexture = (c: WebGLRenderingContext, img: HTMLImageElement, l
|
|
|
73
92
|
c.activeTexture(c.TEXTURE0 + unit)
|
|
74
93
|
c.bindTexture(c.TEXTURE_2D, texture)
|
|
75
94
|
}
|
|
95
|
+
|
|
96
|
+
export const createStorage = (c: WebGL2RenderingContext, size: number, storage: any, array: any) => {
|
|
97
|
+
const data = new Float32Array(size * size * 4)
|
|
98
|
+
for (let i = 0; i < array.length; i++) data[i * 4] = array[i]
|
|
99
|
+
c.activeTexture(c.TEXTURE0 + storage.unit)
|
|
100
|
+
c.bindTexture(c.TEXTURE_2D, storage.a.texture)
|
|
101
|
+
c.texImage2D(c.TEXTURE_2D, 0, c.RGBA32F, size, size, 0, c.RGBA, c.FLOAT, data)
|
|
102
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.NEAREST)
|
|
103
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.NEAREST)
|
|
104
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
105
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
106
|
+
c.bindTexture(c.TEXTURE_2D, storage.b.texture)
|
|
107
|
+
c.texImage2D(c.TEXTURE_2D, 0, c.RGBA32F, size, size, 0, c.RGBA, c.FLOAT, null)
|
|
108
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MIN_FILTER, c.NEAREST)
|
|
109
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_MAG_FILTER, c.NEAREST)
|
|
110
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_S, c.CLAMP_TO_EDGE)
|
|
111
|
+
c.texParameteri(c.TEXTURE_2D, c.TEXTURE_WRAP_T, c.CLAMP_TO_EDGE)
|
|
112
|
+
}
|