simulationjsv2 0.10.6 → 0.11.1

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.
Files changed (46) hide show
  1. package/TODO.md +4 -20
  2. package/dist/backend.d.ts +38 -0
  3. package/dist/backend.js +127 -0
  4. package/dist/backends/backend.d.ts +22 -0
  5. package/dist/backends/backend.js +21 -0
  6. package/dist/backends/webgl.d.ts +19 -0
  7. package/dist/backends/webgl.js +112 -0
  8. package/dist/backends/webgpu.d.ts +25 -0
  9. package/dist/backends/webgpu.js +134 -0
  10. package/dist/buffers/buffer.d.ts +15 -0
  11. package/dist/buffers/buffer.js +42 -0
  12. package/dist/buffers/webgl.d.ts +13 -0
  13. package/dist/buffers/webgl.js +46 -0
  14. package/dist/buffers/webgpu.d.ts +12 -0
  15. package/dist/buffers/webgpu.js +40 -0
  16. package/dist/buffers.d.ts +20 -6
  17. package/dist/buffers.js +54 -20
  18. package/dist/constants.d.ts +1 -0
  19. package/dist/constants.js +1 -0
  20. package/dist/geometry.d.ts +3 -2
  21. package/dist/geometry.js +8 -4
  22. package/dist/globals.d.ts +6 -6
  23. package/dist/globals.js +7 -12
  24. package/dist/graphics.d.ts +19 -19
  25. package/dist/graphics.js +57 -59
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.js +3 -1
  28. package/dist/internalUtils.d.ts +2 -2
  29. package/dist/internalUtils.js +3 -1
  30. package/dist/shaders/shader.d.ts +18 -0
  31. package/dist/shaders/shader.js +63 -0
  32. package/dist/shaders/utils.d.ts +33 -0
  33. package/dist/shaders/utils.js +25 -0
  34. package/dist/shaders/webgl.d.ts +74 -0
  35. package/dist/shaders/webgl.js +242 -0
  36. package/dist/shaders/webgpu.d.ts +40 -0
  37. package/dist/{shaders.js → shaders/webgpu.js} +73 -114
  38. package/dist/simulation.d.ts +11 -5
  39. package/dist/simulation.js +49 -86
  40. package/dist/types.d.ts +54 -35
  41. package/dist/utils.d.ts +3 -3
  42. package/dist/utils.js +6 -9
  43. package/package.json +26 -26
  44. package/dist/pipelineUtil.d.ts +0 -5
  45. package/dist/pipelineUtil.js +0 -22
  46. package/dist/shaders.d.ts +0 -36
package/TODO.md CHANGED
@@ -1,25 +1,9 @@
1
1
  # TODO
2
2
 
3
+ - [ ] per object depth test settings on webgl backend
4
+
5
+ ## later features
6
+
3
7
  - [ ] hex to color
4
8
  - [ ] Materials for planes
5
9
  - [ ] Transform vertex colors on material
