@nodetool-ai/game-renderer 0.8.1 → 0.8.2
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 +46 -0
- package/dist/.nodetool-build-stamp +1 -1
- package/dist/audio/mixer.d.ts +75 -0
- package/dist/audio/mixer.d.ts.map +1 -0
- package/dist/audio/mixer.js +345 -0
- package/dist/audio/mixer.js.map +1 -0
- package/dist/audio/player.d.ts +67 -0
- package/dist/audio/player.d.ts.map +1 -0
- package/dist/audio/player.js +337 -0
- package/dist/audio/player.js.map +1 -0
- package/dist/audio/spatial.d.ts +50 -0
- package/dist/audio/spatial.d.ts.map +1 -0
- package/dist/audio/spatial.js +166 -0
- package/dist/audio/spatial.js.map +1 -0
- package/dist/audio/voices.d.ts +11 -0
- package/dist/audio/voices.d.ts.map +1 -0
- package/dist/audio/voices.js +22 -0
- package/dist/audio/voices.js.map +1 -0
- package/dist/audio.d.ts +6 -38
- package/dist/audio.d.ts.map +1 -1
- package/dist/audio.js +3 -226
- package/dist/audio.js.map +1 -1
- package/dist/browser.d.ts +2 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +5 -5
- package/dist/browser.js.map +1 -1
- package/dist/browser3d.d.ts +2 -0
- package/dist/browser3d.d.ts.map +1 -0
- package/dist/browser3d.js +2 -0
- package/dist/browser3d.js.map +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +29 -19
- package/dist/build.js.map +1 -1
- package/dist/build3d.d.ts +11 -0
- package/dist/build3d.d.ts.map +1 -0
- package/dist/build3d.js +126 -0
- package/dist/build3d.js.map +1 -0
- package/dist/canvas2d.d.ts +12 -3
- package/dist/canvas2d.d.ts.map +1 -1
- package/dist/canvas2d.js +59 -12
- package/dist/canvas2d.js.map +1 -1
- package/dist/fixed-tick-host.d.ts +10 -0
- package/dist/fixed-tick-host.d.ts.map +1 -0
- package/dist/fixed-tick-host.js +29 -0
- package/dist/fixed-tick-host.js.map +1 -0
- package/dist/frame-budget.d.ts +34 -0
- package/dist/frame-budget.d.ts.map +1 -0
- package/dist/frame-budget.js +52 -0
- package/dist/frame-budget.js.map +1 -0
- package/dist/frame.d.ts +20 -1
- package/dist/frame.d.ts.map +1 -1
- package/dist/frame.js +69 -1
- package/dist/frame.js.map +1 -1
- package/dist/game3d-capture-page.js +5834 -0
- package/dist/game3d-player.js +6001 -0
- package/dist/imageDiff.d.ts +9 -0
- package/dist/imageDiff.d.ts.map +1 -0
- package/dist/imageDiff.js +30 -0
- package/dist/imageDiff.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/input-bindings.d.ts +76 -0
- package/dist/input-bindings.d.ts.map +1 -0
- package/dist/input-bindings.js +377 -0
- package/dist/input-bindings.js.map +1 -0
- package/dist/input.d.ts +1 -1
- package/dist/input.d.ts.map +1 -1
- package/dist/input.js +1 -14
- package/dist/input.js.map +1 -1
- package/dist/input3d.d.ts +3 -0
- package/dist/input3d.d.ts.map +1 -0
- package/dist/input3d.js +3 -0
- package/dist/input3d.js.map +1 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +28 -11
- package/dist/node.js.map +1 -1
- package/dist/node3d.d.ts +29 -0
- package/dist/node3d.d.ts.map +1 -0
- package/dist/node3d.js +160 -0
- package/dist/node3d.js.map +1 -0
- package/dist/particles/curves.d.ts +16 -0
- package/dist/particles/curves.d.ts.map +1 -0
- package/dist/particles/curves.js +49 -0
- package/dist/particles/curves.js.map +1 -0
- package/dist/particles/index.d.ts +6 -0
- package/dist/particles/index.d.ts.map +1 -0
- package/dist/particles/index.js +6 -0
- package/dist/particles/index.js.map +1 -0
- package/dist/particles/random.d.ts +10 -0
- package/dist/particles/random.d.ts.map +1 -0
- package/dist/particles/random.js +26 -0
- package/dist/particles/random.js.map +1 -0
- package/dist/particles/render2d.d.ts +40 -0
- package/dist/particles/render2d.d.ts.map +1 -0
- package/dist/particles/render2d.js +70 -0
- package/dist/particles/render2d.js.map +1 -0
- package/dist/particles/shapes.d.ts +18 -0
- package/dist/particles/shapes.d.ts.map +1 -0
- package/dist/particles/shapes.js +92 -0
- package/dist/particles/shapes.js.map +1 -0
- package/dist/particles/simulator.d.ts +88 -0
- package/dist/particles/simulator.d.ts.map +1 -0
- package/dist/particles/simulator.js +371 -0
- package/dist/particles/simulator.js.map +1 -0
- package/dist/player-v1.js +2502 -0
- package/dist/preparation3d.d.ts +3 -0
- package/dist/preparation3d.d.ts.map +1 -0
- package/dist/preparation3d.js +3 -0
- package/dist/preparation3d.js.map +1 -0
- package/dist/renderPipeline.d.ts +12 -0
- package/dist/renderPipeline.d.ts.map +1 -0
- package/dist/renderPipeline.js +17 -0
- package/dist/renderPipeline.js.map +1 -0
- package/dist/renderer3d/animation/index.d.ts +11 -0
- package/dist/renderer3d/animation/index.d.ts.map +1 -0
- package/dist/renderer3d/animation/index.js +212 -0
- package/dist/renderer3d/animation/index.js.map +1 -0
- package/dist/renderer3d/assets/cache.d.ts +14 -0
- package/dist/renderer3d/assets/cache.d.ts.map +1 -0
- package/dist/renderer3d/assets/cache.js +74 -0
- package/dist/renderer3d/assets/cache.js.map +1 -0
- package/dist/renderer3d/assets/dispose.d.ts +4 -0
- package/dist/renderer3d/assets/dispose.d.ts.map +1 -0
- package/dist/renderer3d/assets/dispose.js +41 -0
- package/dist/renderer3d/assets/dispose.js.map +1 -0
- package/dist/renderer3d/assets/hdri-contract.d.ts +23 -0
- package/dist/renderer3d/assets/hdri-contract.d.ts.map +1 -0
- package/dist/renderer3d/assets/hdri-contract.js +2 -0
- package/dist/renderer3d/assets/hdri-contract.js.map +1 -0
- package/dist/renderer3d/assets/instances.d.ts +5 -0
- package/dist/renderer3d/assets/instances.d.ts.map +1 -0
- package/dist/renderer3d/assets/instances.js +86 -0
- package/dist/renderer3d/assets/instances.js.map +1 -0
- package/dist/renderer3d/capture-driver-types.d.ts +42 -0
- package/dist/renderer3d/capture-driver-types.d.ts.map +1 -0
- package/dist/renderer3d/capture-driver-types.js +2 -0
- package/dist/renderer3d/capture-driver-types.js.map +1 -0
- package/dist/renderer3d/capture-page.d.ts +36 -0
- package/dist/renderer3d/capture-page.d.ts.map +1 -0
- package/dist/renderer3d/capture-page.js +68 -0
- package/dist/renderer3d/capture-page.js.map +1 -0
- package/dist/renderer3d/culling.d.ts +10 -0
- package/dist/renderer3d/culling.d.ts.map +1 -0
- package/dist/renderer3d/culling.js +27 -0
- package/dist/renderer3d/culling.js.map +1 -0
- package/dist/renderer3d/environment/index.d.ts +4 -0
- package/dist/renderer3d/environment/index.d.ts.map +1 -0
- package/dist/renderer3d/environment/index.js +9 -0
- package/dist/renderer3d/environment/index.js.map +1 -0
- package/dist/renderer3d/environment/sky.d.ts +45 -0
- package/dist/renderer3d/environment/sky.d.ts.map +1 -0
- package/dist/renderer3d/environment/sky.js +221 -0
- package/dist/renderer3d/environment/sky.js.map +1 -0
- package/dist/renderer3d/hud/fonts.d.ts +13 -0
- package/dist/renderer3d/hud/fonts.d.ts.map +1 -0
- package/dist/renderer3d/hud/fonts.js +69 -0
- package/dist/renderer3d/hud/fonts.js.map +1 -0
- package/dist/renderer3d/hud/paint.d.ts +4 -0
- package/dist/renderer3d/hud/paint.d.ts.map +1 -0
- package/dist/renderer3d/hud/paint.js +22 -0
- package/dist/renderer3d/hud/paint.js.map +1 -0
- package/dist/renderer3d/import.d.ts +34 -0
- package/dist/renderer3d/import.d.ts.map +1 -0
- package/dist/renderer3d/import.js +408 -0
- package/dist/renderer3d/import.js.map +1 -0
- package/dist/renderer3d/index.d.ts +74 -0
- package/dist/renderer3d/index.d.ts.map +1 -0
- package/dist/renderer3d/index.js +338 -0
- package/dist/renderer3d/index.js.map +1 -0
- package/dist/renderer3d/lights/index.d.ts +7 -0
- package/dist/renderer3d/lights/index.d.ts.map +1 -0
- package/dist/renderer3d/lights/index.js +67 -0
- package/dist/renderer3d/lights/index.js.map +1 -0
- package/dist/renderer3d/materials/index.d.ts +4 -0
- package/dist/renderer3d/materials/index.d.ts.map +1 -0
- package/dist/renderer3d/materials/index.js +8 -0
- package/dist/renderer3d/materials/index.js.map +1 -0
- package/dist/renderer3d/passes/post-processing.d.ts +44 -0
- package/dist/renderer3d/passes/post-processing.d.ts.map +1 -0
- package/dist/renderer3d/passes/post-processing.js +213 -0
- package/dist/renderer3d/passes/post-processing.js.map +1 -0
- package/dist/renderer3d/pipeline.d.ts +15 -0
- package/dist/renderer3d/pipeline.d.ts.map +1 -0
- package/dist/renderer3d/pipeline.js +10 -0
- package/dist/renderer3d/pipeline.js.map +1 -0
- package/dist/renderer3d/playwright-contract.d.ts +3 -0
- package/dist/renderer3d/playwright-contract.d.ts.map +1 -0
- package/dist/renderer3d/playwright-contract.js +4 -0
- package/dist/renderer3d/playwright-contract.js.map +1 -0
- package/dist/renderer3d/preparation.d.ts +79 -0
- package/dist/renderer3d/preparation.d.ts.map +1 -0
- package/dist/renderer3d/preparation.js +458 -0
- package/dist/renderer3d/preparation.js.map +1 -0
- package/dist/renderer3d/scene-sync.d.ts +8 -0
- package/dist/renderer3d/scene-sync.d.ts.map +1 -0
- package/dist/renderer3d/scene-sync.js +96 -0
- package/dist/renderer3d/scene-sync.js.map +1 -0
- package/dist/renderer3d/shadows/cascades.d.ts +40 -0
- package/dist/renderer3d/shadows/cascades.d.ts.map +1 -0
- package/dist/renderer3d/shadows/cascades.js +244 -0
- package/dist/renderer3d/shadows/cascades.js.map +1 -0
- package/dist/renderer3d/shadows/index.d.ts +44 -0
- package/dist/renderer3d/shadows/index.d.ts.map +1 -0
- package/dist/renderer3d/shadows/index.js +129 -0
- package/dist/renderer3d/shadows/index.js.map +1 -0
- package/dist/renderer3d/types.d.ts +18 -0
- package/dist/renderer3d/types.d.ts.map +1 -0
- package/dist/renderer3d/types.js +2 -0
- package/dist/renderer3d/types.js.map +1 -0
- package/dist/smoke.d.ts +15 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +138 -0
- package/dist/smoke.js.map +1 -0
- package/dist/standalone-player.d.ts +11 -1
- package/dist/standalone-player.d.ts.map +1 -1
- package/dist/standalone-player.js +67 -41
- package/dist/standalone-player.js.map +1 -1
- package/dist/standalone-player3d.d.ts +14 -0
- package/dist/standalone-player3d.d.ts.map +1 -0
- package/dist/standalone-player3d.js +255 -0
- package/dist/standalone-player3d.js.map +1 -0
- package/dist/touch-controls.d.ts +22 -8
- package/dist/touch-controls.d.ts.map +1 -1
- package/dist/touch-controls.js +88 -21
- package/dist/touch-controls.js.map +1 -1
- package/dist/ui/controller.d.ts +59 -0
- package/dist/ui/controller.d.ts.map +1 -0
- package/dist/ui/controller.js +201 -0
- package/dist/ui/controller.js.map +1 -0
- package/dist/ui/hud2d.d.ts +11 -0
- package/dist/ui/hud2d.d.ts.map +1 -0
- package/dist/ui/hud2d.js +25 -0
- package/dist/ui/hud2d.js.map +1 -0
- package/dist/ui/layout.d.ts +42 -0
- package/dist/ui/layout.d.ts.map +1 -0
- package/dist/ui/layout.js +182 -0
- package/dist/ui/layout.js.map +1 -0
- package/dist/ui/paint.d.ts +45 -0
- package/dist/ui/paint.d.ts.map +1 -0
- package/dist/ui/paint.js +121 -0
- package/dist/ui/paint.js.map +1 -0
- package/dist/webgpu/effects.d.ts +22 -0
- package/dist/webgpu/effects.d.ts.map +1 -0
- package/dist/webgpu/effects.js +69 -0
- package/dist/webgpu/effects.js.map +1 -0
- package/dist/webgpu/hud.d.ts +4 -0
- package/dist/webgpu/hud.d.ts.map +1 -0
- package/dist/webgpu/hud.js +27 -0
- package/dist/webgpu/hud.js.map +1 -0
- package/dist/webgpu/lighting.d.ts +4 -0
- package/dist/webgpu/lighting.d.ts.map +1 -0
- package/dist/webgpu/lighting.js +47 -0
- package/dist/webgpu/lighting.js.map +1 -0
- package/dist/webgpu/pipeline.d.ts +11 -0
- package/dist/webgpu/pipeline.d.ts.map +1 -0
- package/dist/webgpu/pipeline.js +11 -0
- package/dist/webgpu/pipeline.js.map +1 -0
- package/dist/webgpu/sprites.d.ts +6 -0
- package/dist/webgpu/sprites.d.ts.map +1 -0
- package/dist/webgpu/sprites.js +74 -0
- package/dist/webgpu/sprites.js.map +1 -0
- package/dist/webgpu/types.d.ts +14 -0
- package/dist/webgpu/types.d.ts.map +1 -0
- package/dist/webgpu/types.js +2 -0
- package/dist/webgpu/types.js.map +1 -0
- package/dist/webgpu.d.ts +4 -3
- package/dist/webgpu.d.ts.map +1 -1
- package/dist/webgpu.js +119 -279
- package/dist/webgpu.js.map +1 -1
- package/package.json +47 -10
package/dist/webgpu.js
CHANGED
|
@@ -1,142 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { INSTANCE_FLOATS, SHADER, appendBatch, writeInstance } from "./webgpu/sprites.js";
|
|
2
|
+
import { LIGHT_SHADER, gameLightingUniforms } from "./webgpu/lighting.js";
|
|
3
|
+
import { PRESENT_SHADER, encodeGameEffects } from "./webgpu/effects.js";
|
|
4
|
+
import { HUD_SHADER, paintWebGPUHud } from "./webgpu/hud.js";
|
|
5
|
+
import { loadGameUiImages } from "./ui/hud2d.js";
|
|
6
|
+
import { WebGPURenderPipeline } from "./webgpu/pipeline.js";
|
|
7
|
+
import { createDefaultRegistry, createExecutor, createGPUContextFromDevice, createLabeledTexture, createRecipeRunner, LabeledTexture } from "@nodetool-ai/gpu/pool";
|
|
3
8
|
import { imageHeight, imageWidth } from "./canvas2d.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const SHADER = `
|
|
7
|
-
struct Camera { center: vec2f, viewport: vec2f, zoom: f32, padding0: f32, padding1: f32, padding2: f32 };
|
|
8
|
-
override linearizeInput: bool = false;
|
|
9
|
-
fn srgbToLinear(rgb: vec3f) -> vec3f {
|
|
10
|
-
return select(rgb / 12.92, pow((rgb + 0.055) / 1.055, vec3f(2.4)), rgb > vec3f(0.04045));
|
|
11
|
-
}
|
|
12
|
-
fn linearToSrgb(rgb: vec3f) -> vec3f {
|
|
13
|
-
return select(rgb * 12.92, 1.055 * pow(rgb, vec3f(1.0 / 2.4)) - 0.055, rgb > vec3f(0.0031308));
|
|
14
|
-
}
|
|
15
|
-
@group(0) @binding(0) var<uniform> camera: Camera;
|
|
16
|
-
@group(0) @binding(1) var atlasSampler: sampler;
|
|
17
|
-
@group(0) @binding(2) var atlas: texture_2d<f32>;
|
|
18
|
-
@group(1) @binding(0) var lightSampler: sampler;
|
|
19
|
-
@group(1) @binding(1) var lightTexture: texture_2d<f32>;
|
|
20
|
-
|
|
21
|
-
struct VertexInput {
|
|
22
|
-
@builtin(vertex_index) corner: u32,
|
|
23
|
-
@location(0) center: vec2f,
|
|
24
|
-
@location(1) size: vec2f,
|
|
25
|
-
@location(2) uv: vec4f,
|
|
26
|
-
@location(3) color: vec4f,
|
|
27
|
-
@location(4) rotation: f32,
|
|
28
|
-
@location(5) unlit: f32,
|
|
29
|
-
};
|
|
30
|
-
struct VertexOutput {
|
|
31
|
-
@builtin(position) position: vec4f,
|
|
32
|
-
@location(0) uv: vec2f,
|
|
33
|
-
@location(1) color: vec4f,
|
|
34
|
-
@location(3) @interpolate(flat) unlit: f32,
|
|
35
|
-
@location(4) lightUv: vec2f,
|
|
36
|
-
};
|
|
37
|
-
@vertex fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
38
|
-
let corners = array<vec2f, 6>(
|
|
39
|
-
vec2f(-0.5, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, 0.5),
|
|
40
|
-
vec2f(0.5, 0.5), vec2f(-0.5, -0.5), vec2f(0.5, -0.5));
|
|
41
|
-
let local = corners[input.corner] * input.size;
|
|
42
|
-
let sine = sin(input.rotation);
|
|
43
|
-
let cosine = cos(input.rotation);
|
|
44
|
-
let rotated = vec2f(local.x * cosine - local.y * sine, local.x * sine + local.y * cosine);
|
|
45
|
-
let world = input.center + rotated;
|
|
46
|
-
let clip = (world - camera.center) * camera.zoom * 2.0 / camera.viewport;
|
|
47
|
-
var output: VertexOutput;
|
|
48
|
-
output.position = vec4f(clip, 0.0, 1.0);
|
|
49
|
-
let uvCorner = corners[input.corner] + vec2f(0.5, 0.5);
|
|
50
|
-
output.uv = input.uv.xy + vec2f(uvCorner.x, 1.0 - uvCorner.y) * input.uv.zw;
|
|
51
|
-
output.color = input.color;
|
|
52
|
-
output.unlit = input.unlit;
|
|
53
|
-
output.lightUv = clip * vec2f(0.5, -0.5) + vec2f(0.5, 0.5);
|
|
54
|
-
return output;
|
|
55
|
-
}
|
|
56
|
-
@fragment fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
57
|
-
let sampled = textureSample(atlas, atlasSampler, input.uv) * input.color;
|
|
58
|
-
var straight = select(sampled.rgb, srgbToLinear(sampled.rgb), linearizeInput);
|
|
59
|
-
if (input.unlit < 0.5) {
|
|
60
|
-
let irradiance = textureSampleLevel(lightTexture, lightSampler, input.lightUv, 0.0).rgb;
|
|
61
|
-
let lit = clamp(srgbToLinear(sampled.rgb) * irradiance, vec3f(0.0), vec3f(1.0));
|
|
62
|
-
straight = select(linearToSrgb(lit), lit, linearizeInput);
|
|
63
|
-
}
|
|
64
|
-
return vec4f(straight * sampled.a, sampled.a);
|
|
65
|
-
}`;
|
|
66
|
-
const LIGHT_SHADER = `
|
|
67
|
-
struct Camera { center: vec2f, viewport: vec2f, zoom: f32, padding0: f32, padding1: f32, padding2: f32 };
|
|
68
|
-
struct PointLight { position: vec2f, radius: f32, intensity: f32, color: vec4f, falloff: f32, pad0: f32, pad1: f32, pad2: f32 };
|
|
69
|
-
struct Lighting { ambient: vec4f, info: vec4f, points: array<PointLight, 32> };
|
|
70
|
-
fn srgbToLinear(rgb: vec3f) -> vec3f {
|
|
71
|
-
return select(rgb / 12.92, pow((rgb + 0.055) / 1.055, vec3f(2.4)), rgb > vec3f(0.04045));
|
|
72
|
-
}
|
|
73
|
-
@group(0) @binding(0) var<uniform> camera: Camera;
|
|
74
|
-
@group(0) @binding(1) var<uniform> lighting: Lighting;
|
|
75
|
-
struct VertexOutput { @builtin(position) position: vec4f, @location(0) uv: vec2f };
|
|
76
|
-
@vertex fn vertexMain(@builtin(vertex_index) index: u32) -> VertexOutput {
|
|
77
|
-
let positions = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
|
|
78
|
-
var output: VertexOutput;
|
|
79
|
-
output.position = vec4f(positions[index], 0.0, 1.0);
|
|
80
|
-
output.uv = positions[index] * vec2f(0.5, -0.5) + vec2f(0.5, 0.5);
|
|
81
|
-
return output;
|
|
82
|
-
}
|
|
83
|
-
@fragment fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
84
|
-
let world = camera.center + (input.uv - vec2f(0.5, 0.5)) * camera.viewport / camera.zoom * vec2f(1.0, -1.0);
|
|
85
|
-
var irradiance = srgbToLinear(lighting.ambient.rgb) * lighting.ambient.a;
|
|
86
|
-
for (var i = 0u; i < 32u; i += 1u) {
|
|
87
|
-
if (i >= u32(lighting.info.x)) { break; }
|
|
88
|
-
let point = lighting.points[i];
|
|
89
|
-
let distance = length(world - point.position);
|
|
90
|
-
if (distance < point.radius) {
|
|
91
|
-
irradiance += srgbToLinear(point.color.rgb) * point.intensity * pow(1.0 - distance / point.radius, point.falloff);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return vec4f(irradiance, 1.0);
|
|
95
|
-
}`;
|
|
96
|
-
const PRESENT_SHADER = `
|
|
97
|
-
override decodeInput: bool = false;
|
|
98
|
-
override encodeOutput: bool = true;
|
|
99
|
-
override cropScaleX: f32 = 1.0;
|
|
100
|
-
override cropScaleY: f32 = 1.0;
|
|
101
|
-
override cropOffsetX: f32 = 0.0;
|
|
102
|
-
override cropOffsetY: f32 = 0.0;
|
|
103
|
-
@group(0) @binding(0) var sourceSampler: sampler;
|
|
104
|
-
@group(0) @binding(1) var sourceTexture: texture_2d<f32>;
|
|
105
|
-
struct VertexOutput { @builtin(position) position: vec4f, @location(0) uv: vec2f };
|
|
106
|
-
@vertex fn vertexMain(@builtin(vertex_index) index: u32) -> VertexOutput {
|
|
107
|
-
let positions = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
|
|
108
|
-
var output: VertexOutput;
|
|
109
|
-
output.position = vec4f(positions[index], 0.0, 1.0);
|
|
110
|
-
output.uv = positions[index] * vec2f(0.5, -0.5) + vec2f(0.5, 0.5);
|
|
111
|
-
return output;
|
|
112
|
-
}
|
|
113
|
-
@fragment fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
114
|
-
let uv = input.uv * vec2f(cropScaleX, cropScaleY) + vec2f(cropOffsetX, cropOffsetY);
|
|
115
|
-
let premul = textureSample(sourceTexture, sourceSampler, uv);
|
|
116
|
-
let straight = premul.rgb / max(premul.a, 1.0 / 255.0);
|
|
117
|
-
let linear = select(straight / 12.92, pow((straight + 0.055) / 1.055, vec3f(2.4)),
|
|
118
|
-
straight > vec3f(0.04045));
|
|
119
|
-
let encoded = select(straight * 12.92, 1.055 * pow(straight, vec3f(1.0 / 2.4)) - 0.055,
|
|
120
|
-
straight > vec3f(0.0031308));
|
|
121
|
-
var converted = select(straight, linear, decodeInput);
|
|
122
|
-
converted = select(converted, encoded, encodeOutput);
|
|
123
|
-
return vec4f(clamp(converted, vec3f(0.0), vec3f(1.0)) * premul.a, premul.a);
|
|
124
|
-
}`;
|
|
125
|
-
const HUD_SHADER = `
|
|
126
|
-
@group(0) @binding(0) var sourceSampler: sampler;
|
|
127
|
-
@group(0) @binding(1) var sourceTexture: texture_2d<f32>;
|
|
128
|
-
struct VertexOutput { @builtin(position) position: vec4f, @location(0) uv: vec2f };
|
|
129
|
-
@vertex fn vertexMain(@builtin(vertex_index) index: u32) -> VertexOutput {
|
|
130
|
-
let positions = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
|
|
131
|
-
var output: VertexOutput;
|
|
132
|
-
output.position = vec4f(positions[index], 0.0, 1.0);
|
|
133
|
-
output.uv = positions[index] * vec2f(0.5, -0.5) + vec2f(0.5, 0.5);
|
|
134
|
-
return output;
|
|
135
|
-
}
|
|
136
|
-
@fragment fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
|
|
137
|
-
let color = textureSample(sourceTexture, sourceSampler, input.uv);
|
|
138
|
-
return vec4f(color.rgb * color.a, color.a);
|
|
139
|
-
}`;
|
|
9
|
+
import { parseTint, projectedCamera, sourceRect, visibleItems } from "./frame.js";
|
|
10
|
+
import { PARTICLE_DOT_ASSET, PARTICLE_DOT_SIZE, particleDotPixels } from "./particles/render2d.js";
|
|
140
11
|
/** Draws ordered sprites as consecutive texture batches on a private WebGPU device. */
|
|
141
12
|
export class WebGPUGameRenderer {
|
|
142
13
|
canvas;
|
|
@@ -145,6 +16,7 @@ export class WebGPUGameRenderer {
|
|
|
145
16
|
format;
|
|
146
17
|
assets;
|
|
147
18
|
adapterType;
|
|
19
|
+
renderPipeline = new WebGPURenderPipeline();
|
|
148
20
|
backend = "webgpu";
|
|
149
21
|
pipeline;
|
|
150
22
|
effectSpritePipeline;
|
|
@@ -179,6 +51,7 @@ export class WebGPUGameRenderer {
|
|
|
179
51
|
textures = new Map();
|
|
180
52
|
validatedLuts = new Set();
|
|
181
53
|
fallback;
|
|
54
|
+
particleDot;
|
|
182
55
|
placeholders = new Map();
|
|
183
56
|
instanceBuffer;
|
|
184
57
|
instanceCapacity = 0;
|
|
@@ -297,6 +170,10 @@ export class WebGPUGameRenderer {
|
|
|
297
170
|
const fallbackTexture = device.createTexture({ size: [1, 1], format: "rgba8unorm", usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST });
|
|
298
171
|
device.queue.writeTexture({ texture: fallbackTexture }, new Uint8Array([255, 0, 255, 255]), { bytesPerRow: 4 }, [1, 1]);
|
|
299
172
|
this.fallback = this.textureEntry(fallbackTexture, 1, 1);
|
|
173
|
+
const dotTexture = device.createTexture({ size: [PARTICLE_DOT_SIZE, PARTICLE_DOT_SIZE], format: "rgba8unorm",
|
|
174
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST });
|
|
175
|
+
device.queue.writeTexture({ texture: dotTexture }, particleDotPixels(), { bytesPerRow: PARTICLE_DOT_SIZE * 4 }, [PARTICLE_DOT_SIZE, PARTICLE_DOT_SIZE]);
|
|
176
|
+
this.particleDot = this.textureEntry(dotTexture, PARTICLE_DOT_SIZE, PARTICLE_DOT_SIZE, "linear");
|
|
300
177
|
void device.lost.then(() => { this.lost = true; });
|
|
301
178
|
}
|
|
302
179
|
get capabilities() {
|
|
@@ -441,6 +318,9 @@ export class WebGPUGameRenderer {
|
|
|
441
318
|
return this.textureEntry(texture, width, height, sampling);
|
|
442
319
|
}
|
|
443
320
|
async getTexture(assetId, sampling) {
|
|
321
|
+
if (assetId === PARTICLE_DOT_ASSET) {
|
|
322
|
+
return { texture: this.particleDot, uploadedBytes: 0 };
|
|
323
|
+
}
|
|
444
324
|
const cached = this.textures.get(assetId);
|
|
445
325
|
if (cached) {
|
|
446
326
|
return { texture: cached, uploadedBytes: 0 };
|
|
@@ -498,14 +378,14 @@ export class WebGPUGameRenderer {
|
|
|
498
378
|
this.placeholders.set(assetId, entry);
|
|
499
379
|
return entry;
|
|
500
380
|
}
|
|
501
|
-
async render(frame, interpolation) {
|
|
381
|
+
async render(frame, interpolation, particles) {
|
|
502
382
|
if (this.disposed || this.lost) {
|
|
503
383
|
throw new Error(this.lost ? "WebGPU device was lost" : "Game renderer is disposed");
|
|
504
384
|
}
|
|
505
385
|
this.device.pushErrorScope("validation");
|
|
506
386
|
let popped = false;
|
|
507
387
|
try {
|
|
508
|
-
const stats = await this.renderFrame(frame, interpolation);
|
|
388
|
+
const stats = await this.renderFrame(frame, interpolation, particles);
|
|
509
389
|
const validationError = await this.device.popErrorScope();
|
|
510
390
|
popped = true;
|
|
511
391
|
if (validationError) {
|
|
@@ -521,16 +401,16 @@ export class WebGPUGameRenderer {
|
|
|
521
401
|
throw error;
|
|
522
402
|
}
|
|
523
403
|
}
|
|
524
|
-
async renderFrame(frame, interpolation) {
|
|
404
|
+
async renderFrame(frame, interpolation, particles) {
|
|
525
405
|
const bloomRadius = this.effects.reduce((maximum, effect) => effect.kind === "bloom" ? Math.max(maximum, effect.radius) : maximum, 0);
|
|
526
406
|
const overscanPixels = Math.ceil(bloomRadius * 2);
|
|
527
407
|
const overscanWorld = overscanPixels * frame.width / (Math.max(1, this.canvas.width) * projectedCamera(frame, interpolation).zoom);
|
|
528
|
-
const items = visibleItems(frame, interpolation, overscanWorld);
|
|
408
|
+
const items = visibleItems(frame, interpolation, overscanWorld, particles && { field: particles });
|
|
529
409
|
const sampling = new Map(items.map((item) => [item.assetId, item.sampling]));
|
|
530
410
|
const assetIds = [...sampling.keys()];
|
|
531
411
|
const textureResults = await Promise.all(assetIds.map((assetId) => this.getTexture(assetId, sampling.get(assetId) ?? "nearest")));
|
|
532
412
|
const textureById = new Map(assetIds.map((assetId, index) => [assetId, textureResults[index]]));
|
|
533
|
-
const hudTexture = this.updateHud(frame);
|
|
413
|
+
const hudTexture = this.updateHud(frame, await loadGameUiImages(frame, (assetId) => this.assets.get(assetId)));
|
|
534
414
|
const count = items.length + (hudTexture ? 1 : 0);
|
|
535
415
|
this.ensureInstanceCapacity(count);
|
|
536
416
|
const instances = new Float32Array(count * INSTANCE_FLOATS);
|
|
@@ -542,22 +422,23 @@ export class WebGPUGameRenderer {
|
|
|
542
422
|
continue;
|
|
543
423
|
}
|
|
544
424
|
const texture = result.texture;
|
|
545
|
-
const rect = texture.width === 1 && texture.height === 1 ? { x: 0, y: 0, width: 1, height: 1 } : item
|
|
425
|
+
const rect = texture.width === 1 && texture.height === 1 ? { x: 0, y: 0, width: 1, height: 1 } : sourceRect(item, texture.width, texture.height);
|
|
546
426
|
const tint = parseTint(item.tint);
|
|
547
|
-
const
|
|
548
|
-
const
|
|
427
|
+
const subRect = item.frame !== undefined || item.cell !== undefined;
|
|
428
|
+
const insetX = item.sampling === "linear" && subRect && rect.width > 1 ? 0.5 : 0;
|
|
429
|
+
const insetY = item.sampling === "linear" && subRect && rect.height > 1 ? 0.5 : 0;
|
|
549
430
|
const u = (rect.x + insetX) / texture.width;
|
|
550
431
|
const v = (rect.y + insetY) / texture.height;
|
|
551
432
|
const du = (rect.width - 2 * insetX) / texture.width;
|
|
552
433
|
const dv = (rect.height - 2 * insetY) / texture.height;
|
|
553
|
-
|
|
554
|
-
|
|
434
|
+
writeInstance(instances, index, item.x, item.y, item.width, item.height, item.flipX ? u + du : u, item.flipY ? v + dv : v, item.flipX ? -du : du, item.flipY ? -dv : dv, tint[0], tint[1], tint[2], item.opacity, item.rotation, item.unlit ? 1 : 0);
|
|
435
|
+
appendBatch(batches, texture, item.blend, index);
|
|
555
436
|
}
|
|
556
437
|
if (hudTexture) {
|
|
557
438
|
const scale = frame.camera.zoom;
|
|
558
439
|
const projected = projectedCamera(frame, interpolation);
|
|
559
|
-
|
|
560
|
-
|
|
440
|
+
writeInstance(instances, items.length, projected.x, projected.y, frame.width / scale, frame.height / scale, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1);
|
|
441
|
+
appendBatch(batches, hudTexture, "normal", items.length);
|
|
561
442
|
}
|
|
562
443
|
const projected = projectedCamera(frame, interpolation);
|
|
563
444
|
const camera = new Float32Array([projected.x, projected.y,
|
|
@@ -565,18 +446,8 @@ export class WebGPUGameRenderer {
|
|
|
565
446
|
frame.height * (this.canvas.height + overscanPixels * 2) / Math.max(1, this.canvas.height),
|
|
566
447
|
frame.camera.zoom, 0, 0, 0]);
|
|
567
448
|
this.device.queue.writeBuffer(this.cameraBuffer, 0, camera);
|
|
568
|
-
const lighting = new Float32Array(392);
|
|
569
449
|
const sceneLighting = frame.lighting;
|
|
570
|
-
|
|
571
|
-
const ambient = parseTint(sceneLighting.ambient.color);
|
|
572
|
-
lighting.set([ambient[0], ambient[1], ambient[2], sceneLighting.ambient.intensity,
|
|
573
|
-
sceneLighting.points.length, 1], 0);
|
|
574
|
-
for (const [index, point] of sceneLighting.points.entries()) {
|
|
575
|
-
const color = parseTint(point.color);
|
|
576
|
-
lighting.set([point.x, point.y, point.radius, point.intensity,
|
|
577
|
-
color[0], color[1], color[2], 0, point.falloff], 8 + index * 12);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
450
|
+
const lighting = gameLightingUniforms(frame);
|
|
580
451
|
this.device.queue.writeBuffer(this.lightingBuffer, 0, lighting);
|
|
581
452
|
if (count > 0 && this.instanceBuffer) {
|
|
582
453
|
this.device.queue.writeBuffer(this.instanceBuffer, 0, instances);
|
|
@@ -591,107 +462,94 @@ export class WebGPUGameRenderer {
|
|
|
591
462
|
this.lightTarget.destroy();
|
|
592
463
|
this.lightTarget = undefined;
|
|
593
464
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
{ binding: 0, resource: this.sampler },
|
|
610
|
-
{ binding: 1, resource: (this.lightTarget?.texture ?? this.lightFallback).createView() },
|
|
611
|
-
] });
|
|
612
|
-
const pass = encoder.beginRenderPass({ colorAttachments: [{
|
|
613
|
-
view: hasEffects ? this.sourceTarget.createView() : this.context.getCurrentTexture().createView(),
|
|
614
|
-
loadOp: "clear",
|
|
615
|
-
storeOp: "store",
|
|
616
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
617
|
-
}] });
|
|
618
|
-
if (this.instanceBuffer) {
|
|
619
|
-
pass.setVertexBuffer(0, this.instanceBuffer);
|
|
620
|
-
pass.setBindGroup(1, lightSampleGroup);
|
|
621
|
-
let blend;
|
|
622
|
-
for (const batch of batches) {
|
|
623
|
-
if (hudAfterEffects && batch.first >= items.length) {
|
|
624
|
-
continue;
|
|
625
|
-
}
|
|
626
|
-
if (batch.blend !== blend) {
|
|
627
|
-
blend = batch.blend;
|
|
628
|
-
pass.setPipeline(blend === "additive"
|
|
629
|
-
? hasEffects ? this.effectAdditivePipeline : this.additivePipeline
|
|
630
|
-
: hasEffects ? this.effectSpritePipeline : this.pipeline);
|
|
465
|
+
await this.renderPipeline.render({
|
|
466
|
+
lighting: () => {
|
|
467
|
+
if (sceneLighting) {
|
|
468
|
+
const target = this.ensureLightTarget(this.canvas.width + (hasEffects ? overscanPixels * 2 : 0), this.canvas.height + (hasEffects ? overscanPixels * 2 : 0));
|
|
469
|
+
const lightGroup = this.device.createBindGroup({ layout: this.lightLayout, entries: [
|
|
470
|
+
{ binding: 0, resource: { buffer: this.cameraBuffer } },
|
|
471
|
+
{ binding: 1, resource: { buffer: this.lightingBuffer } },
|
|
472
|
+
] });
|
|
473
|
+
const lightPass = encoder.beginRenderPass({ colorAttachments: [{ view: target.createView(),
|
|
474
|
+
loadOp: "clear", storeOp: "store", clearValue: { r: 0, g: 0, b: 0, a: 1 } }] });
|
|
475
|
+
lightPass.setPipeline(this.lightPipeline);
|
|
476
|
+
lightPass.setBindGroup(0, lightGroup);
|
|
477
|
+
lightPass.draw(3);
|
|
478
|
+
lightPass.end();
|
|
479
|
+
target.markWritten();
|
|
631
480
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
481
|
+
},
|
|
482
|
+
sprites: () => {
|
|
483
|
+
const lightSampleGroup = this.device.createBindGroup({ layout: this.lightSampleLayout, entries: [
|
|
484
|
+
{ binding: 0, resource: this.sampler },
|
|
485
|
+
{ binding: 1, resource: (this.lightTarget?.texture ?? this.lightFallback).createView() },
|
|
486
|
+
] });
|
|
487
|
+
const pass = encoder.beginRenderPass({ colorAttachments: [{
|
|
488
|
+
view: hasEffects ? this.sourceTarget.createView() : this.context.getCurrentTexture().createView(),
|
|
489
|
+
loadOp: "clear",
|
|
490
|
+
storeOp: "store",
|
|
491
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
492
|
+
}] });
|
|
493
|
+
if (this.instanceBuffer) {
|
|
494
|
+
pass.setVertexBuffer(0, this.instanceBuffer);
|
|
495
|
+
pass.setBindGroup(1, lightSampleGroup);
|
|
496
|
+
let blend;
|
|
497
|
+
for (const batch of batches) {
|
|
498
|
+
if (hudAfterEffects && batch.first >= items.length) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (batch.blend !== blend) {
|
|
502
|
+
blend = batch.blend;
|
|
503
|
+
pass.setPipeline(blend === "additive"
|
|
504
|
+
? hasEffects ? this.effectAdditivePipeline : this.additivePipeline
|
|
505
|
+
: hasEffects ? this.effectSpritePipeline : this.pipeline);
|
|
506
|
+
}
|
|
507
|
+
pass.setBindGroup(0, batch.texture.bindGroup);
|
|
508
|
+
pass.draw(6, batch.count, 0, batch.first);
|
|
509
|
+
}
|
|
648
510
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
511
|
+
pass.end();
|
|
512
|
+
},
|
|
513
|
+
effects: async () => {
|
|
514
|
+
if (hasEffects) {
|
|
515
|
+
let source = this.sourceTarget;
|
|
516
|
+
const output = this.effectTarget;
|
|
517
|
+
source = await encodeGameEffects({ effects: this.effects, source, output, encoder,
|
|
518
|
+
effectContext: this.effectContext, effectExecutor: this.effectExecutor,
|
|
519
|
+
effectRecipeRunner: this.effectRecipeRunner, effectRegistry: this.effectRegistry,
|
|
520
|
+
srgbTarget: this.srgbTarget, lutTarget: this.lutTarget, toSrgbPipeline: this.toSrgbPipeline,
|
|
521
|
+
toLinearPipeline: this.toLinearPipeline, getLutTexture: (id, size) => this.getLutTexture(id, size),
|
|
522
|
+
convertColor: (command, input, target, pipeline) => this.convertColor(command, input, target, pipeline) });
|
|
523
|
+
const presentGroup = this.device.createBindGroup({ layout: this.presentLayout, entries: [
|
|
524
|
+
{ binding: 0, resource: this.sampler }, { binding: 1, resource: source.createView() },
|
|
525
|
+
] });
|
|
526
|
+
const presentPass = encoder.beginRenderPass({ colorAttachments: [{
|
|
527
|
+
view: this.context.getCurrentTexture().createView(), loadOp: "clear", storeOp: "store",
|
|
528
|
+
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
529
|
+
}] });
|
|
530
|
+
presentPass.setPipeline(this.presentPipeline);
|
|
531
|
+
presentPass.setBindGroup(0, presentGroup);
|
|
532
|
+
presentPass.draw(3);
|
|
533
|
+
presentPass.end();
|
|
654
534
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
535
|
+
},
|
|
536
|
+
hud: () => {
|
|
537
|
+
if (hudAfterEffects && hudTexture) {
|
|
538
|
+
const hudGroup = this.device.createBindGroup({ layout: this.presentLayout, entries: [
|
|
539
|
+
{ binding: 0, resource: this.sampler }, { binding: 1, resource: hudTexture.texture.createView() },
|
|
540
|
+
] });
|
|
541
|
+
const hudPass = encoder.beginRenderPass({ colorAttachments: [{
|
|
542
|
+
view: this.context.getCurrentTexture().createView(), loadOp: "load", storeOp: "store",
|
|
543
|
+
}] });
|
|
544
|
+
hudPass.setPipeline(this.hudPipeline);
|
|
545
|
+
hudPass.setBindGroup(0, hudGroup);
|
|
546
|
+
hudPass.draw(3);
|
|
547
|
+
hudPass.end();
|
|
666
548
|
}
|
|
667
|
-
[source, output] = [output, source];
|
|
668
|
-
}
|
|
669
|
-
const presentGroup = this.device.createBindGroup({ layout: this.presentLayout, entries: [
|
|
670
|
-
{ binding: 0, resource: this.sampler }, { binding: 1, resource: source.createView() },
|
|
671
|
-
] });
|
|
672
|
-
const presentPass = encoder.beginRenderPass({ colorAttachments: [{
|
|
673
|
-
view: this.context.getCurrentTexture().createView(), loadOp: "clear", storeOp: "store",
|
|
674
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
675
|
-
}] });
|
|
676
|
-
presentPass.setPipeline(this.presentPipeline);
|
|
677
|
-
presentPass.setBindGroup(0, presentGroup);
|
|
678
|
-
presentPass.draw(3);
|
|
679
|
-
presentPass.end();
|
|
680
|
-
if (hudAfterEffects && hudTexture) {
|
|
681
|
-
const hudGroup = this.device.createBindGroup({ layout: this.presentLayout, entries: [
|
|
682
|
-
{ binding: 0, resource: this.sampler }, { binding: 1, resource: hudTexture.texture.createView() },
|
|
683
|
-
] });
|
|
684
|
-
const hudPass = encoder.beginRenderPass({ colorAttachments: [{
|
|
685
|
-
view: this.context.getCurrentTexture().createView(), loadOp: "load", storeOp: "store",
|
|
686
|
-
}] });
|
|
687
|
-
hudPass.setPipeline(this.hudPipeline);
|
|
688
|
-
hudPass.setBindGroup(0, hudGroup);
|
|
689
|
-
hudPass.draw(3);
|
|
690
|
-
hudPass.end();
|
|
691
549
|
}
|
|
692
|
-
}
|
|
550
|
+
});
|
|
693
551
|
this.device.queue.submit([encoder.finish()]);
|
|
694
|
-
const textureBytes = [...this.textures.values(), ...this.placeholders.values(), this.fallback,
|
|
552
|
+
const textureBytes = [...this.textures.values(), ...this.placeholders.values(), this.fallback, this.particleDot,
|
|
695
553
|
...(this.hudTexture ? [this.hudTexture] : [])].reduce((sum, entry) => sum + entry.width * entry.height * 4, 0);
|
|
696
554
|
return {
|
|
697
555
|
backend: this.backend,
|
|
@@ -707,21 +565,14 @@ export class WebGPUGameRenderer {
|
|
|
707
565
|
instanceBufferBytes: this.instanceCapacity * INSTANCE_FLOATS * 4,
|
|
708
566
|
};
|
|
709
567
|
}
|
|
710
|
-
updateHud(frame) {
|
|
711
|
-
if (frame.hud.length === 0) {
|
|
568
|
+
updateHud(frame, images) {
|
|
569
|
+
if (frame.hud.length === 0 && !frame.ui) {
|
|
712
570
|
return undefined;
|
|
713
571
|
}
|
|
714
|
-
const key = JSON.stringify([this.canvas.width, this.canvas.height, frame.hud]);
|
|
572
|
+
const key = JSON.stringify([this.canvas.width, this.canvas.height, frame.hud, frame.ui, [...images.keys()]]);
|
|
715
573
|
if (key !== this.hudKey) {
|
|
716
574
|
const canvas = this.hudCanvas ?? document.createElement("canvas");
|
|
717
|
-
canvas.width
|
|
718
|
-
canvas.height = this.canvas.height;
|
|
719
|
-
const context = canvas.getContext("2d");
|
|
720
|
-
if (!context) {
|
|
721
|
-
throw new Error("HUD canvas is unavailable");
|
|
722
|
-
}
|
|
723
|
-
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
724
|
-
paintHud(context, frame.hud, 1, frame.gameId);
|
|
575
|
+
paintWebGPUHud(canvas, this.canvas.width, this.canvas.height, frame, images);
|
|
725
576
|
this.hudTexture?.texture.destroy();
|
|
726
577
|
this.hudTexture = this.upload(canvas);
|
|
727
578
|
this.hudCanvas = canvas;
|
|
@@ -729,18 +580,6 @@ export class WebGPUGameRenderer {
|
|
|
729
580
|
}
|
|
730
581
|
return this.hudTexture;
|
|
731
582
|
}
|
|
732
|
-
appendBatch(batches, texture, blend, index) {
|
|
733
|
-
const last = batches[batches.length - 1];
|
|
734
|
-
if (last?.texture === texture && last.blend === blend) {
|
|
735
|
-
last.count++;
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
batches.push({ texture, blend, first: index, count: 1 });
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
writeInstance(target, index, ...values) {
|
|
742
|
-
target.set(values, index * INSTANCE_FLOATS);
|
|
743
|
-
}
|
|
744
583
|
ensureInstanceCapacity(count) {
|
|
745
584
|
if (count <= this.instanceCapacity) {
|
|
746
585
|
return;
|
|
@@ -790,6 +629,7 @@ export class WebGPUGameRenderer {
|
|
|
790
629
|
this.lightTarget?.destroy();
|
|
791
630
|
this.lightFallback.destroy();
|
|
792
631
|
this.fallback.texture.destroy();
|
|
632
|
+
this.particleDot.texture.destroy();
|
|
793
633
|
this.hudTexture?.texture.destroy();
|
|
794
634
|
for (const entry of this.placeholders.values()) {
|
|
795
635
|
entry.texture.destroy();
|