@footgun/cobalt 0.8.0 → 0.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@footgun/cobalt",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "main": "bundle.js",
6
6
  "description": "A 2D WebGpu renderer",
package/src/cobalt.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { default as createTexture } from './create-texture.js'
2
2
  export { default as createTextureFromBuffer } from './create-texture-from-buffer.js'
3
3
  export { default as createTextureFromUrl } from './create-texture-from-url.js'
4
+ export { default as getPreferredFormat } from './get-preferred-format.js'
4
5
 
5
6
  // built-in run nodes
6
7
  import bloomNode from './bloom/bloom.js'
@@ -1,50 +1,27 @@
1
- /// <reference types="@webgpu/types"/>
2
-
3
- import { DisplacementParametersBuffer } from "./displacement-parameters-buffer";
4
- import compositionWGSL from "./composition.wgsl"
5
-
6
- type Parameters = {
7
- readonly device: GPUDevice;
8
- readonly targetFormat: GPUTextureFormat;
9
-
10
- readonly colorTextureView: GPUTextureView;
11
- readonly noiseMapTextureView: GPUTextureView;
12
- readonly displacementTextureView: GPUTextureView;
13
-
14
- readonly displacementParametersBuffer: DisplacementParametersBuffer;
15
- };
1
+ import compositionWGSL from "./composition.wgsl";
16
2
 
