p3nGu1nZz commited on
Commit
9905bd0
·
1 Parent(s): d178651

state to state

Browse files
Files changed (3) hide show
  1. index.js +49 -63
  2. wgpu-state.js +29 -28
  3. wgpu-texture.js +13 -15
index.js CHANGED
@@ -4,8 +4,8 @@ import { createState } from './wgpu-state.js';
4
  import { generateGlyphTextureAtlas, createTextureFromSource } from './wgpu-utility.js';
5
  import { createPipeline } from './wgpu-pipeline.js';
6
  import { fetchShaderCode } from './wgpu-shader.js';
7
- import { GenerateVertexDataAndTexture } from './wgpu-texture.js'; // Import the function
8
- import { generateGlyphVerticesForText } from './wgpu-text.js'; // Corrected import statement
9
  import { config } from './wgpu-config.js';
10
  import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './wgpu-constants.js';
11
 
@@ -14,140 +14,126 @@ const canvas = document.querySelector('canvas');
14
 
15
  // State initialization
16
  const state = createState(config);
17
- state.canvas = canvas; // Move canvas to state
18
 
19
- const FIXED_DELTA_TIME = 1 / 60; // Fixed time step of 60 FPS for physics and game logic
20
- const MAX_FRAME_TIME = 0.25; // Maximum time to simulate per frame to prevent spiral of death
21
- const TARGET_FPS = 60; // Target frames per second
22
- const FRAME_DURATION = 1000 / TARGET_FPS; // Duration of a single frame in milliseconds
23
 
24
- state.fixedDeltaTime = FIXED_DELTA_TIME;
25
- state.maxFrameTime = MAX_FRAME_TIME;
26
- state.targetFps = TARGET_FPS;
27
- state.frameDuration = FRAME_DURATION;
 
 
28
 
29
  async function Main() {
30
  const adapter = await navigator.gpu?.requestAdapter();
31
- const { device, context, presentationFormat } = await initializeWebGPU(navigator, adapter, state.canvas); // Use state.canvas
32
  if (!device) return;
33
 
34
- state.device = device;
 
 
35
 
36
  // Initialize Resources
37
- await InitializeResources(presentationFormat);
38
 
39
  // Start the game loop
40
- GameLoop(context);
41
  }
42
 
43
- // Initialize shaders, pipeline, textures, and buffers
44
- async function InitializeResources(presentationFormat) {
45
- // Load shader code
46
  const shaderCode = await fetchShaderCode('shaders.wgsl');
47
  const vertexSize = config.floatsPerVertex * 4;
48
 
49
- // Create rendering pipeline
50
- state.pipeline = await createPipeline(state.device, presentationFormat, vertexSize, shaderCode);
51
-
52
- // Generate glyph texture atlas
53
  const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
54
  document.body.appendChild(glyphCanvas);
55
  glyphCanvas.style.backgroundColor = '#222';
56
 
57
- // Create vertex and index buffers
58
  CreateBuffers();
59
 
60
- // Generate vertex buffer data and texture
61
  GenerateVertexDataAndTexture(state, glyphCanvas, generateGlyphVerticesForText, COLORS, config, createTextureFromSource);
62
  }
63
 
64
- // Function to create vertex and index buffers
65
  function CreateBuffers() {
66
  const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * config.floatsPerVertex * 4;
67
- state.vertexBuffer = state.device.createBuffer({
68
  label: 'vertices',
69
  size: vertexBufferSize,
70
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
71
  });
72
 
73
- state.indexBuffer = state.device.createBuffer({
74
  label: 'indices',
75
  size: config.maxGlyphs * config.vertsPerGlyph * 4,
76
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
77
  });
78
 
79
  const indices = GenerateIndices(config.maxGlyphs);
80
- state.device.queue.writeBuffer(state.indexBuffer, 0, new Uint32Array(indices));
81
  }
82
 
83
- // Function to generate indices for glyphs
84
  function GenerateIndices(maxGlyphs) {
85
- // Generate index array for glyphs
86
  return Array.from({ length: maxGlyphs * 6 }, (_, i) => {
87
  const ndx = Math.floor(i / 6) * 4;
88
  return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
89
  });
90
  }
91
 
