simulationjsv2 0.10.6 → 0.11.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/TODO.md +4 -20
- package/dist/backend.d.ts +38 -0
- package/dist/backend.js +127 -0
- package/dist/backends/backend.d.ts +22 -0
- package/dist/backends/backend.js +21 -0
- package/dist/backends/webgl.d.ts +19 -0
- package/dist/backends/webgl.js +112 -0
- package/dist/backends/webgpu.d.ts +25 -0
- package/dist/backends/webgpu.js +134 -0
- package/dist/buffers/buffer.d.ts +15 -0
- package/dist/buffers/buffer.js +42 -0
- package/dist/buffers/webgl.d.ts +13 -0
- package/dist/buffers/webgl.js +46 -0
- package/dist/buffers/webgpu.d.ts +12 -0
- package/dist/buffers/webgpu.js +40 -0
- package/dist/buffers.d.ts +20 -6
- package/dist/buffers.js +54 -20
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/geometry.d.ts +3 -2
- package/dist/geometry.js +8 -4
- package/dist/globals.d.ts +6 -6
- package/dist/globals.js +7 -12
- package/dist/graphics.d.ts +18 -18
- package/dist/graphics.js +57 -59
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/internalUtils.d.ts +2 -2
- package/dist/internalUtils.js +3 -1
- package/dist/shaders/shader.d.ts +18 -0
- package/dist/shaders/shader.js +63 -0
- package/dist/shaders/utils.d.ts +33 -0
- package/dist/shaders/utils.js +25 -0
- package/dist/shaders/webgl.d.ts +74 -0
- package/dist/shaders/webgl.js +242 -0
- package/dist/shaders/webgpu.d.ts +40 -0
- package/dist/{shaders.js → shaders/webgpu.js} +73 -114
- package/dist/simulation.d.ts +11 -5
- package/dist/simulation.js +49 -86
- package/dist/types.d.ts +54 -35
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +6 -9
- package/package.json +26 -26
- package/dist/pipelineUtil.d.ts +0 -5
- package/dist/pipelineUtil.js +0 -22
- package/dist/shaders.d.ts +0 -36
|
@@ -1,32 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mat4ByteLength } from '
|
|
3
|
-
import { globalInfo } from '
|
|
4
|
-
import { createBindGroup
|
|
5
|
-
|
|
1
|
+
import { WebGPUMemoBuffer } from '../buffers/webgpu.js';
|
|
2
|
+
import { mat4ByteLength, modelProjMatOffset } from '../constants.js';
|
|
3
|
+
import { globalInfo, logger } from '../globals.js';
|
|
4
|
+
import { createBindGroup } from '../utils.js';
|
|
5
|
+
import { orthogonalMatrix, worldProjectionMatrix } from '../simulation.js';
|
|
6
|
+
import { worldProjMatOffset } from '../constants.js';
|
|
7
|
+
import { defaultVertexColorBufferWriter, SimJSShader } from './shader.js';
|
|
8
|
+
export const WEBGPU_DEFAULT_SHADER_UNIFORM_BUFFER_SIZE = mat4ByteLength * 2 + 4 * 2 + 8; // 4x4 matrix * 2 + vec2<f32> + 8 bc 144 is cool
|
|
6
9
|
const defaultInfos = [
|
|
7
10
|
{
|
|
8
|
-
usage: GPUBufferUsage.
|
|
9
|
-
owned: false
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
usage: GPUBufferUsage.STORAGE,
|
|
11
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
13
12
|
defaultSize: 10 * 4 * 16 // not sure where this came from, made it up a while ago
|
|
14
13
|
}
|
|
15
14
|
];
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const defaultWebGPUUniformBufferWriter = (device, el, buffers) => {
|
|
16
|
+
const projBuf = el.is3d ? worldProjectionMatrix : orthogonalMatrix;
|
|
17
|
+
let buffer = el.getUniformBuffer();
|
|
18
|
+
if (!buffer) {
|
|
19
|
+
buffer = new WebGPUMemoBuffer(device, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, WEBGPU_DEFAULT_SHADER_UNIFORM_BUFFER_SIZE);
|
|
20
|
+
el.setUniformBuffer(buffer);
|
|
21
|
+
}
|
|
22
|
+
device.queue.writeBuffer(buffer.getBuffer(), worldProjMatOffset, projBuf.buffer, projBuf.byteOffset, projBuf.byteLength);
|
|
23
|
+
const modelMatrix = el.getModelMatrix();
|
|
24
|
+
device.queue.writeBuffer(buffer.getBuffer(), modelProjMatOffset, modelMatrix.buffer, modelMatrix.byteOffset, modelMatrix.byteLength);
|
|
25
|
+
if (el.isInstance) {
|
|
26
|
+
buffers[0].write(el.getInstanceBuffer());
|
|
27
|
+
}
|
|
20
28
|
};
|
|
21
|
-
const defaultBindGroupGenerator = (el, buffers) => {
|
|
29
|
+
const defaultBindGroupGenerator = (_device, el, buffers) => {
|
|
30
|
+
// TODO - why is this here?
|
|
22
31
|
const shader = el.getShader();
|
|
32
|
+
if (!shader.compatableWith('webgpu'))
|
|
33
|
+
throw logger.error('Wrong shader type for backend or something idk');
|
|
23
34
|
const gpuBuffers = [
|
|
24
|
-
el.getUniformBuffer(),
|
|
25
|
-
|
|
35
|
+
el.getUniformBuffer().getBuffer(),
|
|
36
|
+
buffers[0].getBuffer()
|
|
26
37
|
];
|
|
27
38
|
return [createBindGroup(shader, 0, gpuBuffers)];
|
|
28
39
|
};
|
|
29
|
-
export class
|
|
40
|
+
export class SimJSWebGPUShader extends SimJSShader {
|
|
41
|
+
buffers;
|
|
30
42
|
bindGroupLayoutDescriptors;
|
|
31
43
|
bindGroupLayouts;
|
|
32
44
|
module;
|
|
@@ -34,29 +46,23 @@ export class Shader {
|
|
|
34
46
|
fragmentMain;
|
|
35
47
|
vertexMain;
|
|
36
48
|
vertexBuffers;
|
|
37
|
-
|
|
38
|
-
bufferWriter;
|
|
39
|
-
vertexBufferWriter;
|
|
49
|
+
uniformBufferWriter;
|
|
40
50
|
bindGroupGenerator;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
constructor(code, descriptors, vertexParams,
|
|
51
|
+
bufferDeclerations;
|
|
52
|
+
device;
|
|
53
|
+
constructor(code, descriptors, vertexParams, bufferDeclerations, uniformBufferWriter, bindGroupGenerator, vertexBufferWriter, vertexMain = 'vertex_main', fragmentMain = 'fragment_main') {
|
|
54
|
+
super('webgpu', vertexBufferWriter);
|
|
55
|
+
this.buffers = [];
|
|
56
|
+
this.device = null;
|
|
44
57
|
this.code = code;
|
|
45
58
|
this.bindGroupLayoutDescriptors = descriptors;
|
|
46
59
|
this.bindGroupLayouts = null;
|
|
47
60
|
this.module = null;
|
|
48
|
-
this.
|
|
49
|
-
this.vertexBufferWriter = vertexBufferWriter;
|
|
61
|
+
this.uniformBufferWriter = uniformBufferWriter;
|
|
50
62
|
this.bindGroupGenerator = bindGroupGenerator;
|
|
51
63
|
this.vertexMain = vertexMain;
|
|
52
64
|
this.fragmentMain = fragmentMain;
|
|
53
|
-
this.
|
|
54
|
-
this.buffers = [];
|
|
55
|
-
for (let i = 0; i < bufferInfos.length; i++) {
|
|
56
|
-
if (bufferInfos[i].owned === false)
|
|
57
|
-
continue;
|
|
58
|
-
this.buffers.push(new MemoBuffer(bufferInfos[i].usage, bufferInfos[i].defaultSize ?? 0));
|
|
59
|
-
}
|
|
65
|
+
this.bufferDeclerations = bufferDeclerations;
|
|
60
66
|
let stride = 0;
|
|
61
67
|
const attributes = [];
|
|
62
68
|
for (let i = 0; i < vertexParams.length; i++) {
|
|
@@ -72,18 +78,30 @@ export class Shader {
|
|
|
72
78
|
arrayStride: stride,
|
|
73
79
|
attributes
|
|
74
80
|
};
|
|
81
|
+
const canvas = globalInfo.getCanvas();
|
|
82
|
+
if (!canvas) {
|
|
83
|
+
globalInfo.addToInitShader(this);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const backend = canvas.getBackend().as('webgpu');
|
|
87
|
+
this.init(backend.getDeviceOrError());
|
|
88
|
+
}
|
|
89
|
+
init(device) {
|
|
90
|
+
this.device = device;
|
|
91
|
+
for (let i = 0; i < this.bufferDeclerations.length; i++) {
|
|
92
|
+
this.buffers.push(new WebGPUMemoBuffer(device, this.bufferDeclerations[i].usage, this.bufferDeclerations[i].defaultSize ?? 0));
|
|
93
|
+
}
|
|
75
94
|
}
|
|
76
95
|
getCode() {
|
|
77
96
|
return this.code;
|
|
78
97
|
}
|
|
79
|
-
getBufferLength() {
|
|
80
|
-
return this.bufferLength;
|
|
81
|
-
}
|
|
82
98
|
getVertexBuffers() {
|
|
83
99
|
return this.vertexBuffers;
|
|
84
100
|
}
|
|
85
101
|
getBindGroupLayouts() {
|
|
86
|
-
|
|
102
|
+
// TODO - probably change
|
|
103
|
+
const backend = globalInfo.errorGetCanvas().getBackend();
|
|
104
|
+
const device = backend.getDeviceOrError();
|
|
87
105
|
if (!this.bindGroupLayouts) {
|
|
88
106
|
this.bindGroupLayouts = this.bindGroupLayoutDescriptors.map((descriptor) => device.createBindGroupLayout(descriptor));
|
|
89
107
|
}
|
|
@@ -93,10 +111,10 @@ export class Shader {
|
|
|
93
111
|
return this.bindGroupLayoutDescriptors;
|
|
94
112
|
}
|
|
95
113
|
getBufferInfo() {
|
|
96
|
-
return this.
|
|
114
|
+
return this.bufferDeclerations;
|
|
97
115
|
}
|
|
98
|
-
|
|
99
|
-
return this.
|
|
116
|
+
getUniformBufferWriter() {
|
|
117
|
+
return this.uniformBufferWriter;
|
|
100
118
|
}
|
|
101
119
|
getVertexBufferWriter() {
|
|
102
120
|
return this.vertexBufferWriter;
|
|
@@ -105,7 +123,9 @@ export class Shader {
|
|
|
105
123
|
return this.bindGroupGenerator;
|
|
106
124
|
}
|
|
107
125
|
getModule() {
|
|
108
|
-
|
|
126
|
+
// TODO - probably change
|
|
127
|
+
const backend = globalInfo.errorGetCanvas().getBackend();
|
|
128
|
+
const device = backend.getDeviceOrError();
|
|
109
129
|
if (!this.module) {
|
|
110
130
|
this.module = device.createShaderModule({ code: this.code });
|
|
111
131
|
}
|
|
@@ -120,19 +140,20 @@ export class Shader {
|
|
|
120
140
|
setVertexInfo(element, buffer, vertex, vertexIndex, offset) {
|
|
121
141
|
this.vertexBufferWriter(element, buffer, vertex, vertexIndex, offset);
|
|
122
142
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
writeUniformBuffers(el) {
|
|
144
|
+
if (!this.device)
|
|
145
|
+
throw logger.error('Shader not initialized');
|
|
146
|
+
this.uniformBufferWriter(this.device, el, this.buffers);
|
|
126
147
|
}
|
|
127
|
-
getBindGroups(el) {
|
|
128
|
-
return this.bindGroupGenerator(el, this.buffers);
|
|
148
|
+
getBindGroups(device, el) {
|
|
149
|
+
return this.bindGroupGenerator(device, el, this.buffers);
|
|
129
150
|
}
|
|
130
151
|
}
|
|
131
152
|
const positionSize = 4 * 3;
|
|
132
153
|
const colorSize = 4 * 4;
|
|
133
154
|
const uvSize = 4 * 2;
|
|
134
155
|
const drawingInstancesSize = 4;
|
|
135
|
-
|
|
156
|
+
const defaultWebGPUShaderSource = `struct Uniforms {
|
|
136
157
|
worldProjectionMatrix: mat4x4<f32>,
|
|
137
158
|
modelProjectionMatrix: mat4x4<f32>,
|
|
138
159
|
}
|
|
@@ -178,7 +199,8 @@ fn fragment_main(
|
|
|
178
199
|
) -> @location(0) vec4<f32> {
|
|
179
200
|
return fragColor;
|
|
180
201
|
}
|
|
181
|
-
|
|
202
|
+
`;
|
|
203
|
+
export const defaultWebGPUShader = new SimJSWebGPUShader(defaultWebGPUShaderSource, [
|
|
182
204
|
{
|
|
183
205
|
entries: [
|
|
184
206
|
{
|
|
@@ -214,7 +236,7 @@ fn fragment_main(
|
|
|
214
236
|
size: drawingInstancesSize,
|
|
215
237
|
format: 'float32'
|
|
216
238
|
}
|
|
217
|
-
], defaultInfos,
|
|
239
|
+
], defaultInfos, defaultWebGPUUniformBufferWriter, defaultBindGroupGenerator, (el, buffer, vertex, _, offset) => {
|
|
218
240
|
const material = el.getMaterial();
|
|
219
241
|
const vertexColor = material.getColor();
|
|
220
242
|
buffer[offset] = vertex[0];
|
|
@@ -229,54 +251,7 @@ fn fragment_main(
|
|
|
229
251
|
buffer[offset + 8] = 0;
|
|
230
252
|
buffer[offset + 9] = el.isInstanced ? 1 : 0;
|
|
231
253
|
});
|
|
232
|
-
export const
|
|
233
|
-
struct Uniforms {
|
|
234
|
-
worldProjectionMatrix: mat4x4<f32>,
|
|
235
|
-
modelProjectionMatrix: mat4x4<f32>,
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
239
|
-
|
|
240
|
-
@group(0) @binding(1) var<storage> instanceMatrices: array<mat4x4f>;
|
|
241
|
-
|
|
242
|
-
struct VertexOutput {
|
|
243
|
-
@builtin(position) Position: vec4<f32>,
|
|
244
|
-
@location(0) fragUV: vec2<f32>,
|
|
245
|
-
@location(1) fragColor: vec4<f32>,
|
|
246
|
-
@location(2) fragPosition: vec4<f32>,
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
@vertex
|
|
250
|
-
fn vertex_main(
|
|
251
|
-
@builtin(instance_index) instanceIdx: u32,
|
|
252
|
-
@location(0) position: vec3<f32>,
|
|
253
|
-
@location(1) color: vec4<f32>,
|
|
254
|
-
@location(2) uv: vec2<f32>,
|
|
255
|
-
@location(3) drawingInstance: f32
|
|
256
|
-
) -> VertexOutput {
|
|
257
|
-
var output: VertexOutput;
|
|
258
|
-
|
|
259
|
-
if (drawingInstance == 1) {
|
|
260
|
-
output.Position = uniforms.worldProjectionMatrix * uniforms.modelProjectionMatrix * instanceMatrices[instanceIdx] * vec4(position, 1.0);
|
|
261
|
-
} else {
|
|
262
|
-
output.Position = uniforms.worldProjectionMatrix * uniforms.modelProjectionMatrix * vec4(position, 1.0);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
output.fragUV = uv;
|
|
266
|
-
output.fragPosition = output.Position;
|
|
267
|
-
output.fragColor = color;
|
|
268
|
-
return output;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
@fragment
|
|
272
|
-
fn fragment_main(
|
|
273
|
-
@location(0) fragUV: vec2<f32>,
|
|
274
|
-
@location(1) fragColor: vec4<f32>,
|
|
275
|
-
@location(2) fragPosition: vec4<f32>
|
|
276
|
-
) -> @location(0) vec4<f32> {
|
|
277
|
-
return fragColor;
|
|
278
|
-
}
|
|
279
|
-
`, [
|
|
254
|
+
export const defaultWebGPUVertexColorShader = new SimJSWebGPUShader(defaultWebGPUShaderSource, [
|
|
280
255
|
{
|
|
281
256
|
entries: [
|
|
282
257
|
{
|
|
@@ -312,20 +287,4 @@ fn fragment_main(
|
|
|
312
287
|
size: drawingInstancesSize,
|
|
313
288
|
format: 'float32'
|
|
314
289
|
}
|
|
315
|
-
], defaultInfos,
|
|
316
|
-
const material = el.getMaterial();
|
|
317
|
-
const colors = material.getVertexColors();
|
|
318
|
-
const vertexColor = colors[vertexIndex] ?? el.getColor();
|
|
319
|
-
// const vertexColor = color(0, 255, 255);
|
|
320
|
-
buffer[offset] = vertex[0];
|
|
321
|
-
buffer[offset + 1] = vertex[1];
|
|
322
|
-
buffer[offset + 2] = vertex[2];
|
|
323
|
-
buffer[offset + 3] = vertexColor.r / 255;
|
|
324
|
-
buffer[offset + 4] = vertexColor.g / 255;
|
|
325
|
-
buffer[offset + 5] = vertexColor.b / 255;
|
|
326
|
-
buffer[offset + 6] = vertexColor.a;
|
|
327
|
-
// TODO possibly change uv for textures
|
|
328
|
-
buffer[offset + 7] = 0;
|
|
329
|
-
buffer[offset + 8] = 0;
|
|
330
|
-
buffer[offset + 9] = el.isInstanced ? 1 : 0;
|
|
331
|
-
});
|
|
290
|
+
], defaultInfos, defaultWebGPUUniformBufferWriter, defaultBindGroupGenerator, defaultVertexColorBufferWriter);
|
package/dist/simulation.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SimulationElement3d } from './graphics.js';
|
|
2
|
-
import type { Vector2, Vector3, LerpFunc } from './types.js';
|
|
2
|
+
import type { Vector2, Vector3, LerpFunc, BackendType } from './types.js';
|
|
3
3
|
import { Color } from './utils.js';
|
|
4
4
|
import { Settings } from './settings.js';
|
|
5
|
+
import { SimJsBackend } from './backends/backend.js';
|
|
5
6
|
export declare const worldProjectionMatrix: import("./types.js").Mat4;
|
|
6
7
|
export declare const orthogonalMatrix: import("./types.js").Mat4;
|
|
7
8
|
export declare class Camera {
|
|
@@ -24,9 +25,13 @@ export declare class Camera {
|
|
|
24
25
|
getAspectRatio(): number;
|
|
25
26
|
}
|
|
26
27
|
export declare let camera: Camera;
|
|
28
|
+
type SimulationOptions = {
|
|
29
|
+
sceneCamera?: Camera | null;
|
|
30
|
+
showFrameRate?: boolean;
|
|
31
|
+
backendMode?: BackendType;
|
|
32
|
+
};
|
|
27
33
|
export declare class Simulation extends Settings {
|
|
28
34
|
canvasRef: HTMLCanvasElement | null;
|
|
29
|
-
private bgColor;
|
|
30
35
|
private scene;
|
|
31
36
|
private fittingElement;
|
|
32
37
|
private running;
|
|
@@ -34,12 +39,12 @@ export declare class Simulation extends Settings {
|
|
|
34
39
|
private resizeEvents;
|
|
35
40
|
private frameRateView;
|
|
36
41
|
private transparentElements;
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
constructor(idOrCanvasRef: string | HTMLCanvasElement, sceneCamera?: Camera | null, showFrameRate?: boolean);
|
|
42
|
+
private backend;
|
|
43
|
+
constructor(idOrCanvasRef: string | HTMLCanvasElement, options?: SimulationOptions);
|
|
40
44
|
private handleCanvasResize;
|
|
41
45
|
on<K extends keyof HTMLElementEventMap>(event: K, cb: (this: HTMLCanvasElement, ev: HTMLElementEventMap[K]) => void, options?: boolean | AddEventListenerOptions): void;
|
|
42
46
|
onResize(cb: (width: number, height: number) => void): void;
|
|
47
|
+
getBackend(): SimJsBackend;
|
|
43
48
|
getWidth(): number;
|
|
44
49
|
getHeight(): number;
|
|
45
50
|
add(el: SimulationElement3d, id?: string): void;
|
|
@@ -58,3 +63,4 @@ export declare class Simulation extends Settings {
|
|
|
58
63
|
private renderScene;
|
|
59
64
|
fitElement(): void;
|
|
60
65
|
}
|
|
66
|
+
export {};
|
package/dist/simulation.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { vec3 } from 'wgpu-matrix';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { matrix4, transitionValues, vector2, vector3, webGLAvailable } from './utils.js';
|
|
3
|
+
import { updateProjectionMatrix, updateOrthoProjectionMatrix, updateWorldProjectionMatrix, CachedArray, addToScene, removeSceneObj, removeSceneId } from './internalUtils.js';
|
|
4
4
|
import { Settings } from './settings.js';
|
|
5
|
-
import { MemoBuffer } from './buffers.js';
|
|
6
5
|
import { globalInfo, logger } from './globals.js';
|
|
6
|
+
import { WebGLBackend } from './backends/webgl.js';
|
|
7
|
+
import { WebGPUBackend } from './backends/webgpu.js';
|
|
7
8
|
const simjsFrameRateCss = `.simjs-frame-rate {
|
|
8
9
|
position: absolute;
|
|
9
10
|
top: 0;
|
|
@@ -142,9 +143,13 @@ export class Camera {
|
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
export let camera = new Camera(vector3());
|
|
146
|
+
const defaultSimulationOptions = {
|
|
147
|
+
sceneCamera: null,
|
|
148
|
+
showFrameRate: false,
|
|
149
|
+
backendMode: 'webgpu'
|
|
150
|
+
};
|
|
145
151
|
export class Simulation extends Settings {
|
|
146
152
|
canvasRef = null;
|
|
147
|
-
bgColor = new Color(255, 255, 255);
|
|
148
153
|
scene = [];
|
|
149
154
|
fittingElement = false;
|
|
150
155
|
running = true;
|
|
@@ -152,9 +157,9 @@ export class Simulation extends Settings {
|
|
|
152
157
|
resizeEvents;
|
|
153
158
|
frameRateView;
|
|
154
159
|
transparentElements;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
backend;
|
|
161
|
+
constructor(idOrCanvasRef, options = {}) {
|
|
162
|
+
const { sceneCamera = defaultSimulationOptions.sceneCamera, showFrameRate = defaultSimulationOptions.showFrameRate, backendMode = defaultSimulationOptions.backendMode } = options;
|
|
158
163
|
super();
|
|
159
164
|
if (typeof idOrCanvasRef === 'string') {
|
|
160
165
|
const ref = document.getElementById(idOrCanvasRef);
|
|
@@ -181,8 +186,15 @@ export class Simulation extends Settings {
|
|
|
181
186
|
this.frameRateView = new FrameRateView(showFrameRate);
|
|
182
187
|
this.frameRateView.updateFrameRate(1);
|
|
183
188
|
this.transparentElements = new CachedArray();
|
|
184
|
-
|
|
185
|
-
|
|
189
|
+
if (backendMode === 'webgpu' && 'gpu' in navigator) {
|
|
190
|
+
this.backend = new WebGPUBackend();
|
|
191
|
+
}
|
|
192
|
+
else if (webGLAvailable(this.canvasRef)) {
|
|
193
|
+
this.backend = new WebGLBackend();
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
throw logger.error('WebGL and WebGPU not available');
|
|
197
|
+
}
|
|
186
198
|
}
|
|
187
199
|
handleCanvasResize(parent) {
|
|
188
200
|
if (this.fittingElement) {
|
|
@@ -199,6 +211,9 @@ export class Simulation extends Settings {
|
|
|
199
211
|
onResize(cb) {
|
|
200
212
|
this.resizeEvents.push(cb);
|
|
201
213
|
}
|
|
214
|
+
getBackend() {
|
|
215
|
+
return this.backend;
|
|
216
|
+
}
|
|
202
217
|
getWidth() {
|
|
203
218
|
return this.canvasRef?.width || 0;
|
|
204
219
|
}
|
|
@@ -242,35 +257,23 @@ export class Simulation extends Settings {
|
|
|
242
257
|
(async () => {
|
|
243
258
|
if (this.canvasRef === null)
|
|
244
259
|
return;
|
|
245
|
-
this.initialized = true;
|
|
246
|
-
this.running = true;
|
|
247
|
-
const adapter = await navigator.gpu.requestAdapter();
|
|
248
|
-
if (!adapter)
|
|
249
|
-
throw logger.error('Adapter is null');
|
|
250
|
-
const ctx = this.canvasRef.getContext('webgpu');
|
|
251
|
-
if (!ctx)
|
|
252
|
-
throw logger.error('Context is null');
|
|
253
|
-
const device = await adapter.requestDevice();
|
|
254
|
-
globalInfo.setDevice(device);
|
|
255
260
|
const screenSize = vector2(this.canvasRef.width, this.canvasRef.height);
|
|
256
261
|
camera.setScreenSize(screenSize);
|
|
257
262
|
const canvas = this.canvasRef;
|
|
258
263
|
canvas.width = canvas.clientWidth * devicePixelRatio;
|
|
259
264
|
canvas.height = canvas.clientHeight * devicePixelRatio;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
});
|
|
266
|
-
this.render(device, ctx, canvas);
|
|
265
|
+
this.initialized = true;
|
|
266
|
+
this.running = true;
|
|
267
|
+
await this.backend.init(this.canvasRef);
|
|
268
|
+
this.backend.initShaders(globalInfo.getToInitShaders());
|
|
269
|
+
this.render(canvas, this.backend);
|
|
267
270
|
})();
|
|
268
271
|
}
|
|
269
272
|
stop() {
|
|
270
273
|
this.running = false;
|
|
271
274
|
}
|
|
272
275
|
setBackground(color) {
|
|
273
|
-
this.
|
|
276
|
+
this.backend.setClearColor(color);
|
|
274
277
|
}
|
|
275
278
|
setDefaultColor(color) {
|
|
276
279
|
globalInfo.setDefaultColor(color);
|
|
@@ -281,15 +284,7 @@ export class Simulation extends Settings {
|
|
|
281
284
|
getScene() {
|
|
282
285
|
return this.scene;
|
|
283
286
|
}
|
|
284
|
-
render(
|
|
285
|
-
const colorAttachment = {
|
|
286
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
287
|
-
// @ts-ignore
|
|
288
|
-
view: undefined, // Assigned later
|
|
289
|
-
clearValue: this.bgColor.toObject(),
|
|
290
|
-
loadOp: 'clear',
|
|
291
|
-
storeOp: 'store'
|
|
292
|
-
};
|
|
287
|
+
render(canvas, backend) {
|
|
293
288
|
const newAspectRatio = canvas.width / canvas.height;
|
|
294
289
|
if (newAspectRatio !== aspectRatio) {
|
|
295
290
|
updateProjectionMatrix(projMat, newAspectRatio);
|
|
@@ -297,23 +292,11 @@ export class Simulation extends Settings {
|
|
|
297
292
|
}
|
|
298
293
|
updateWorldProjectionMatrix(worldProjectionMatrix, projMat);
|
|
299
294
|
updateOrthoProjectionMatrix(orthogonalMatrix, camera.getScreenSize());
|
|
300
|
-
|
|
301
|
-
let depthTexture = buildDepthTexture(device, canvas.width, canvas.height);
|
|
302
|
-
const renderPassDescriptor = {
|
|
303
|
-
colorAttachments: [colorAttachment],
|
|
304
|
-
depthStencilAttachment: {
|
|
305
|
-
view: depthTexture.createView(),
|
|
306
|
-
depthClearValue: 1.0,
|
|
307
|
-
depthLoadOp: 'clear',
|
|
308
|
-
depthStoreOp: 'store'
|
|
309
|
-
}
|
|
310
|
-
};
|
|
295
|
+
backend.renderStart(canvas);
|
|
311
296
|
// sub 10 to start with a reasonable gap between starting time and next frame time
|
|
312
297
|
let prev = Date.now() - 10;
|
|
313
298
|
let prevFps = 0;
|
|
314
299
|
const frame = async () => {
|
|
315
|
-
if (!canvas)
|
|
316
|
-
return;
|
|
317
300
|
requestAnimationFrame(frame);
|
|
318
301
|
if (!this.running)
|
|
319
302
|
return;
|
|
@@ -333,36 +316,23 @@ export class Simulation extends Settings {
|
|
|
333
316
|
aspectRatio = camera.getAspectRatio();
|
|
334
317
|
updateProjectionMatrix(projMat, aspectRatio);
|
|
335
318
|
updateWorldProjectionMatrix(worldProjectionMatrix, projMat);
|
|
336
|
-
|
|
337
|
-
depthTexture = buildDepthTexture(device, screenSize[0], screenSize[1]);
|
|
338
|
-
renderPassDescriptor.depthStencilAttachment.view = depthTexture.createView();
|
|
319
|
+
backend.updateTextures(screenSize);
|
|
339
320
|
}
|
|
340
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
341
|
-
// @ts-ignore
|
|
342
|
-
renderPassDescriptor.colorAttachments[0].view = multisampleTexture.createView();
|
|
343
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
344
|
-
// @ts-ignore
|
|
345
|
-
renderPassDescriptor.colorAttachments[0].resolveTarget = ctx.getCurrentTexture().createView();
|
|
346
321
|
if (camera.hasUpdated()) {
|
|
347
322
|
updateOrthoProjectionMatrix(orthogonalMatrix, camera.getScreenSize());
|
|
348
323
|
updateWorldProjectionMatrix(worldProjectionMatrix, projMat);
|
|
349
324
|
}
|
|
350
|
-
|
|
351
|
-
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
|
|
352
|
-
const [totalVerticesSize, totalIndexSize] = getVertexAndIndexSize(this.scene);
|
|
353
|
-
this.vertexBuffer.setSize(totalVerticesSize * 4);
|
|
354
|
-
this.indexBuffer.setSize(totalIndexSize * 4);
|
|
325
|
+
backend.preRender(this.scene);
|
|
355
326
|
this.transparentElements.reset();
|
|
356
|
-
const [opaqueVertexOffset, opaqueIndexOffset] = this.renderScene(
|
|
357
|
-
this.renderScene(
|
|
327
|
+
const [opaqueVertexOffset, opaqueIndexOffset] = this.renderScene(backend, 0, 0, this.scene, this.scene.length, diff, false);
|
|
328
|
+
this.renderScene(backend, opaqueVertexOffset, opaqueIndexOffset, this.transparentElements.getArray(), this.transparentElements.length, diff, true);
|
|
358
329
|
camera.updateConsumed();
|
|
359
|
-
|
|
360
|
-
device.queue.submit([commandEncoder.finish()]);
|
|
330
|
+
backend.finishRender();
|
|
361
331
|
};
|
|
362
332
|
requestAnimationFrame(frame);
|
|
363
333
|
}
|
|
364
|
-
renderScene(
|
|
365
|
-
let
|
|
334
|
+
renderScene(backend, startVertexCallOffset, startIndexOffset, scene, numElements, diff, transparent) {
|
|
335
|
+
let vertexCallOffset = startVertexCallOffset;
|
|
366
336
|
let indexOffset = startIndexOffset;
|
|
367
337
|
for (let i = 0; i < numElements; i++) {
|
|
368
338
|
const obj = scene[i];
|
|
@@ -372,30 +342,23 @@ export class Simulation extends Settings {
|
|
|
372
342
|
}
|
|
373
343
|
if (obj.hasChildren()) {
|
|
374
344
|
const childObjects = obj.getChildrenInfos();
|
|
375
|
-
const [vertexDiff, indexDiff] = this.renderScene(
|
|
376
|
-
|
|
345
|
+
const [vertexDiff, indexDiff] = this.renderScene(backend, vertexCallOffset, indexOffset, childObjects, childObjects.length, diff, transparent);
|
|
346
|
+
vertexCallOffset += vertexDiff;
|
|
377
347
|
indexOffset += indexDiff;
|
|
378
348
|
}
|
|
379
349
|
if (obj.isEmpty)
|
|
380
350
|
continue;
|
|
381
|
-
const
|
|
351
|
+
const vertexCallBuffer = obj.getVertexCallBuffer();
|
|
382
352
|
const indices = obj.getIndexBuffer();
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const instances = obj.isInstance ? obj.getNumInstances() : 1;
|
|
390
|
-
const bindGroups = obj.getShader().getBindGroups(obj);
|
|
391
|
-
for (let i = 0; i < bindGroups.length; i++) {
|
|
392
|
-
passEncoder.setBindGroup(i, bindGroups[i]);
|
|
393
|
-
}
|
|
394
|
-
passEncoder.drawIndexed(indices.length, instances);
|
|
395
|
-
vertexOffset += vertices.byteLength;
|
|
353
|
+
backend.draw(obj,
|
|
354
|
+
// vertex
|
|
355
|
+
vertexCallOffset, vertexCallBuffer,
|
|
356
|
+
// index
|
|
357
|
+
indexOffset, indices);
|
|
358
|
+
vertexCallOffset += vertexCallBuffer.byteLength;
|
|
396
359
|
indexOffset += indices.byteLength;
|
|
397
360
|
}
|
|
398
|
-
return [
|
|
361
|
+
return [vertexCallOffset - startVertexCallOffset, indexOffset - startIndexOffset];
|
|
399
362
|
}
|
|
400
363
|
fitElement() {
|
|
401
364
|
if (this.canvasRef === null)
|