17
3
  class DisplacementComposition {
18
- private readonly device: GPUDevice;
19
- private readonly targetFormat: GPUTextureFormat;
20
- private readonly renderPipeline: GPURenderPipeline;
21
-
22
- private readonly colorSampler: GPUSampler;
23
- private readonly noiseSampler: GPUSampler;
24
-
25
- private readonly displacementParametersBuffer: DisplacementParametersBuffer;
26
-
27
- private renderBundle: GPURenderBundle | null = null;
28
-
29
- private colorTextureView: GPUTextureView;
30
- private noiseMapTextureView: GPUTextureView;
31
- private displacementTextureView: GPUTextureView;
32
-
33
- public constructor(params: Parameters) {
4
+ device;
5
+ targetFormat;
6
+ renderPipeline;
7
+ colorSampler;
8
+ noiseSampler;
9
+ displacementParametersBuffer;
10
+ renderBundle = null;
11
+ colorTextureView;
12
+ noiseMapTextureView;
13
+ displacementTextureView;
14
+ constructor(params) {
34
15
  this.device = params.device;
35
-
36
16
  this.targetFormat = params.targetFormat;
37
17
  this.colorTextureView = params.colorTextureView;
38
18
  this.noiseMapTextureView = params.noiseMapTextureView;
39
19
  this.displacementTextureView = params.displacementTextureView;
40
-
41
20
  this.displacementParametersBuffer = params.displacementParametersBuffer;
42
-
43
21
  const shaderModule = this.device.createShaderModule({
44
22
  label: "DisplacementComposition shader module",
45
23
  code: compositionWGSL,
46
24
  });
47
-
48
25
  this.renderPipeline = this.device.createRenderPipeline({
49
26
  label: "DisplacementComposition renderpipeline",
50
27
  layout: "auto",
@@ -56,15 +33,14 @@ class DisplacementComposition {
56
33
  module: shaderModule,
57
34
  entryPoint: "main_fragment",
58
35
  targets: [{
59
- format: params.targetFormat,
60
- }],
36
+ format: params.targetFormat,
37
+ }],
61
38
  },
62
39
  primitive: {
63
40
  cullMode: "none",
64
41
  topology: "triangle-strip",
65
42
  },
66
43
  });
67
-
68
44
  this.noiseSampler = this.device.createSampler({
69
45
  label: "DisplacementComposition noisesampler",
70
46
  addressModeU: "repeat",
@@ -74,7 +50,6 @@ class DisplacementComposition {
74
50
  minFilter: "linear",
75
51
  mipmapFilter: "linear",
76
52
  });
77
-
78
53
  this.colorSampler = this.device.createSampler({
79
54
  label: "DisplacementComposition colorSampler",
80
55
  addressModeU: "clamp-to-edge",
@@ -85,34 +60,28 @@ class DisplacementComposition {
85
60
  mipmapFilter: "linear",
86
61
  });
87
62
  }
88
-
89
- public getRenderBundle(): GPURenderBundle {
63
+ getRenderBundle() {
90
64
  if (!this.renderBundle) {
91
65
  this.renderBundle = this.buildRenderBundle();
92
66
  }
93
67
  return this.renderBundle;
94
68
  }
95
-
96
- public destroy(): void {
69
+ destroy() {
97
70
  // nothing to do
98
71
  }
99
-
100
- public setColorTextureView(textureView: GPUTextureView): void {
72
+ setColorTextureView(textureView) {
101
73
  this.colorTextureView = textureView;
102
74
  this.renderBundle = null;
103
75
  }
104
-
105
- public setNoiseMapTextureView(textureView: GPUTextureView): void {
76
+ setNoiseMapTextureView(textureView) {
106
77
  this.noiseMapTextureView = textureView;
107
78
  this.renderBundle = null;
108
79
  }
109
-
110
- public setDisplacementTextureView(textureView: GPUTextureView): void {
80
+ setDisplacementTextureView(textureView) {
111
81
  this.displacementTextureView = textureView;
112
82
  this.renderBundle = null;
113
83
  }
114
-
115
- private buildRenderBundle(): GPURenderBundle {
84
+ buildRenderBundle() {
116
85
  const bindgroup = this.device.createBindGroup({
117
86
  label: "DisplacementComposition bindgroup 0",
118
87
  layout: this.renderPipeline.getBindGroupLayout(0),
@@ -143,7 +112,6 @@ class DisplacementComposition {
143
112
  },
144
113
  ],
145
114
  });
146
-
147
115
  const renderBundleEncoder = this.device.createRenderBundleEncoder({
148
116
  label: "DisplacementComposition renderbundle encoder",
149
117
  colorFormats: [this.targetFormat],
@@ -154,8 +122,4 @@ class DisplacementComposition {
154
122
  return renderBundleEncoder.finish({ label: "DisplacementComposition renderbundle" });
155
123
  }
156
124
  }
157
-
158
- export {
159
- DisplacementComposition
160
- };
161
-
125
+ export { DisplacementComposition };
@@ -0,0 +1,21 @@
1
+ class DisplacementParametersBuffer {
2
+ device;
3
+ bufferGpu;
4
+ needsUpdate = true;
5
+ constructor(params) {
6
+ this.device = params.device;
7
+ this.bufferGpu = this.device.createBuffer({
8
+ label: "DisplacementParametersBuffer buffer",
9
+ size: 16,
10
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
11
+ });
12
+ this.setParameters(params.initialParameters);
13
+ }
14
+ setParameters(params) {
15
+ this.device.queue.writeBuffer(this.bufferGpu, 0, new Float32Array([params.offsetX, params.offsetY, params.scale]));
16
+ }
17
+ destroy() {
18
+ this.bufferGpu.destroy();
19
+ }
20
+ }
21
+ export { DisplacementParametersBuffer };
@@ -1,63 +1,27 @@
1
- /// <reference types="@webgpu/types"/>
2
-
3
- import displacementWGSL from './displacement.wgsl'
4
- import * as wgpuMatrix from "wgpu-matrix";
5
- import { TrianglesBuffer } from "./triangles-buffer";
6
-
7
-
8
- type Viewport = {
9
- readonly width: number;
10
- readonly height: number;
11
- readonly zoom: number;
12
- readonly position: [number, number];
13
- };
14
-
15
- type Parameters = {
16
- readonly device: GPUDevice;
17
-
18
- readonly width: number;
19
- readonly height: number;
20
-
21
- readonly blurFactor: number;
22
-
23
- readonly trianglesBuffer: TrianglesBuffer;
24
- };
25
-
26
- type TextureWithView = {
27
- readonly texture: GPUTexture;
28
- readonly view: GPUTextureView;
29
- };
1
+ import displacementWGSL from './displacement.wgsl';
2
+ import * as wgpuMatrix from "wgpu-matrix";
30
3
 
31
4
  class DisplacementTexture {
32
- private readonly device: GPUDevice;
33
- private readonly format: GPUTextureFormat = "r8unorm";
34
- private readonly downsizeFactor: number;
35
- private readonly multisample: number;
36
-
37
- private textureSimple: TextureWithView;
38
- private textureMultisampled: TextureWithView | null = null;
39
-
40
- private readonly renderPipeline: GPURenderPipeline;
41
- private readonly bindgroup: GPUBindGroup;
42
- private readonly uniformsBuffer: GPUBuffer;
43
-
44
- private readonly trianglesBuffer: TrianglesBuffer;
45
-
46
- public constructor(params: Parameters) {
5
+ device;
6
+ format = "r8unorm";
7
+ downsizeFactor;
8
+ multisample;
9
+ textureSimple;
10
+ textureMultisampled = null;
11
+ renderPipeline;
12
+ bindgroup;
13
+ uniformsBuffer;
14
+ trianglesBuffer;
15
+ constructor(params) {
47
16
  this.device = params.device;
48
17
  this.downsizeFactor = params.blurFactor;
49
18
  this.multisample = this.downsizeFactor > 1 ? 4 : 1;
50
-
51
19
  [this.textureSimple, this.textureMultisampled] = this.createTextures(params.width, params.height);
52
-
53
20
  this.trianglesBuffer = params.trianglesBuffer;
54
-
55
21
  const shaderModule = this.device.createShaderModule({
56
22
  label: "DisplacementTexture shader module",
57
23
  code: displacementWGSL,
58
-
59
24
  });
60
-
61
25
  this.renderPipeline = this.device.createRenderPipeline({
62
26
  label: "DisplacementTexture renderpipeline",
63
27
  layout: "auto",
@@ -82,8 +46,8 @@ class DisplacementTexture {
82
46
  module: shaderModule,
83
47
  entryPoint: "main_fragment",
84
48
  targets: [{
85
- format: this.format,
86
- }],
49
+ format: this.format,
50
+ }],
87
51
  },
88
52
  primitive: {
89
53
  cullMode: "none",
@@ -93,13 +57,11 @@ class DisplacementTexture {
93
57
  count: this.multisample,
94
58
  },
95
59
  });
96
-
97
60
  this.uniformsBuffer = this.device.createBuffer({
98
61
  label: "DisplacementTexture uniforms buffer",
99
62
  size: 64,
100
63
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
101
64
  });
102
-
103
65
  this.bindgroup = this.device.createBindGroup({
104
66
  label: "DisplacementTexture bindgroup",
105
67
  layout: this.renderPipeline.getBindGroupLayout(0),
@@ -111,11 +73,9 @@ class DisplacementTexture {
111
73
  ],
112
74
  });
113
75
  }
114
-
115
- public update(commandEncoder: GPUCommandEncoder): void {
76
+ update(commandEncoder) {
116
77
  const targetTexture = this.textureMultisampled ?? this.textureSimple;
117
-
118
- const textureRenderpassColorAttachment: GPURenderPassColorAttachment = {
78
+ const textureRenderpassColorAttachment = {
119
79
  view: targetTexture.view,
120
80
  clearValue: [0, 0, 0, 1],
121
81
  loadOp: "clear",
@@ -124,12 +84,10 @@ class DisplacementTexture {
124
84
  if (this.textureMultisampled) {
125
85
  textureRenderpassColorAttachment.resolveTarget = this.textureSimple.view;
126
86
  }
127
-
128
87
  const renderpassEncoder = commandEncoder.beginRenderPass({
129
88
  label: "DisplacementTexture render to texture renderpass",
130
89
  colorAttachments: [textureRenderpassColorAttachment],
131
90
  });
132
-
133
91
  const [textureWidth, textureHeight] = [targetTexture.texture.width, targetTexture.texture.height];
134
92
  renderpassEncoder.setViewport(0, 0, textureWidth, textureHeight, 0, 1);
135
93
  renderpassEncoder.setScissorRect(0, 0, textureWidth, textureHeight);
@@ -138,50 +96,40 @@ class DisplacementTexture {
138
96
  renderpassEncoder.setVertexBuffer(0, this.trianglesBuffer.bufferGpu);
139
97
  renderpassEncoder.draw(3 * this.trianglesBuffer.spriteCount);
140
98
  renderpassEncoder.end();
141
- };
142
-
143
- public resize(width: number, height: number): void {
99
+ }
100
+ ;
101
+ resize(width, height) {
144
102
  this.textureSimple.texture.destroy();
145
103
  this.textureMultisampled?.texture.destroy();
146
-
147
104
  [this.textureSimple, this.textureMultisampled] = this.createTextures(width, height);
148
105
  }
149
-
150
- public setViewport(viewport: Viewport): void {
106
+ setViewport(viewport) {
151
107
  const scaling = [1, 1, 1];
152
108
  const rotation = 0;
153
109
  const translation = [1, 1, 0];
154
-
155
110
  const modelMatrix = wgpuMatrix.mat4.identity();
156
111
  wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.scaling(scaling), modelMatrix, modelMatrix);
157
112
  wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.rotationZ(rotation), modelMatrix, modelMatrix);
158
113
  wgpuMatrix.mat4.multiply(wgpuMatrix.mat4.translation(translation), modelMatrix, modelMatrix);
159
-
160
114
  const viewMatrix = wgpuMatrix.mat4.translation([-viewport.position[0], -viewport.position[1], 0]);
161
-
162
- const gameWidth = viewport.width / viewport.zoom
163
- const gameHeight = viewport.height / viewport.zoom
115
+ const gameWidth = viewport.width / viewport.zoom;
116
+ const gameHeight = viewport.height / viewport.zoom;
164
117
  // left right bottom top near far
165
- const projectionMatrix = wgpuMatrix.mat4.ortho(0, gameWidth, gameHeight, 0, -10.0, 10.0)
166
-
118
+ const projectionMatrix = wgpuMatrix.mat4.ortho(0, gameWidth, gameHeight, 0, -10.0, 10.0);
167
119
  const mvpMatrix = wgpuMatrix.mat4.identity();
168
120
  wgpuMatrix.mat4.multiply(viewMatrix, modelMatrix, mvpMatrix);
169
121
  wgpuMatrix.mat4.multiply(projectionMatrix, mvpMatrix, mvpMatrix);
170
-
171
122
  this.device.queue.writeBuffer(this.uniformsBuffer, 0, mvpMatrix);
172
123
  }
173
-
174
- public getView(): GPUTextureView {
124
+ getView() {
175
125
  return this.textureSimple.view;
176
126
  }
177
-
178
- public destroy(): void {
127
+ destroy() {
179
128
  this.textureSimple.texture.destroy();
180
129
  this.textureMultisampled?.texture.destroy();
181
130
  this.uniformsBuffer.destroy();
182
131
  }
183
-
184
- private createTextures(width: number, height: number): [TextureWithView, TextureWithView | null] {
132
+ createTextures(width, height) {
185
133
  const texture = this.device.createTexture({
186
134
  label: "DisplacementTexture texture",
187
135
  size: [
@@ -195,8 +143,7 @@ class DisplacementTexture {
195
143
  texture,
196
144
  view: texture.createView({ label: "DisplacementTexture texture view" }),
197
145
  };
198
-
199
- let textureMultisampled: TextureWithView | null = null;
146
+ let textureMultisampled = null;
200
147
  if (this.multisample > 1) {
201
148
  const textureMulti = this.device.createTexture({
202
149
  label: "DisplacementTexture texture multisampled",
@@ -210,12 +157,7 @@ class DisplacementTexture {
210
157
  view: textureMulti.createView({ label: "DisplacementTexture texture multisampled view" }),
211
158
  };
212
159
  }
213
-
214
160
  return [textureSimple, textureMultisampled];
215
161
  }
216
162
  }
217
-
218
- export {
219
- DisplacementTexture
220
- };
221
-
163
+ export { DisplacementTexture };
@@ -1,74 +1,53 @@
1
- /// <reference types="@webgpu/types"/>
2
-
3
- import uuid from '../uuid.js'
4
-
5
- type Parameters = {
6
- readonly device: GPUDevice;
7
- readonly maxSpriteCount: number;
8
- };
9
-
10
- type Point = [number, number];
11
- type TriangleVertices = [Point, Point, Point];
12
- type TriangleData = [number, number, number, number, number, number];
1
+ import uuid from '../uuid.js';
13
2
 
14
3
  class TrianglesBuffer {
15
- private readonly device: GPUDevice;
16
-
17
- private readonly floatsPerSprite = 6; // vec2(translate) + vec2(scale) + rotation + opacity
18
- public readonly bufferGpu: GPUBuffer;
19
- private bufferNeedsUpdate: boolean = false;
20
-
21
- private readonly sprites: Map<number, TriangleData> = new Map();
22
- public get spriteCount(): number {
4
+ device;
5
+ floatsPerSprite = 6; // vec2(translate) + vec2(scale) + rotation + opacity
6
+ bufferGpu;
7
+ bufferNeedsUpdate = false;
8
+ sprites = new Map();
9
+ get spriteCount() {
23
10
  return this.sprites.size;
24
11
  }
25
-
26
- public constructor(params: Parameters) {
12
+ constructor(params) {
27
13
  this.device = params.device;
28
-
29
14
  this.bufferGpu = this.device.createBuffer({
30
15
  size: params.maxSpriteCount * this.floatsPerSprite * Float32Array.BYTES_PER_ELEMENT,
31
16
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
32
17
  });
33
18
  }
34
-
35
- public destroy(): void {
19
+ destroy() {
36
20
  this.bufferGpu.destroy;
37
21
  }
38
-
39
- public update(): void {
22
+ update() {
40
23
  if (this.bufferNeedsUpdate) {
41
- const bufferData: number[] = [];
24
+ const bufferData = [];
42
25
  for (const sprite of this.sprites.values()) {
43
26
  bufferData.push(...sprite);
44
- };
27
+ }
28
+ ;
45
29
  const buffer = new Float32Array(bufferData);
46
30
  this.device.queue.writeBuffer(this.bufferGpu, 0, buffer);
47
31
  }
48
32
  }
49
-
50
- public addTriangle(triangleVertices: TriangleVertices): number {
33
+ addTriangle(triangleVertices) {
51
34
  const triangleId = uuid();
52
35
  if (this.sprites.has(triangleId)) {
53
36
  throw new Error(`Duplicate triangle "${triangleId}".`);
54
37
  }
55
-
56
38
  const triangleData = this.buildTriangleData(triangleVertices);
57
39
  this.sprites.set(triangleId, triangleData);
58
40
  this.bufferNeedsUpdate = true;
59
-
60
41
  return triangleId;
61
42
  }
62
-
63
- public removeTriangle(triangleId: number): void {
43
+ removeTriangle(triangleId) {
64
44
  if (!this.sprites.has(triangleId)) {
65
45
  throw new Error(`Unknown triangle "${triangleId}".`);
66
46
  }
67
47
  this.sprites.delete(triangleId);
68
48
  this.bufferNeedsUpdate = true;
69
49
  }
70
-
71
- public setTriangle(triangleId: number, triangleVertices: TriangleVertices): void {
50
+ setTriangle(triangleId, triangleVertices) {
72
51
  if (!this.sprites.has(triangleId)) {
73
52
  throw new Error(`Unknown triangle "${triangleId}".`);
74
53
  }
@@ -76,8 +55,7 @@ class TrianglesBuffer {
76
55
  this.sprites.set(triangleId, triangleData);
77
56
  this.bufferNeedsUpdate = true;
78
57
  }
79
-
80
- private buildTriangleData(triangleVertices: TriangleVertices): TriangleData {
58
+ buildTriangleData(triangleVertices) {
81
59
  return [
82
60
  triangleVertices[0][0],
83
61
  triangleVertices[0][1],
@@ -88,8 +66,4 @@ class TrianglesBuffer {
88
66
  ];
89
67
  }
90
68
  }
91
-
92
- export {
93
- TrianglesBuffer
94
- };
95
-
69
+ export { TrianglesBuffer };
@@ -1,9 +1,5 @@
1
- /// <reference types="@webgpu/types"/>
2
-
3
- import { type Light } from "./types";
4
-
5
1
  class LightsBuffer {
6
- public static readonly structs = {
2
+ static structs = {
7
3
  definition: `
8
4
  struct Light { // align(16) size(48)
9
5
  color: vec3<f32>, // offset(0) align(16) size(12)
@@ -28,24 +24,16 @@ struct LightsBuffer { // align(16)
28
24
  lights: { offset: 16, stride: 48 },
29
25
  },
30
26
  };
31
-
32
- private readonly device: GPUDevice;
33
-
34
- public readonly maxLightsCount: number;
35
- private currentLightsCount: number = 0;
36
-
37
- private readonly buffer: {
38
- readonly bufferCpu: ArrayBuffer;
39
- readonly bufferGpu: GPUBuffer;
40
- };
41
- public get gpuBuffer(): GPUBuffer {
27
+ device;
28
+ maxLightsCount;
29
+ currentLightsCount = 0;
30
+ buffer;
31
+ get gpuBuffer() {
42
32
  return this.buffer.bufferGpu;
43
33
  }
44
-
45
- public constructor(device: GPUDevice, maxLightsCount: number) {
34
+ constructor(device, maxLightsCount) {
46
35
  this.device = device;
47
36
  this.maxLightsCount = maxLightsCount;
48
-
49
37
  const bufferCpu = new ArrayBuffer(LightsBuffer.computeBufferBytesLength(maxLightsCount));
50
38
  const bufferGpu = device.createBuffer({
51
39
  label: "LightsBuffer buffer",
@@ -53,19 +41,15 @@ struct LightsBuffer { // align(16)
53
41
  usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.VERTEX,
54
42
  });
55
43
  this.buffer = { bufferCpu, bufferGpu };
56
-
57
44
  this.setLights([]);
58
45
  }
59
-
60
- public setLights(lights: ReadonlyArray<Light>): void {
46
+ setLights(lights) {
61
47
  if (lights.length > this.maxLightsCount) {
62
48
  throw new Error(`Too many lights "${lights.length}", max is "${this.maxLightsCount}".`);
63
49
  }
64
-
65
50
  const newBufferLength = LightsBuffer.computeBufferBytesLength(lights.length);
66
51
  new Uint32Array(this.buffer.bufferCpu, 0, 1).set([lights.length]);
67
-
68
- lights.forEach((light: Light, index: number) => {
52
+ lights.forEach((light, index) => {
69
53
  new Float32Array(this.buffer.bufferCpu, LightsBuffer.structs.lightsBuffer.lights.offset + LightsBuffer.structs.lightsBuffer.lights.stride * index, 9).set([
70
54
  ...light.color,
71
55
  light.radius,
@@ -75,24 +59,17 @@ struct LightsBuffer { // align(16)
75
59
  light.attenuationExp
76
60
  ]);
77
61
  });
78
-
79
62
  this.device.queue.writeBuffer(this.buffer.bufferGpu, 0, this.buffer.bufferCpu, 0, newBufferLength);
80
63
  this.currentLightsCount = lights.length;
81
64
  }
82
-
83
- public get lightsCount(): number {
65
+ get lightsCount() {
84
66
  return this.currentLightsCount;
85
67
  }
86
-
87
- public destroy(): void {
68
+ destroy() {
88
69
  this.buffer.bufferGpu.destroy();
89
70
  }
90
-
91
- private static computeBufferBytesLength(lightsCount: number): number {
71
+ static computeBufferBytesLength(lightsCount) {
92
72
  return LightsBuffer.structs.lightsBuffer.lights.offset + LightsBuffer.structs.lightsBuffer.lights.stride * lightsCount;
93
73
  }
94
74
  }
95
-
96
- export {
97
- LightsBuffer
98
- };
75
+ export { LightsBuffer };