92
- // Game loop function
93
- function GameLoop(context) {
94
  let lastTime = performance.now();
95
- state.accumulator = 0;
96
 
97
  function Tick() {
98
  const currentTime = performance.now();
99
  const frameTime = (currentTime - lastTime) / 1000;
100
  lastTime = currentTime;
101
 
102
- const deltaTime = Math.min(frameTime, state.maxFrameTime);
103
- state.accumulator += deltaTime;
104
 
105
- // Fixed time step updates for game logic
106
- while (state.accumulator >= state.fixedDeltaTime) {
107
- FixedUpdate(state.fixedDeltaTime);
108
- state.accumulator -= state.fixedDeltaTime;
109
  }
110
 
111
- // Variable time step update for rendering
112
- const alpha = state.accumulator / state.fixedDeltaTime;
113
- Render(alpha, context);
114
 
115
- // Schedule the next frame update
116
- setTimeout(Tick, state.frameDuration);
117
  }
118
 
119
  Tick();
120
  }
121
 
122
- // Fixed update function for game logic
123
  function FixedUpdate(deltaTime) {
124
- state.time += deltaTime;
125
- // Perform game logic updates here, such as physics and AI
126
  }
127
 
128
- // Render function
129
- function Render(alpha, context) {
130
- // Set up projection and view matrices
131
  const fov = 60 * Math.PI / 180;
132
- const aspect = state.canvas.clientWidth / state.canvas.clientHeight; // Use state.canvas
133
  const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
134
  const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
135
  const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
136
- RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = context.getCurrentTexture().createView();
137
 
138
- const encoder = state.device.createCommandEncoder();
139
  const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
140
- pass.setPipeline(state.pipeline);
141
- mat4.rotateY(viewProjectionMatrix, state.time, state.matrix);
142
- mat4.translate(state.matrix, [-state.width / 2, -state.height / 2, 0], state.matrix);
143
- state.device.queue.writeBuffer(state.uniformBuffer, 0, state.uniformValues);
144
- pass.setBindGroup(0, state.bindGroup);
145
- pass.setVertexBuffer(0, state.vertexBuffer);
146
- pass.setIndexBuffer(state.indexBuffer, 'uint32');
147
- pass.drawIndexed(state.numGlyphs * 6);
148
  pass.end();
149
- state.device.queue.submit([encoder.finish()]);
150
  }
151
 
152
- // Initialize application
153
  Main();
 
4
  import { generateGlyphTextureAtlas, createTextureFromSource } from './wgpu-utility.js';
5
  import { createPipeline } from './wgpu-pipeline.js';
6
  import { fetchShaderCode } from './wgpu-shader.js';
7
+ import { GenerateVertexDataAndTexture } from './wgpu-texture.js';
8
+ import { generateGlyphVerticesForText } from './wgpu-text.js';
9
  import { config } from './wgpu-config.js';
10
  import { CANVAS, CTX, COLORS, RENDER_PASS_DESCRIPTOR } from './wgpu-constants.js';
11
 
 
14
 
15
  // State initialization
16
  const state = createState(config);
17
+ state.canvas = canvas;
18
 
19
+ const FIXED_DELTA_TIME = 1 / 60;
20
+ const MAX_FRAME_TIME = 0.25;
21
+ const TARGET_FPS = 60;
22
+ const FRAME_DURATION = 1000 / TARGET_FPS;
23
 
24
+ // Ensure timing object exists within state
25
+ state.timing = state.timing || {};
26
+ state.timing.fixedDeltaTime = FIXED_DELTA_TIME;
27
+ state.timing.maxFrameTime = MAX_FRAME_TIME;
28
+ state.timing.targetFps = TARGET_FPS;
29
+ state.timing.frameDuration = FRAME_DURATION;
30
 
31
  async function Main() {
32
  const adapter = await navigator.gpu?.requestAdapter();
33
+ const { device, context, presentationFormat } = await initializeWebGPU(navigator, adapter, state.canvas);
34
  if (!device) return;
35
 
36
+ state.webgpu.device = device;
37
+ state.webgpu.context = context;
38
+ state.webgpu.presentationFormat = presentationFormat;
39
 
40
  // Initialize Resources
41
+ await InitializeResources();
42
 
43
  // Start the game loop
44
+ GameLoop();
45
  }
46
 
47
+ async function InitializeResources() {
 
 
48
  const shaderCode = await fetchShaderCode('shaders.wgsl');
49
  const vertexSize = config.floatsPerVertex * 4;
50
 
51
+ state.webgpu.pipeline = await createPipeline(state.webgpu.device, state.webgpu.presentationFormat, vertexSize, shaderCode);
52
+
 
 
53
  const glyphCanvas = generateGlyphTextureAtlas(CANVAS, CTX, config);
54
  document.body.appendChild(glyphCanvas);
55
  glyphCanvas.style.backgroundColor = '#222';
56
 
 
57
  CreateBuffers();
58
 
 
59
  GenerateVertexDataAndTexture(state, glyphCanvas, generateGlyphVerticesForText, COLORS, config, createTextureFromSource);
60
  }
