reze-engine 0.16.1 → 0.16.3
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 +139 -284
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +5 -5
- package/dist/model.d.ts +4 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +14 -1
- package/dist/physics/body.d.ts +1 -0
- package/dist/physics/body.d.ts.map +1 -1
- package/dist/physics/body.js +2 -0
- package/dist/physics/constraint.d.ts +8 -1
- package/dist/physics/constraint.d.ts.map +1 -1
- package/dist/physics/constraint.js +13 -1
- package/dist/physics/physics.d.ts +15 -1
- package/dist/physics/physics.d.ts.map +1 -1
- package/dist/physics/physics.js +413 -48
- package/dist/physics/solver.d.ts.map +1 -1
- package/dist/physics/solver.js +223 -39
- package/dist/physics/types.d.ts +2 -1
- package/dist/physics/types.d.ts.map +1 -1
- package/dist/physics/types.js +5 -0
- package/dist/physics/world.d.ts +3 -0
- package/dist/physics/world.d.ts.map +1 -1
- package/dist/physics/world.js +31 -4
- package/dist/physics-debug.d.ts +30 -0
- package/dist/physics-debug.d.ts.map +1 -0
- package/dist/physics-debug.js +526 -0
- package/dist/pmx-loader.d.ts +2 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +37 -20
- package/dist/shaders/materials/body.d.ts +1 -1
- package/dist/shaders/materials/body.d.ts.map +1 -1
- package/dist/shaders/materials/body.js +90 -85
- package/dist/shaders/materials/cloth_rough.d.ts +1 -1
- package/dist/shaders/materials/cloth_rough.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_rough.js +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts +1 -1
- package/dist/shaders/materials/cloth_smooth.d.ts.map +1 -1
- package/dist/shaders/materials/cloth_smooth.js +2 -2
- package/dist/shaders/materials/common.d.ts +1 -1
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +10 -0
- package/dist/shaders/materials/default.d.ts +1 -1
- package/dist/shaders/materials/default.d.ts.map +1 -1
- package/dist/shaders/materials/default.js +37 -32
- package/dist/shaders/materials/eye.d.ts +1 -1
- package/dist/shaders/materials/eye.d.ts.map +1 -1
- package/dist/shaders/materials/eye.js +40 -35
- package/dist/shaders/materials/face.d.ts +1 -1
- package/dist/shaders/materials/face.d.ts.map +1 -1
- package/dist/shaders/materials/face.js +90 -85
- package/dist/shaders/materials/hair.d.ts +1 -1
- package/dist/shaders/materials/hair.d.ts.map +1 -1
- package/dist/shaders/materials/hair.js +85 -75
- package/dist/shaders/materials/metal.d.ts +1 -1
- package/dist/shaders/materials/metal.d.ts.map +1 -1
- package/dist/shaders/materials/metal.js +1 -1
- package/dist/shaders/materials/stockings.d.ts +1 -1
- package/dist/shaders/materials/stockings.d.ts.map +1 -1
- package/dist/shaders/materials/stockings.js +1 -1
- package/dist/shaders/passes/physics-debug.d.ts +2 -0
- package/dist/shaders/passes/physics-debug.d.ts.map +1 -0
- package/dist/shaders/passes/physics-debug.js +69 -0
- package/package.json +1 -1
- package/src/engine.ts +36 -21
- package/src/model.ts +18 -1
- package/src/physics/body.ts +5 -0
- package/src/physics/constraint.ts +35 -6
- package/src/physics/physics.ts +423 -56
- package/src/physics/solver.ts +213 -38
- package/src/physics/types.ts +10 -2
- package/src/physics/world.ts +33 -4
- package/src/pmx-loader.ts +38 -20
- package/src/shaders/materials/body.ts +97 -92
- package/src/shaders/materials/cloth_rough.ts +1 -1
- package/src/shaders/materials/cloth_smooth.ts +2 -2
- package/src/shaders/materials/common.ts +10 -0
- package/src/shaders/materials/default.ts +45 -40
- package/src/shaders/materials/eye.ts +48 -43
- package/src/shaders/materials/face.ts +97 -92
- package/src/shaders/materials/hair.ts +92 -82
- package/src/shaders/materials/metal.ts +1 -1
- package/src/shaders/materials/stockings.ts +1 -1
- package/dist/gpu-profile.d.ts +0 -19
- package/dist/gpu-profile.d.ts.map +0 -1
- package/dist/gpu-profile.js +0 -120
- package/dist/physics/profile.d.ts +0 -18
- package/dist/physics/profile.d.ts.map +0 -1
- package/dist/physics/profile.js +0 -44
|
@@ -1,82 +1,92 @@
|
|
|
1
|
-
// M_Hair — 仿深空之眼渲染预设v1.0_by_小绿毛猫 "M_Hair". Toon + fresnel rim + bevel +
|
|
2
|
-
// bright-tex gate, mixed 80/20 NPR/PBR. MixShader.001 Fac=0.2 keeps Principled subtle.
|
|
3
|
-
|
|
4
|
-
import { NODES_WGSL } from "./nodes"
|
|
5
|
-
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
6
|
-
|
|
7
|
-
export const HAIR_SHADER_WGSL = /* wgsl */ `
|
|
8
|
-
|
|
9
|
-
${NODES_WGSL}
|
|
10
|
-
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
11
|
-
|
|
12
|
-
// Pipeline-override: the engine compiles two variants — the normal opaque hair pipeline
|
|
13
|
-
// (IS_OVER_EYES=false) and a second pipeline that re-draws hair fragments stencil-matched
|
|
14
|
-
// against the eye stamp with 25% alpha so eyes read through the hair silhouette. Resolved
|
|
15
|
-
// at pipeline-compile time; the dead branch is dropped by the shader compiler.
|
|
16
|
-
override IS_OVER_EYES: bool = false;
|
|
17
|
-
|
|
18
|
-
const HAIR_SPECULAR: f32 = 1.0;
|
|
19
|
-
const HAIR_ROUGHNESS: f32 = 0.3;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
let
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
let
|
|
40
|
-
let
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
let
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let
|
|
54
|
-
let
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
let
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
1
|
+
// M_Hair — 仿深空之眼渲染预设v1.0_by_小绿毛猫 "M_Hair". Toon + fresnel rim + bevel +
|
|
2
|
+
// bright-tex gate, mixed 80/20 NPR/PBR. MixShader.001 Fac=0.2 keeps Principled subtle.
|
|
3
|
+
|
|
4
|
+
import { NODES_WGSL } from "./nodes"
|
|
5
|
+
import { COMMON_MATERIAL_PRELUDE_WGSL } from "./common"
|
|
6
|
+
|
|
7
|
+
export const HAIR_SHADER_WGSL = /* wgsl */ `
|
|
8
|
+
|
|
9
|
+
${NODES_WGSL}
|
|
10
|
+
${COMMON_MATERIAL_PRELUDE_WGSL}
|
|
11
|
+
|
|
12
|
+
// Pipeline-override: the engine compiles two variants — the normal opaque hair pipeline
|
|
13
|
+
// (IS_OVER_EYES=false) and a second pipeline that re-draws hair fragments stencil-matched
|
|
14
|
+
// against the eye stamp with 25% alpha so eyes read through the hair silhouette. Resolved
|
|
15
|
+
// at pipeline-compile time; the dead branch is dropped by the shader compiler.
|
|
16
|
+
override IS_OVER_EYES: bool = false;
|
|
17
|
+
|
|
18
|
+
const HAIR_SPECULAR: f32 = 1.0;
|
|
19
|
+
const HAIR_ROUGHNESS: f32 = 0.3;
|
|
20
|
+
// EEVEE Light Clamp equivalent (same value as body/face/cloth_rough). Hair
|
|
21
|
+
// is the firefly-prone material: physics-animated with the noisiest blended
|
|
22
|
+
// normals, so grazing NV spikes the GGX term and flashes white through
|
|
23
|
+
// bloom without this.
|
|
24
|
+
const HAIR_SPEC_CLAMP: f32 = 10.0;
|
|
25
|
+
const HAIR_TEX_GATE_THRESH: f32 = 0.15000000596046448;
|
|
26
|
+
const HAIR_RIM2_POW: f32 = 0.6300000548362732;
|
|
27
|
+
const HAIR_MIX_BG: vec3f = vec3f(0.1673291176557541);
|
|
28
|
+
const HAIR_MIX_NPR: f32 = 0.2;
|
|
29
|
+
|
|
30
|
+
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
31
|
+
let tex_s = textureSample(diffuseTexture, diffuseSampler, input.uv);
|
|
32
|
+
// MMD alpha semantics: material alpha × texture alpha. Hair/lace/accessory
|
|
33
|
+
// textures cut their shapes in the alpha channel — ignoring it renders each
|
|
34
|
+
// card's full quad with the texture's padding color (white shimmer on dark
|
|
35
|
+
// hair for models whose textures pad with white).
|
|
36
|
+
let alpha = material.alpha * tex_s.a;
|
|
37
|
+
if (alpha < 0.001) { discard; }
|
|
38
|
+
|
|
39
|
+
let n = safe_normal(input.normal);
|
|
40
|
+
let v = normalize(camera.viewPos - input.worldPos);
|
|
41
|
+
let l = -light.lights[0].direction.xyz;
|
|
42
|
+
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
43
|
+
let amb = light.ambientColor.xyz;
|
|
44
|
+
let shadow = sampleShadow(input.worldPos, n);
|
|
45
|
+
|
|
46
|
+
let tex_color = tex_s.rgb;
|
|
47
|
+
|
|
48
|
+
// ═══ NPR STACK ═══
|
|
49
|
+
let hue_sat_shadow = hue_sat_id(1.2, 0.5, 1.0, tex_color);
|
|
50
|
+
let hue_sat_002 = hue_sat(0.48, 1.2, 0.7, 1.0, hue_sat_shadow);
|
|
51
|
+
let hue_sat_001 = hue_sat_id(1.5, 1.0, 1.0, tex_color);
|
|
52
|
+
|
|
53
|
+
let ndotl_raw = shader_to_rgb_diffuse(n, l, sun, amb, shadow);
|
|
54
|
+
let ramp_008 = ramp_constant(ndotl_raw, 0.0, vec4f(0,0,0,1), 0.2966, vec4f(1,1,1,1)).r;
|
|
55
|
+
|
|
56
|
+
let mix_004 = mix_blend(ramp_008, hue_sat_002, hue_sat_001);
|
|
57
|
+
let bc = bright_contrast(mix_004, 0.1, 0.2);
|
|
58
|
+
|
|
59
|
+
let bevel_z = clamp(n.y, 0.0, 1.0);
|
|
60
|
+
let mix_003 = mix_blend(bevel_z, bc, hue_sat_002);
|
|
61
|
+
|
|
62
|
+
let rim2_raw = fresnel(1.45, n, v) * layer_weight_fresnel(0.61, n, v);
|
|
63
|
+
let rim2_fac = math_power(rim2_raw, HAIR_RIM2_POW);
|
|
64
|
+
let mix_shader_002 = mix(mix_003, HAIR_MIX_BG, rim2_fac);
|
|
65
|
+
|
|
66
|
+
// GREATER_THAN on a color input uses BT.601 luminance — same socket-semantic fix as face.ts.
|
|
67
|
+
let tex_gate = math_greater_than(color_to_value(tex_color), HAIR_TEX_GATE_THRESH);
|
|
68
|
+
let gate_emit = vec3f(tex_gate) * 0.1;
|
|
69
|
+
|
|
70
|
+
let npr_stack = mix_shader_002 + gate_emit;
|
|
71
|
+
|
|
72
|
+
// ═══ PRINCIPLED BSDF ═══
|
|
73
|
+
// Graph has a noise→normal_map bump (Strength=0.1) on Principled.Normal, but MixShader.001
|
|
74
|
+
// weights Principled at only 0.2 — the bumped spec × that weight is imperceptible, so we
|
|
75
|
+
// drop the subtree and keep plain n (saves a tex_noise + bump_lh per hair fragment).
|
|
76
|
+
let principled = eval_principled(
|
|
77
|
+
PrincipledIn(bc, 0.0, HAIR_SPECULAR, HAIR_ROUGHNESS, HAIR_SPEC_CLAMP, 0.0, 0.0),
|
|
78
|
+
n, l, v, sun, amb, shadow
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
let final_color = mix(npr_stack, principled, HAIR_MIX_NPR);
|
|
82
|
+
|
|
83
|
+
var outAlpha = alpha;
|
|
84
|
+
if (IS_OVER_EYES) { outAlpha = alpha * 0.25; }
|
|
85
|
+
|
|
86
|
+
var out: FSOut;
|
|
87
|
+
out.color = vec4f(final_color, outAlpha);
|
|
88
|
+
out.mask = vec4f(1.0, 1.0, 0.0, out.color.a);
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
`
|
|
@@ -26,7 +26,7 @@ const METAL_MIX_SHADER_FAC: f32 = 0.6967;
|
|
|
26
26
|
const METAL_VORONOI_SCALE: f32 = 4.3;
|
|
27
27
|
|
|
28
28
|
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
29
|
-
let n =
|
|
29
|
+
let n = safe_normal(input.normal);
|
|
30
30
|
let v = normalize(camera.viewPos - input.worldPos);
|
|
31
31
|
let l = -light.lights[0].direction.xyz;
|
|
32
32
|
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
|
@@ -66,7 +66,7 @@ fn ramp_ease_s(f: f32, p0: f32, p1: f32) -> f32 {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
@fragment fn fs(input: VertexOutput) -> FSOut {
|
|
69
|
-
let n =
|
|
69
|
+
let n = safe_normal(input.normal);
|
|
70
70
|
let v = normalize(camera.viewPos - input.worldPos);
|
|
71
71
|
let l = -light.lights[0].direction.xyz;
|
|
72
72
|
let sun = light.lights[0].color.xyz * light.lights[0].color.w;
|
package/dist/gpu-profile.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
export declare class GpuTimestamp {
|
|
2
|
-
enabled: boolean;
|
|
3
|
-
readonly supported: boolean;
|
|
4
|
-
private device;
|
|
5
|
-
private querySet;
|
|
6
|
-
private resolveBuffer;
|
|
7
|
-
private readbackBuffer;
|
|
8
|
-
private nextSlot;
|
|
9
|
-
private frameLabels;
|
|
10
|
-
private inFlight;
|
|
11
|
-
private pendingLabels;
|
|
12
|
-
constructor(device: GPUDevice);
|
|
13
|
-
private ensureCreated;
|
|
14
|
-
beginFrame(): void;
|
|
15
|
-
wrap(desc: GPURenderPassDescriptor, label: string): GPURenderPassDescriptor;
|
|
16
|
-
endFrame(encoder: GPUCommandEncoder): void;
|
|
17
|
-
afterSubmit(): void;
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=gpu-profile.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gpu-profile.d.ts","sourceRoot":"","sources":["../src/gpu-profile.ts"],"names":[],"mappings":"AAoBA,qBAAa,YAAY;IACvB,OAAO,UAAQ;IACf,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,QAAQ,CAAQ;IAKxB,OAAO,CAAC,aAAa,CAAwB;gBAEjC,MAAM,EAAE,SAAS;IAK7B,OAAO,CAAC,aAAa;IAiBrB,UAAU,IAAI,IAAI;IAWlB,IAAI,CAAC,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAqB3E,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAW1C,WAAW,IAAI,IAAI;CAwBpB"}
|
package/dist/gpu-profile.js
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
// WebGPU timestamp-query profiling. Records pass durations into the same
|
|
2
|
-
// profiler module as CPU phases (so the snapshot table mixes both with a
|
|
3
|
-
// "gpu." prefix). Read-only — no effect on rendering output.
|
|
4
|
-
//
|
|
5
|
-
// Pattern (per spec):
|
|
6
|
-
// 1. Each render pass declares timestampWrites { begin, end } slot indices.
|
|
7
|
-
// 2. After all passes, encoder.resolveQuerySet writes uint64 ns timestamps
|
|
8
|
-
// into a QUERY_RESOLVE buffer.
|
|
9
|
-
// 3. We copy the resolve buffer into a MAP_READ readback buffer.
|
|
10
|
-
// 4. Submit. After the GPU finishes, mapAsync resolves; we read durations
|
|
11
|
-
// and record them.
|
|
12
|
-
//
|
|
13
|
-
// One readback buffer with an in-flight gate: if mapAsync hasn't resolved
|
|
14
|
-
// from a previous frame, we skip the copy this frame (no contention, just
|
|
15
|
-
// one frame of dropped sampling).
|
|
16
|
-
import { profiler } from "./physics/profile";
|
|
17
|
-
const MAX_PASSES_PER_FRAME = 64;
|
|
18
|
-
export class GpuTimestamp {
|
|
19
|
-
constructor(device) {
|
|
20
|
-
this.enabled = false;
|
|
21
|
-
this.querySet = null;
|
|
22
|
-
this.resolveBuffer = null;
|
|
23
|
-
this.readbackBuffer = null;
|
|
24
|
-
this.nextSlot = 0;
|
|
25
|
-
this.frameLabels = [];
|
|
26
|
-
this.inFlight = false;
|
|
27
|
-
// Set inside endFrame() when a copy was queued this frame; consumed by
|
|
28
|
-
// afterSubmit() to kick mapAsync. mapAsync must run AFTER queue.submit —
|
|
29
|
-
// calling it before submit puts the buffer into "mapping pending" state,
|
|
30
|
-
// which makes the copy in the encoder illegal.
|
|
31
|
-
this.pendingLabels = null;
|
|
32
|
-
this.device = device;
|
|
33
|
-
this.supported = device.features.has("timestamp-query");
|
|
34
|
-
}
|
|
35
|
-
ensureCreated() {
|
|
36
|
-
if (this.querySet || !this.supported)
|
|
37
|
-
return;
|
|
38
|
-
const slots = MAX_PASSES_PER_FRAME * 2;
|
|
39
|
-
this.querySet = this.device.createQuerySet({ type: "timestamp", count: slots });
|
|
40
|
-
this.resolveBuffer = this.device.createBuffer({
|
|
41
|
-
label: "gpu-timestamp-resolve",
|
|
42
|
-
size: slots * 8,
|
|
43
|
-
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
|
|
44
|
-
});
|
|
45
|
-
this.readbackBuffer = this.device.createBuffer({
|
|
46
|
-
label: "gpu-timestamp-readback",
|
|
47
|
-
size: slots * 8,
|
|
48
|
-
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
// Called once at the top of each render() before any pass wrap.
|
|
52
|
-
beginFrame() {
|
|
53
|
-
if (!this.enabled || !this.supported)
|
|
54
|
-
return;
|
|
55
|
-
this.ensureCreated();
|
|
56
|
-
this.nextSlot = 0;
|
|
57
|
-
this.frameLabels = [];
|
|
58
|
-
}
|
|
59
|
-
// Wrap a render-pass descriptor with timestampWrites for `label`. If
|
|
60
|
-
// profiling is off / unsupported / out of slots, returns desc unchanged.
|
|
61
|
-
// The same label called multiple times (e.g. each bloom mip) is fine —
|
|
62
|
-
// the profiler accumulates by label.
|
|
63
|
-
wrap(desc, label) {
|
|
64
|
-
if (!this.enabled || !this.querySet)
|
|
65
|
-
return desc;
|
|
66
|
-
if (this.nextSlot + 2 > MAX_PASSES_PER_FRAME * 2)
|
|
67
|
-
return desc;
|
|
68
|
-
const begin = this.nextSlot;
|
|
69
|
-
const end = this.nextSlot + 1;
|
|
70
|
-
this.nextSlot += 2;
|
|
71
|
-
this.frameLabels.push(label);
|
|
72
|
-
return {
|
|
73
|
-
...desc,
|
|
74
|
-
timestampWrites: {
|
|
75
|
-
querySet: this.querySet,
|
|
76
|
-
beginningOfPassWriteIndex: begin,
|
|
77
|
-
endOfPassWriteIndex: end,
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
// Call on the encoder after all passes, BEFORE encoder.finish(). Adds
|
|
82
|
-
// resolveQuerySet + copyBufferToBuffer commands. mapAsync is deferred to
|
|
83
|
-
// afterSubmit() — calling it before submit transitions the readback buffer
|
|
84
|
-
// into "mapping pending" state, which makes the queued copy illegal.
|
|
85
|
-
endFrame(encoder) {
|
|
86
|
-
if (!this.enabled || !this.querySet || this.frameLabels.length === 0)
|
|
87
|
-
return;
|
|
88
|
-
const count = this.frameLabels.length * 2;
|
|
89
|
-
encoder.resolveQuerySet(this.querySet, 0, count, this.resolveBuffer, 0);
|
|
90
|
-
if (this.inFlight)
|
|
91
|
-
return;
|
|
92
|
-
encoder.copyBufferToBuffer(this.resolveBuffer, 0, this.readbackBuffer, 0, count * 8);
|
|
93
|
-
this.pendingLabels = this.frameLabels.slice();
|
|
94
|
-
}
|
|
95
|
-
// Call AFTER device.queue.submit(). Kicks mapAsync if a copy was queued
|
|
96
|
-
// this frame; the .then() reads durations and unmaps.
|
|
97
|
-
afterSubmit() {
|
|
98
|
-
const labels = this.pendingLabels;
|
|
99
|
-
if (!labels)
|
|
100
|
-
return;
|
|
101
|
-
this.pendingLabels = null;
|
|
102
|
-
this.inFlight = true;
|
|
103
|
-
const readback = this.readbackBuffer;
|
|
104
|
-
void readback.mapAsync(GPUMapMode.READ).then(() => {
|
|
105
|
-
const arr = new BigInt64Array(readback.getMappedRange());
|
|
106
|
-
for (let i = 0; i < labels.length; i++) {
|
|
107
|
-
const start = arr[i * 2];
|
|
108
|
-
const end = arr[i * 2 + 1];
|
|
109
|
-
// Per spec, timestamps are nanoseconds.
|
|
110
|
-
const ms = Number(end - start) / 1e6;
|
|
111
|
-
if (ms >= 0 && ms < 1000)
|
|
112
|
-
profiler.record("gpu." + labels[i], ms);
|
|
113
|
-
}
|
|
114
|
-
readback.unmap();
|
|
115
|
-
this.inFlight = false;
|
|
116
|
-
}, () => {
|
|
117
|
-
this.inFlight = false;
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
declare class PhysicsProfiler {
|
|
2
|
-
enabled: boolean;
|
|
3
|
-
private sums;
|
|
4
|
-
private counts;
|
|
5
|
-
begin(): number;
|
|
6
|
-
end(label: string, t0: number): void;
|
|
7
|
-
record(label: string, ms: number): void;
|
|
8
|
-
snapshot(): Record<string, {
|
|
9
|
-
avgMs: number;
|
|
10
|
-
calls: number;
|
|
11
|
-
totalMs: number;
|
|
12
|
-
}>;
|
|
13
|
-
reset(): void;
|
|
14
|
-
}
|
|
15
|
-
export declare const profiler: PhysicsProfiler;
|
|
16
|
-
export type ProfileSnapshot = ReturnType<PhysicsProfiler["snapshot"]>;
|
|
17
|
-
export {};
|
|
18
|
-
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/physics/profile.ts"],"names":[],"mappings":"AAIA,cAAM,eAAe;IACnB,OAAO,UAAQ;IACf,OAAO,CAAC,IAAI,CAA8C;IAC1D,OAAO,CAAC,MAAM,CAA8C;IAE5D,KAAK,IAAI,MAAM;IAIf,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IASpC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAQvC,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAU7E,KAAK,IAAI,IAAI;CAId;AAED,eAAO,MAAM,QAAQ,iBAAwB,CAAA;AAC7C,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAA"}
|
package/dist/physics/profile.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// Per-phase wall-clock accumulator. Off by default — when `enabled` is false,
|
|
2
|
-
// begin()/end() short-circuit so production has zero overhead. Toggle via
|
|
3
|
-
// `RezePhysics.setProfileEnabled(true)` and read with `getProfile()`.
|
|
4
|
-
class PhysicsProfiler {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.enabled = false;
|
|
7
|
-
this.sums = Object.create(null);
|
|
8
|
-
this.counts = Object.create(null);
|
|
9
|
-
}
|
|
10
|
-
begin() {
|
|
11
|
-
return this.enabled ? performance.now() : 0;
|
|
12
|
-
}
|
|
13
|
-
end(label, t0) {
|
|
14
|
-
if (!this.enabled)
|
|
15
|
-
return;
|
|
16
|
-
const dt = performance.now() - t0;
|
|
17
|
-
this.sums[label] = (this.sums[label] ?? 0) + dt;
|
|
18
|
-
this.counts[label] = (this.counts[label] ?? 0) + 1;
|
|
19
|
-
}
|
|
20
|
-
// Direct accumulator for durations not measured via begin()/end() — used by
|
|
21
|
-
// GPU timestamp readback, which arrives async after the frame has ended.
|
|
22
|
-
record(label, ms) {
|
|
23
|
-
if (!this.enabled)
|
|
24
|
-
return;
|
|
25
|
-
this.sums[label] = (this.sums[label] ?? 0) + ms;
|
|
26
|
-
this.counts[label] = (this.counts[label] ?? 0) + 1;
|
|
27
|
-
}
|
|
28
|
-
// Snapshot since last reset(). avgMs is per call; totalMs is the cumulative
|
|
29
|
-
// window cost — divide by elapsed wall time to get a "% of frame budget".
|
|
30
|
-
snapshot() {
|
|
31
|
-
const out = {};
|
|
32
|
-
for (const k in this.sums) {
|
|
33
|
-
const total = this.sums[k];
|
|
34
|
-
const calls = this.counts[k];
|
|
35
|
-
out[k] = { avgMs: calls > 0 ? total / calls : 0, calls, totalMs: total };
|
|
36
|
-
}
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
reset() {
|
|
40
|
-
this.sums = Object.create(null);
|
|
41
|
-
this.counts = Object.create(null);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
export const profiler = new PhysicsProfiler();
|