6
- - [x] Morph objects into other objects
7
- - [x] `transform` function taking another object and transitioning from current geometry's vertices (positions only)
8
- - [x] Change render vertices to use index buffer
9
- - [x] Add cull modes
10
- - [x] Fix shaders
11
- - [x] Clean up polygons
12
- - [x] Fix transparency
13
- - [x] Change absolute pos function to pos function, and pos function to relative pos
14
- - [x] Update `updateModelMatrix2d`
15
- - [x] Trace line element (wireframe strip for tracing paths)
16
- - [x] Test new transform things on 3d stuff
17
- - [x] Fix rotating nested children elements
18
- - [x] Fix instancing
19
- - [x] Remove SceneCollection and replace by elements with children
20
- - [x] Change position/rotation to be matrix transform on gpu
21
- - [x] Add update square center offset position in-place + not
22
- - [x] Make getBuffer return cached Float32Array
23
- - [x] Make input buffer position vec3 not vec4
24
- - [x] Use line strip vertices for polygon buffers
25
- - [x] Scene collection wireframe
@@ -0,0 +1,38 @@
1
+ /// <reference types="@webgpu/types" />
2
+ import { SimulationElement3d } from './graphics.js';
3
+ import { Shader } from './shaders/webgpu.js';
4
+ import { BackendType, Vector2 } from './types.js';
5
+ import { Color } from './utils.js';
6
+ export declare abstract class SimJsBackend {
7
+ readonly type: BackendType;
8
+ constructor(type: BackendType);
9
+ init(_canvas: HTMLCanvasElement): Promise<void>;
10
+ renderStart(_canvas: HTMLCanvasElement, _clearColor: Color): void;
11
+ updateTextures(_screenSize: Vector2): void;
12
+ preRender(_scene: SimulationElement3d[]): void;
13
+ finishRender(): void;
14
+ draw(_obj: SimulationElement3d, _vertexOffset: number, _vertices: Float32Array, _vertexByteOffset: number, _vertexByteLength: number, _indexOffset: number, _indices: Uint32Array, _indexByteOffset: number, _indexByteLength: number): void;
15
+ initShaders(_shaders: Shader[]): void;
16
+ }
17
+ export declare class WebGPUBackend extends SimJsBackend {
18
+ private device;
19
+ private ctx;
20
+ private renderPassDescriptor;
21
+ private multisampleTexture;
22
+ private depthTexture;
23
+ private passEncoder;
24
+ private commandEncoder;
25
+ private buffers;
26
+ constructor();
27
+ getDevice(): GPUDevice | null;
28
+ init(canvas: HTMLCanvasElement): Promise<void>;
29
+ renderStart(canvas: HTMLCanvasElement, clearColor: Color): void;
30
+ updateTextures(screenSize: Vector2): void;
31
+ preRender(scene: SimulationElement3d[]): void;
32
+ finishRender(): void;
33
+ draw(obj: SimulationElement3d, vertexOffset: number, vertices: Float32Array, vertexByteOffset: number, vertexByteLength: number, indexOffset: number, indices: Uint32Array, indexByteOffset: number, indexByteLength: number): void;
34
+ initShaders(shaders: Shader[]): void;
35
+ }
36
+ export declare class WebGLBackend extends SimJsBackend {
37
+ constructor();
38
+ }
@@ -0,0 +1,127 @@
1
+ import { WebGPUMemoBuffer } from './buffers.js';
2
+ import { logger } from './globals.js';
3
+ import { buildDepthTexture, buildMultisampleTexture, getVertexAndIndexSize } from './internalUtils.js';
4
+ export class SimJsBackend {
5
+ type;
6
+ constructor(type) {
7
+ this.type = type;
8
+ }
9
+ async init(_canvas) { }
10
+ renderStart(_canvas, _clearColor) { }
11
+ updateTextures(_screenSize) { }
12
+ preRender(_scene) { }
13
+ finishRender() { }
14
+ draw(_obj, _vertexOffset, _vertices, _vertexByteOffset, _vertexByteLength, _indexOffset, _indices, _indexByteOffset, _indexByteLength) { }
15
+ initShaders(_shaders) { }
16
+ }
17
+ export class WebGPUBackend extends SimJsBackend {
18
+ device = null;
19
+ ctx = null;
20
+ renderPassDescriptor = null;
21
+ multisampleTexture = null;
22
+ depthTexture = null;
23
+ passEncoder = null;
24
+ commandEncoder = null;
25
+ buffers = null;
26
+ constructor() {
27
+ super('webgpu');
28
+ }
29
+ getDevice() {
30
+ return this.device;
31
+ }
32
+ async init(canvas) {
33
+ const adapter = await navigator.gpu.requestAdapter();
34
+ if (!adapter)
35
+ throw logger.error('Adapter is null');
36
+ this.ctx = canvas.getContext('webgpu');
37
+ if (!this.ctx)
38
+ throw logger.error('Context is null');
39
+ this.device = await adapter.requestDevice();
40
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
41
+ this.ctx.configure({
42
+ device: this.device,
43
+ format: presentationFormat,
44
+ alphaMode: 'opaque'
45
+ });
46
+ this.buffers = {
47
+ gpuVertexBuffer: new WebGPUMemoBuffer(this.device, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, 0),
48
+ gpuIndexBuffer: new WebGPUMemoBuffer(this.device, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, 0)
49
+ };
50
+ }
51
+ renderStart(canvas, clearColor) {
52
+ if (!this.device || !this.ctx)
53
+ throw logger.error('Invalid render start state');
54
+ const colorAttachment = {
55
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
56
+ // @ts-ignore
57
+ view: undefined, // Assigned later
58
+ clearValue: clearColor.toObject(),
59
+ loadOp: 'clear',
60
+ storeOp: 'store'
61
+ };
62
+ this.multisampleTexture = buildMultisampleTexture(this.device, this.ctx, canvas.width, canvas.height);
63
+ this.depthTexture = buildDepthTexture(this.device, canvas.width, canvas.height);
64
+ this.renderPassDescriptor = {
65
+ colorAttachments: [colorAttachment],
66
+ depthStencilAttachment: {
67
+ view: this.depthTexture.createView(),
68
+ depthClearValue: 1.0,
69
+ depthLoadOp: 'clear',
70
+ depthStoreOp: 'store'
71
+ }
72
+ };
73
+ }
74
+ updateTextures(screenSize) {
75
+ if (!this.device || !this.ctx || !this.renderPassDescriptor)
76
+ return;
77
+ this.multisampleTexture = buildMultisampleTexture(this.device, this.ctx, screenSize[0], screenSize[1]);
78
+ this.depthTexture = buildDepthTexture(this.device, screenSize[0], screenSize[1]);
79
+ this.renderPassDescriptor.depthStencilAttachment.view = this.depthTexture.createView();
80
+ }
81
+ preRender(scene) {
82
+ if (!this.renderPassDescriptor || !this.ctx || !this.multisampleTexture || !this.device) {
83
+ throw logger.error('Invalid prerender state');
84
+ }
85
+ const attachment = this.renderPassDescriptor.colorAttachments[0];
86
+ attachment.view = this.multisampleTexture.createView();
87
+ attachment.resolveTarget = this.ctx.getCurrentTexture().createView();
88
+ this.commandEncoder = this.device.createCommandEncoder();
89
+ this.passEncoder = this.commandEncoder.beginRenderPass(this.renderPassDescriptor);
90
+ const [totalVerticesSize, totalIndexSize] = getVertexAndIndexSize(scene);
91
+ this.buffers.gpuVertexBuffer.ensureCapacity(totalVerticesSize * 4);
92
+ this.buffers.gpuIndexBuffer.ensureCapacity(totalIndexSize * 4);
93
+ }
94
+ finishRender() {
95
+ this.passEncoder.end();
96
+ this.device.queue.submit([this.commandEncoder.finish()]);
97
+ }
98
+ draw(obj, vertexOffset, vertices, vertexByteOffset, vertexByteLength, indexOffset, indices, indexByteOffset, indexByteLength) {
99
+ this.device.queue.writeBuffer(this.buffers.gpuVertexBuffer.getBuffer(), vertexOffset, vertices.buffer, vertexByteOffset, vertexByteLength);
100
+ this.device.queue.writeBuffer(this.buffers.gpuIndexBuffer.getBuffer(), indexOffset, indices.buffer, indexByteOffset, indexByteLength);
101
+ this.passEncoder.setVertexBuffer(0, this.buffers.gpuVertexBuffer.getBuffer(), vertexOffset, vertexByteLength);
102
+ this.passEncoder.setIndexBuffer(this.buffers.gpuIndexBuffer.getBuffer(), 'uint32', indexOffset, indexByteLength);
103
+ this.passEncoder.setPipeline(obj.getPipeline());
104
+ const shader = obj.getShader();
105
+ shader.writeBuffers(this.device, obj);
106
+ const bindGroups = obj.getShader().getBindGroups(this.device, obj);
107
+ for (let i = 0; i < bindGroups.length; i++) {
108
+ this.passEncoder.setBindGroup(i, bindGroups[i]);
109
+ }
110
+ const instances = obj.isInstance
111
+ ? obj.getInstanceCount()
112
+ : 1;
113
+ this.passEncoder.drawIndexed(indices.length, instances);
114
+ }
115
+ initShaders(shaders) {
116
+ if (!this.device)
117
+ throw logger.error('Device is null');
118
+ for (let i = 0; i < shaders.length; i++) {
119
+ shaders[i].init(this.device);
120
+ }
121
+ }
122
+ }
123
+ export class WebGLBackend extends SimJsBackend {
124
+ constructor() {
125
+ super('webgl');
126
+ }
127
+ }
@@ -0,0 +1,22 @@
1
+ import { SimulationElement3d } from '../graphics.js';
2
+ import { SimJSShader } from '../shaders/shader.js';
3
+ import { BackendType, GPUBuffers, SpecificBackendType, Vector2 } from '../types.js';
4
+ import { Color } from '../utils.js';
5
+ export declare abstract class SimJsBackend {
6
+ private type;
7
+ protected abstract buffers: GPUBuffers<unknown> | null;
8
+ protected clearColor: Color;
9
+ constructor(type: BackendType);
10
+ getBackendType(): BackendType;
11
+ abstract init(canvas: HTMLCanvasElement): Promise<void>;
12
+ abstract renderStart(canvas: HTMLCanvasElement): void;
13
+ abstract updateTextures(screenSize: Vector2): void;
14
+ abstract preRender(scene: SimulationElement3d[]): void;
15
+ abstract finishRender(): void;
16
+ abstract draw(obj: SimulationElement3d, vertexCallOffset: number, vertexCallBuffer: Float32Array, indexOffset: number, indices: Uint32Array): void;
17
+ abstract initShaders(shaders: SimJSShader[]): void;
18
+ abstract destroy(): void;
19
+ abstract onClearColorChange(): void;
20
+ setClearColor(color: Color): void;
21
+ as<T extends BackendType>(type: T): SpecificBackendType<T>;
22
+ }
@@ -0,0 +1,21 @@
1
+ import { logger } from '../globals.js';
2
+ import { color } from '../utils.js';
3
+ export class SimJsBackend {
4
+ type;
5
+ clearColor = color();
6
+ constructor(type) {
7
+ this.type = type;
8
+ }
9
+ getBackendType() {
10
+ return this.type;
11
+ }
12
+ setClearColor(color) {
13
+ this.clearColor = color;
14
+ this.onClearColorChange();
15
+ }
16
+ as(type) {
17
+ if (this.type !== type)
18
+ throw logger.error('Incompatible backend cast');
19
+ return this;
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ import { SimulationElement3d } from '../graphics.js';
2
+ import { SimJsBackend } from './backend.js';
3
+ import { SimJSShader } from '../shaders/shader.js';
4
+ import { GPUBuffers, Vector2 } from '../types.js';
5
+ export declare class WebGLBackend extends SimJsBackend {
6
+ private gl;
7
+ protected buffers: GPUBuffers<'webgl'> | null;
8
+ constructor();
9
+ init(canvas: HTMLCanvasElement): Promise<void>;
10
+ getContextOrError(): WebGL2RenderingContext;
11
+ renderStart(_canvas: HTMLCanvasElement): void;
12
+ updateTextures(screenSize: Vector2): void;
13
+ preRender(): void;
14
+ finishRender(): void;
15
+ initShaders(shaders: SimJSShader[]): void;
16
+ draw(obj: SimulationElement3d, vertexCallOffset: number, vertexCallBuffer: Float32Array, indexOffset: number, indices: Uint32Array): void;
17
+ destroy(): void;
18
+ onClearColorChange(): void;
19
+ }
@@ -0,0 +1,112 @@
1
+ import { logger } from '../globals.js';
2
+ import { SimJsBackend } from './backend.js';
3
+ import { WebGLMemoBuffer } from '../buffers/webgl.js';
4
+ export class WebGLBackend extends SimJsBackend {
5
+ gl = null;
6
+ buffers = null;
7
+ constructor() {
8
+ super('webgl');
9
+ console.log('new webgl backend');
10
+ }
11
+ async init(canvas) {
12
+ this.gl = canvas.getContext('webgl2');
13
+ if (this.gl === null) {
14
+ throw logger.error('WebGL init error');
15
+ }
16
+ this.buffers = {
17
+ gpuVertexCallBuffer: new WebGLMemoBuffer(this.gl, this.gl.ARRAY_BUFFER, this.gl.DYNAMIC_DRAW, 0),
18
+ gpuIndexBuffer: new WebGLMemoBuffer(this.gl, this.gl.ELEMENT_ARRAY_BUFFER, this.gl.DYNAMIC_DRAW, 0)
19
+ };
20
+ this.gl.viewport(0, 0, canvas.width, canvas.height);
21
+ const clearColor = this.clearColor.toObject();
22
+ this.gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
23
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
24
+ }
25
+ getContextOrError() {
26
+ if (!this.gl)
27
+ throw logger.error('Backend not initialized');
28
+ return this.gl;
29
+ }
30
+ renderStart(_canvas) {
31
+ if (!this.gl)
32
+ throw logger.error('Invalid render start state');
33
+ const gl = this.gl;
34
+ gl.enable(gl.DEPTH_TEST);
35
+ gl.depthFunc(gl.LESS);
36
+ gl.enable(gl.BLEND);
37
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
38
+ const clearColor = this.clearColor.toObject();
39
+ gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
40
+ gl.clearDepth(1.0);
41
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
42
+ }
43
+ updateTextures(screenSize) {
44
+ const gl = this.gl;
45
+ if (!gl)
46
+ throw logger.error('Invalid update texture state');
47
+ gl.viewport(0, 0, screenSize[0], screenSize[1]);
48
+ const clearColor = this.clearColor.toObject();
49
+ gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
50
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
51
+ }
52
+ preRender() {
53
+ const gl = this.gl;
54
+ if (!gl)
55
+ throw logger.error('Backend not initialized');
56
+ const clearColor = this.clearColor.toObject();
57
+ gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
58
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
59
+ }
60
+ finishRender() { }
61
+ initShaders(shaders) {
62
+ if (!this.gl)
63
+ throw logger.error('WebGL context is null');
64
+ for (let i = 0; i < shaders.length; i++) {
65
+ const shader = shaders[i];
66
+ if (shader.compatableWith('webgl')) {
67
+ shader.as('webgl').init(this.gl);
68
+ }
69
+ }
70
+ }
71
+ draw(obj, vertexCallOffset, vertexCallBuffer, indexOffset, indices) {
72
+ if (!this.gl || !this.buffers)
73
+ throw logger.error('Invalid draw state');
74
+ const gl = this.gl;
75
+ const shader = obj.getShader().as('webgl');
76
+ const shaderProgram = shader.getShaderProgram();
77
+ if (!shaderProgram)
78
+ throw logger.error('Null shader program');
79
+ const program = shader.getShaderProgram();
80
+ gl.useProgram(program);
81
+ this.buffers.gpuVertexCallBuffer.ensureCapacity(vertexCallBuffer.length);
82
+ shader.writeShaderProgramAttributes(this.buffers.gpuVertexCallBuffer, vertexCallOffset, vertexCallBuffer);
83
+ shader.writeUniformBuffers(obj);
84
+ this.buffers.gpuIndexBuffer.write(indices, indexOffset);
85
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers.gpuIndexBuffer.getBuffer());
86
+ const topology = obj.getGeometryTopology();
87
+ const wireframe = obj.isWireframe();
88
+ const mode = wireframe
89
+ ? gl.LINE_STRIP
90
+ : topology === 'list'
91
+ ? gl.TRIANGLES
92
+ : gl.TRIANGLE_STRIP;
93
+ const type = gl.UNSIGNED_INT;
94
+ const instances = obj.isInstance
95
+ ? obj.getInstanceCount()
96
+ : 1;
97
+ gl.drawElementsInstanced(mode, indices.length, type, indexOffset, instances);
98
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
99
+ }
100
+ destroy() {
101
+ if (!this.gl || !this.buffers)
102
+ return;
103
+ this.gl.deleteBuffer(this.buffers.gpuVertexCallBuffer);
104
+ this.gl.deleteBuffer(this.buffers.gpuIndexBuffer);
105
+ }
106
+ onClearColorChange() {
107
+ if (!this.gl)
108
+ return;
109
+ const clearColor = this.clearColor.toObject();
110
+ this.gl.clearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
111
+ }
112
+ }
@@ -0,0 +1,25 @@
1
+ import { SimulationElement3d } from '../graphics.js';
2
+ import { SimJSShader } from '../shaders/shader.js';
3
+ import { GPUBuffers, Vector2 } from '../types.js';
4
+ import { SimJsBackend } from './backend.js';
5
+ export declare class WebGPUBackend extends SimJsBackend {
6
+ private device;
7
+ private ctx;
8
+ private renderPassDescriptor;
9
+ private multisampleTexture;
10
+ private depthTexture;
11
+ private passEncoder;
12
+ private commandEncoder;
13
+ protected buffers: GPUBuffers<'webgpu'> | null;
14
+ constructor();
15
+ getDeviceOrError(): GPUDevice;
16
+ init(canvas: HTMLCanvasElement): Promise<void>;
17
+ renderStart(canvas: HTMLCanvasElement): void;
18
+ destroy(): void;
19
+ updateTextures(screenSize: Vector2): void;
20
+ preRender(scene: SimulationElement3d[]): void;
21
+ finishRender(): void;
22
+ draw(obj: SimulationElement3d, vertexCallOffset: number, vertexCallBuffer: Float32Array, indexOffset: number, indices: Uint32Array): void;
23
+ initShaders(shaders: SimJSShader[]): void;
24
+ onClearColorChange(): void;
25
+ }
@@ -0,0 +1,134 @@
1
+ import { WebGPUMemoBuffer } from '../buffers/webgpu.js';
2
+ import { logger } from '../globals.js';
3
+ import { buildDepthTexture, buildMultisampleTexture, getVertexAndIndexSize } from '../internalUtils.js';
4
+ import { SimJsBackend } from './backend.js';
5
+ export class WebGPUBackend extends SimJsBackend {
6
+ device = null;
7
+ ctx = null;
8
+ renderPassDescriptor = null;
9
+ multisampleTexture = null;
10
+ depthTexture = null;
11
+ passEncoder = null;
12
+ commandEncoder = null;
13
+ buffers = null;
14
+ constructor() {
15
+ super('webgpu');
16
+ }
17
+ getDeviceOrError() {
18
+ if (!this.device)
19
+ throw logger.error('Backend not initialized');
20
+ return this.device;
21
+ }
22
+ async init(canvas) {
23
+ const adapter = await navigator.gpu.requestAdapter();
24
+ if (!adapter)
25
+ throw logger.error('Adapter is null');
26
+ this.ctx = canvas.getContext('webgpu');
27
+ if (!this.ctx)
28
+ throw logger.error('Context is null');
29
+ this.device = await adapter.requestDevice();
30
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
31
+ this.ctx.configure({
32
+ device: this.device,
33
+ format: presentationFormat,
34
+ alphaMode: 'opaque'
35
+ });
36
+ this.buffers = {
37
+ gpuVertexCallBuffer: new WebGPUMemoBuffer(this.device, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, 0),
38
+ gpuIndexBuffer: new WebGPUMemoBuffer(this.device, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, 0)
39
+ };
40
+ }
41
+ renderStart(canvas) {
42
+ if (!this.device || !this.ctx)
43
+ throw logger.error('Invalid render start state');
44
+ const colorAttachment = {
45
+ // @ts-ignore
46
+ view: undefined, // Assigned later
47
+ clearValue: this.clearColor.toObject(),
48
+ loadOp: 'clear',
49
+ storeOp: 'store'
50
+ };
51
+ this.multisampleTexture = buildMultisampleTexture(this.device, this.ctx, canvas.width, canvas.height);
52
+ this.depthTexture = buildDepthTexture(this.device, canvas.width, canvas.height);
53
+ this.renderPassDescriptor = {
54
+ colorAttachments: [colorAttachment],
55
+ depthStencilAttachment: {
56
+ view: this.depthTexture.createView(),
57
+ depthClearValue: 1.0,
58
+ depthLoadOp: 'clear',
59
+ depthStoreOp: 'store'
60
+ }
61
+ };
62
+ }
63
+ destroy() {
64
+ this.device?.destroy();
65
+ this.multisampleTexture?.destroy();
66
+ this.depthTexture?.destroy();
67
+ this.buffers?.gpuVertexCallBuffer.destroy();
68
+ this.buffers?.gpuIndexBuffer.destroy();
69
+ }
70
+ updateTextures(screenSize) {
71
+ if (!this.device || !this.ctx || !this.renderPassDescriptor) {
72
+ throw logger.error('Invalid update texture state');
73
+ }
74
+ this.multisampleTexture = buildMultisampleTexture(this.device, this.ctx, screenSize[0], screenSize[1]);
75
+ this.depthTexture = buildDepthTexture(this.device, screenSize[0], screenSize[1]);
76
+ this.renderPassDescriptor.depthStencilAttachment.view = this.depthTexture.createView();
77
+ }
78
+ preRender(scene) {
79
+ if (!this.renderPassDescriptor || !this.ctx || !this.multisampleTexture || !this.device) {
80
+ throw logger.error('Invalid prerender state');
81
+ }
82
+ const colorAttachments = this.renderPassDescriptor
83
+ .colorAttachments;
84
+ const attachment = colorAttachments[0];
85
+ attachment.view = this.multisampleTexture.createView();
86
+ attachment.resolveTarget = this.ctx.getCurrentTexture().createView();
87
+ this.commandEncoder = this.device.createCommandEncoder();
88
+ this.passEncoder = this.commandEncoder.beginRenderPass(this.renderPassDescriptor);
89
+ const [totalVerticesSize, totalIndexSize] = getVertexAndIndexSize(scene);
90
+ this.buffers.gpuVertexCallBuffer.ensureCapacity(totalVerticesSize * 4);
91
+ this.buffers.gpuIndexBuffer.ensureCapacity(totalIndexSize * 4);
92
+ }
93
+ finishRender() {
94
+ this.passEncoder.end();
95
+ this.device.queue.submit([this.commandEncoder.finish()]);
96
+ }
97
+ draw(obj, vertexCallOffset, vertexCallBuffer, indexOffset, indices) {
98
+ if (!this.device || !this.buffers || !this.passEncoder) {
99
+ throw logger.error('Invalid draw state');
100
+ }
101
+ this.device.queue.writeBuffer(this.buffers.gpuVertexCallBuffer.getBuffer(), vertexCallOffset, vertexCallBuffer.buffer, vertexCallBuffer.byteOffset, vertexCallBuffer.byteLength);
102
+ this.device.queue.writeBuffer(this.buffers.gpuIndexBuffer.getBuffer(), indexOffset, indices.buffer, indices.byteOffset, indices.byteLength);
103
+ this.passEncoder.setVertexBuffer(0, this.buffers.gpuVertexCallBuffer.getBuffer(), vertexCallOffset, vertexCallBuffer.byteLength);
104
+ this.passEncoder.setIndexBuffer(this.buffers.gpuIndexBuffer.getBuffer(), 'uint32', indexOffset, indices.byteLength);
105
+ this.passEncoder.setPipeline(obj.getPipeline());
106
+ const shader = obj.getShader().as('webgpu');
107
+ shader.writeUniformBuffers(obj);
108
+ const bindGroups = shader.getBindGroups(this.device, obj);
109
+ for (let i = 0; i < bindGroups.length; i++) {
110
+ this.passEncoder.setBindGroup(i, bindGroups[i]);
111
+ }
112
+ const instances = obj.isInstance
113
+ ? obj.getInstanceCount()
114
+ : 1;
115
+ this.passEncoder.drawIndexed(indices.length, instances);
116
+ }
117
+ initShaders(shaders) {
118
+ if (!this.device)
119
+ throw logger.error('WebGPU device is null');
120
+ for (let i = 0; i < shaders.length; i++) {
121
+ const shader = shaders[i];
122
+ if (shader.compatableWith('webgpu')) {
123
+ shader.as('webgpu').init(this.device);
124
+ }
125
+ }
126
+ }
127
+ onClearColorChange() {
128
+ if (!this.renderPassDescriptor)
129
+ return;
130
+ const colorAttachments = this.renderPassDescriptor
131
+ .colorAttachments;
132
+ colorAttachments[0].clearValue = this.clearColor.toObject();
133
+ }
134
+ }
@@ -0,0 +1,15 @@
1
+ import { ArrayTypes, BackendType, BufferFromBackendType, SpecificMemoBufferType } from '../types.js';
2
+ export declare abstract class MemoBuffer {
3
+ protected abstract buffer: BufferFromBackendType<unknown> | null;
4
+ protected bufferCapacity: number;
5
+ private backendType;
6
+ constructor(backendType: BackendType, initCapacity: number);
7
+ abstract allocBuffer(): void;
8
+ abstract destroy(): void;
9
+ write(_buf: ArrayTypes, _offset?: number): void;
10
+ getBuffer(): {};
11
+ private growCapacity;
12
+ ensureCapacity(capacity: number): void;
13
+ setCapacityPrecise(capacity: number): void;
14
+ as<T extends BackendType>(type: T): SpecificMemoBufferType<T>;
15
+ }
@@ -0,0 +1,42 @@
1
+ import { logger } from '../globals.js';
2
+ export class MemoBuffer {
3
+ bufferCapacity;
4
+ backendType;
5
+ constructor(backendType, initCapacity) {
6
+ this.backendType = backendType;
7
+ this.bufferCapacity = initCapacity;
8
+ }
9
+ // cant be abstract because offset should be default param to 0
10
+ // which it wont allow (even though it could)
11
+ write(_buf, _offset = 0) { }
12
+ getBuffer() {
13
+ if (!this.buffer)
14
+ this.allocBuffer();
15
+ return this.buffer;
16
+ }
17
+ growCapacity(current, target) {
18
+ let res = Math.max(1, current);
19
+ while (res < target) {
20
+ res += Math.ceil(res / 2);
21
+ }
22
+ return res;
23
+ }
24
+ ensureCapacity(capacity) {
25
+ this.setCapacityPrecise(this.growCapacity(this.bufferCapacity, capacity));
26
+ }
27
+ setCapacityPrecise(capacity) {
28
+ if (!this.buffer) {
29
+ this.bufferCapacity = capacity;
30
+ this.allocBuffer();
31
+ }
32
+ if (capacity <= this.bufferCapacity)
33
+ return;
34
+ this.bufferCapacity = capacity;
35
+ this.allocBuffer();
36
+ }
37
+ as(type) {
38
+ if (type !== this.backendType)
39
+ throw logger.error('Incompatible memo buffer cast');
40
+ return this;
41
+ }
42
+ }
@@ -0,0 +1,13 @@
1
+ import { ArrayTypes } from '../types.js';
2
+ import { MemoBuffer } from './buffer.js';
3
+ export declare class WebGLMemoBuffer extends MemoBuffer {
4
+ private gl;
5
+ protected buffer: WebGLBuffer | null;
6
+ private target;
7
+ private usage;
8
+ constructor(gl: WebGL2RenderingContext, target: GLenum, usage: GLenum, initCapacity: number);
9
+ allocBuffer(): void;
10
+ destroy(): void;
11
+ getBuffer(): WebGLBuffer;
12
+ write(buf: ArrayTypes, offset?: number): void;
13
+ }
@@ -0,0 +1,46 @@
1
+ import { logger } from '../globals.js';
2
+ import { MemoBuffer } from './buffer.js';
3
+ export class WebGLMemoBuffer extends MemoBuffer {
4
+ gl;
5
+ buffer = null;
6
+ target;
7
+ usage;
8
+ constructor(gl, target, usage, initCapacity) {
9
+ super('webgl', initCapacity);
10
+ this.gl = gl;
11
+ this.target = target;
12
+ this.usage = usage;
13
+ }
14
+ allocBuffer() {
15
+ const gl = this.gl;
16
+ if (this.buffer)
17
+ gl.deleteBuffer(this.buffer);
18
+ this.buffer = gl.createBuffer();
19
+ if (!this.buffer)
20
+ throw logger.error('WebGLMemoBuffer init error');
21
+ gl.bindBuffer(this.target, this.buffer);
22
+ gl.bufferData(this.target, this.bufferCapacity, this.usage);
23
+ gl.bindBuffer(this.target, null);
24
+ }
25
+ destroy() {
26
+ if (this.buffer) {
27
+ this.gl.deleteBuffer(this.buffer);
28
+ this.buffer = null;
29
+ }
30
+ }
31
+ getBuffer() {
32
+ if (!this.buffer)
33
+ this.allocBuffer();
34
+ return this.buffer;
35
+ }
36
+ write(buf, offset = 0) {
37
+ const neededSize = offset + buf.byteLength;
38
+ if (!this.buffer || neededSize > this.bufferCapacity) {
39
+ this.bufferCapacity = neededSize;
40
+ this.allocBuffer();
41
+ }
42
+ this.gl.bindBuffer(this.target, this.buffer);
43
+ this.gl.bufferSubData(this.target, offset, buf);
44
+ this.gl.bindBuffer(this.target, null);
45
+ }
46
+ }
@@ -0,0 +1,12 @@
1
+ import { ArrayTypes } from '../types.js';
2
+ import { MemoBuffer } from './buffer.js';
3
+ export declare class WebGPUMemoBuffer extends MemoBuffer {
4
+ protected device: GPUDevice;
5
+ protected buffer: GPUBuffer | null;
6
+ private usage;
7
+ constructor(device: GPUDevice, usage: GPUBufferDescriptor['usage'], initCapacity: number);
8
+ allocBuffer(): void;
9
+ getBuffer(): GPUBuffer;
10
+ write(buf: ArrayTypes, offset?: number): void;
11
+ destroy(): void;
12
+ }