61
 
 
62
  function CreateBuffers() {
63
  const vertexBufferSize = config.maxGlyphs * config.vertsPerGlyph * config.floatsPerVertex * 4;
64
+ state.webgpu.vertexBuffer = state.webgpu.device.createBuffer({
65
  label: 'vertices',
66
  size: vertexBufferSize,
67
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
68
  });
69
 
70
+ state.webgpu.indexBuffer = state.webgpu.device.createBuffer({
71
  label: 'indices',
72
  size: config.maxGlyphs * config.vertsPerGlyph * 4,
73
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
74
  });
75
 
76
  const indices = GenerateIndices(config.maxGlyphs);
77
+ state.webgpu.device.queue.writeBuffer(state.webgpu.indexBuffer, 0, new Uint32Array(indices));
78
  }
79
 
 
80
  function GenerateIndices(maxGlyphs) {
 
81
  return Array.from({ length: maxGlyphs * 6 }, (_, i) => {
82
  const ndx = Math.floor(i / 6) * 4;
83
  return (i % 6 < 3 ? [ndx, ndx + 1, ndx + 2] : [ndx + 2, ndx + 1, ndx + 3])[i % 3];
84
  });
85
  }
86
 
87
+ function GameLoop() {
 
88
  let lastTime = performance.now();
89
+ state.timing.accumulator = 0;
90
 
91
  function Tick() {
92
  const currentTime = performance.now();
93
  const frameTime = (currentTime - lastTime) / 1000;
94
  lastTime = currentTime;
95
 
96
+ const deltaTime = Math.min(frameTime, state.timing.maxFrameTime);
97
+ state.timing.accumulator += deltaTime;
98
 
99
+ while (state.timing.accumulator >= state.timing.fixedDeltaTime) {
100
+ FixedUpdate(state.timing.fixedDeltaTime);
101
+ state.timing.accumulator -= state.timing.fixedDeltaTime;
 
102
  }
103
 
104
+ const alpha = state.timing.accumulator / state.timing.fixedDeltaTime;
105
+ Render(alpha);
 
106
 
107
+ setTimeout(Tick, state.timing.frameDuration);
 
108
  }
109
 
110
  Tick();
111
  }
112
 
 
113
  function FixedUpdate(deltaTime) {
114
+ state.timing.time += deltaTime;
 
115
  }
116
 
117
+ function Render(alpha) {
 
 
118
  const fov = 60 * Math.PI / 180;
119
+ const aspect = state.canvas.clientWidth / state.canvas.clientHeight;
120
  const projectionMatrix = mat4.perspective(fov, aspect, config.render.zNear, config.render.zFar);
121
  const viewMatrix = mat4.lookAt([0, 0, 5], [0, 0, 0], [0, 1, 0]);
122
  const viewProjectionMatrix = mat4.multiply(projectionMatrix, viewMatrix);
123
+ RENDER_PASS_DESCRIPTOR.colorAttachments[0].view = state.webgpu.context.getCurrentTexture().createView();
124
 
125
+ const encoder = state.webgpu.device.createCommandEncoder();
126
  const pass = encoder.beginRenderPass(RENDER_PASS_DESCRIPTOR);
127
+ pass.setPipeline(state.webgpu.pipeline);
128
+ mat4.rotateY(viewProjectionMatrix, state.timing.time, state.matrices.matrix);
129
+ mat4.translate(state.matrices.matrix, [-state.glyphs.width / 2, -state.glyphs.height / 2, 0], state.matrices.matrix);
130
+ state.webgpu.device.queue.writeBuffer(state.webgpu.uniformBuffer, 0, state.matrices.uniformValues);
131
+ pass.setBindGroup(0, state.webgpu.bindGroup);
132
+ pass.setVertexBuffer(0, state.webgpu.vertexBuffer);
133
+ pass.setIndexBuffer(state.webgpu.indexBuffer, 'uint32');
134
+ pass.drawIndexed(state.glyphs.numGlyphs * 6);
135
  pass.end();
136
+ state.webgpu.device.queue.submit([encoder.finish()]);
137
  }
138
 
 
139
  Main();
wgpu-state.js CHANGED
@@ -1,33 +1,34 @@
1
  export function createState(config) {
2
  return {
3
- // WebGPU resources
4
- device: null,
5
- pipeline: null,
6
- vertexBuffer: null,
7
- indexBuffer: null,
8
- uniformBuffer: null,
9
- texture: null,
10
- sampler: null,
11
- bindGroup: null,
12
-
13
- // Uniform values and matrices
14
- uniformValues: new Float32Array(config.uniformBufferSize / 4),
15
- matrix: null,
16
-
17
- // Glyph details
18
- numGlyphs: 0,
19
- width: 0,
20
- height: 0,
21
-
22
- // Canvas and context
 
23
  canvas: null,
24
-
25
- // Timing and animation
26
- time: 0,
27
- fixedDeltaTime: 1 / 60,
28
- maxFrameTime: 0.25,
29
- targetFps: 60,
30
- frameDuration: 1000 / 60,
31
- accumulator: 0,
32
  };
33
  }
 
1
  export function createState(config) {
2
  return {
3
+ webgpu: {
4
+ device: null,
5
+ pipeline: null,
6
+ vertexBuffer: null,
7
+ indexBuffer: null,
8
+ uniformBuffer: null,
9
+ texture: null,
10
+ sampler: null,
11
+ bindGroup: null,
12
+ context: null,
13
+ presentationFormat: null,
14
+ },
15
+ matrices: {
16
+ uniformValues: new Float32Array(config.uniformBufferSize / 4),
17
+ matrix: null,
18
+ },
19
+ glyphs: {
20
+ numGlyphs: 0,
21
+ width: 0,
22
+ height: 0,
23
+ },
24
  canvas: null,
25
+ timing: {
26
+ time: 0,
27
+ fixedDeltaTime: 1 / 60,
28
+ maxFrameTime: 0.25,
29
+ targetFps: 60,
30
+ frameDuration: 1000 / 60,
31
+ accumulator: 0,
32
+ }
33
  };
34
  }
wgpu-texture.js CHANGED
@@ -1,34 +1,32 @@
1
- // wgpu-texture.js
2
-
3
  export function GenerateVertexDataAndTexture(state, glyphCanvas, generateGlyphVerticesForText, COLORS, config, createTextureFromSource) {
4
  const glyphData = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, config, glyphCanvas);
5
- state.device.queue.writeBuffer(state.vertexBuffer, 0, glyphData.vertexData);
6
 
7
- state.texture = createTextureFromSource(state.device, glyphCanvas, { mips: true });
8
- state.sampler = state.device.createSampler({
9
  minFilter: 'linear',
10
  magFilter: 'linear',
11
  });
12
 
13
- state.uniformBuffer = state.device.createBuffer({
14
  label: 'uniforms for quad',
15
  size: config.uniformBufferSize,
16
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
17
  });
18
 
19
- state.matrix = state.uniformValues.subarray(0, 16);
20
 
21
- state.bindGroup = state.device.createBindGroup({
22
- layout: state.pipeline.getBindGroupLayout(0),
23
  entries: [
24
- { binding: 0, resource: state.sampler },
25
- { binding: 1, resource: state.texture.createView() },
26
- { binding: 2, resource: { buffer: state.uniformBuffer } },
27
  ],
28
  });
29
 
30
  // Update state with glyph details
31
- state.numGlyphs = glyphData.numGlyphs;
32
- state.width = glyphData.width;
33
- state.height = glyphData.height;
34
  }
 
 
 
1
  export function GenerateVertexDataAndTexture(state, glyphCanvas, generateGlyphVerticesForText, COLORS, config, createTextureFromSource) {
2
  const glyphData = generateGlyphVerticesForText('Hello\nworld!\nText in\nWebGPU!', COLORS, config, glyphCanvas);
3
+ state.webgpu.device.queue.writeBuffer(state.webgpu.vertexBuffer, 0, glyphData.vertexData);
4
 
5
+ state.webgpu.texture = createTextureFromSource(state.webgpu.device, glyphCanvas, { mips: true });
6
+ state.webgpu.sampler = state.webgpu.device.createSampler({
7
  minFilter: 'linear',
8
  magFilter: 'linear',
9
  });
10
 
11
+ state.webgpu.uniformBuffer = state.webgpu.device.createBuffer({
12
  label: 'uniforms for quad',
13
  size: config.uniformBufferSize,
14
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
15
  });
16
 
17
+ state.matrices.matrix = state.matrices.uniformValues.subarray(0, 16);
18
 
19
+ state.webgpu.bindGroup = state.webgpu.device.createBindGroup({
20
+ layout: state.webgpu.pipeline.getBindGroupLayout(0),
21
  entries: [
22
+ { binding: 0, resource: state.webgpu.sampler },
23
+ { binding: 1, resource: state.webgpu.texture.createView() },
24
+ { binding: 2, resource: { buffer: state.webgpu.uniformBuffer } },
25
  ],
26
  });
27
 
28
  // Update state with glyph details
29
+ state.glyphs.numGlyphs = glyphData.numGlyphs;
30
+ state.glyphs.width = glyphData.width;
31
+ state.glyphs.height = glyphData.height;
32
  }