@gg-web-engine/core 0.0.38 → 0.0.40
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/dist/2d/components/physics/i-rigid-body-2d.component.d.ts +3 -0
- package/dist/2d/components/physics/i-trigger-2d.component.d.ts +3 -0
- package/dist/2d/components/rendering/i-visual-scene-2d.component.d.ts +1 -1
- package/dist/2d/entities/i-renderable-2d.entity.d.ts +3 -1
- package/dist/2d/entities/i-renderable-2d.entity.js +7 -0
- package/dist/2d/gg-2d-world.d.ts +1 -0
- package/dist/2d/models/body-options.d.ts +5 -1
- package/dist/3d/components/physics/i-physics-world-3d.component.d.ts +7 -0
- package/dist/3d/components/physics/i-rigid-body-3d.component.d.ts +3 -0
- package/dist/3d/components/physics/i-trigger-3d.component.d.ts +3 -0
- package/dist/3d/components/rendering/i-visual-scene-3d.component.d.ts +1 -1
- package/dist/3d/entities/controllers/input/car-keyboard-handling.controller.js +2 -2
- package/dist/3d/entities/controllers/input/gg-car-keyboard-handling.controller.js +3 -3
- package/dist/3d/entities/i-renderable-3d.entity.d.ts +3 -1
- package/dist/3d/entities/i-renderable-3d.entity.js +7 -0
- package/dist/3d/entities/map-graph-3d.entity.d.ts +2 -3
- package/dist/3d/entities/map-graph-3d.entity.js +10 -7
- package/dist/3d/entities/raycast-vehicle-3d.entity.d.ts +2 -0
- package/dist/3d/entities/raycast-vehicle-3d.entity.js +15 -1
- package/dist/3d/factories.d.ts +3 -3
- package/dist/3d/gg-3d-world.d.ts +1 -0
- package/dist/3d/models/body-options.d.ts +5 -1
- package/dist/3d/models/shapes.d.ts +41 -1
- package/dist/base/clock/pausable-clock.d.ts +62 -1
- package/dist/base/clock/pausable-clock.js +102 -8
- package/dist/base/components/physics/i-body.component.d.ts +3 -1
- package/dist/base/components/physics/i-physics-world.component.d.ts +8 -6
- package/dist/base/components/rendering/i-renderer.component.d.ts +6 -2
- package/dist/base/components/rendering/i-visual-scene.component.d.ts +0 -4
- package/dist/base/entities/controllers/inline-controller.d.ts +1 -1
- package/dist/base/entities/controllers/inline-controller.js +4 -1
- package/dist/base/entities/i-entity.d.ts +1 -1
- package/dist/base/entities/i-entity.js +3 -3
- package/dist/base/entities/i-renderer.entity.d.ts +4 -0
- package/dist/base/entities/i-renderer.entity.js +8 -0
- package/dist/base/gg-world.d.ts +8 -5
- package/dist/base/gg-world.js +81 -39
- package/dist/base/index.d.ts +0 -1
- package/dist/base/index.js +0 -1
- package/dist/base/inputs/mouse.input.js +9 -4
- package/dist/base/models/body-options.d.ts +9 -0
- package/dist/dev/gg-console.ui.js +16 -8
- package/dist/dev/gg-debugger.ui.d.ts +9 -0
- package/dist/dev/gg-debugger.ui.js +206 -34
- package/dist/dev/gg-static.d.ts +0 -1
- package/dist/dev/gg-static.js +15 -8
- package/dist/dev/index.d.ts +1 -0
- package/dist/dev/index.js +1 -0
- package/dist/dev/performance-meter.entity.d.ts +21 -0
- package/dist/dev/performance-meter.entity.js +120 -0
- package/package.json +1 -1
- package/dist/base/interfaces/i-debug-physics-drawer.d.ts +0 -8
- package/dist/base/interfaces/i-debug-physics-drawer.js +0 -1
|
@@ -1,6 +1,42 @@
|
|
|
1
|
-
import { createInlineTickController } from '../base';
|
|
2
|
-
import { fromEvent, Subject, takeUntil } from 'rxjs';
|
|
1
|
+
import { createInlineTickController, IRendererEntity } from '../base';
|
|
2
|
+
import { animationFrameScheduler, fromEvent, of, Subject, takeUntil } from 'rxjs';
|
|
3
3
|
import Stats from 'stats.js';
|
|
4
|
+
import { repeat } from 'rxjs/operators';
|
|
5
|
+
import { PerformanceMeterEntity } from './performance-meter.entity';
|
|
6
|
+
const snapshotEqual = (a, b) => {
|
|
7
|
+
if (a.timeScale !== b.timeScale)
|
|
8
|
+
return false;
|
|
9
|
+
if (a.performanceStatsEnabled !== b.performanceStatsEnabled)
|
|
10
|
+
return false;
|
|
11
|
+
if (a.renderers.length !== b.renderers.length)
|
|
12
|
+
return false;
|
|
13
|
+
for (let i = 0; i < a.renderers.length; i++) {
|
|
14
|
+
if (a.renderers[i].name !== b.renderers[i].name)
|
|
15
|
+
return false;
|
|
16
|
+
if (a.renderers[i].entity !== b.renderers[i].entity)
|
|
17
|
+
return false;
|
|
18
|
+
if (a.renderers[i].physicsDebugViewActive !== b.renderers[i].physicsDebugViewActive)
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
const performanceStatsSnapshotEqual = (a, b) => {
|
|
24
|
+
if (!!a !== !!b)
|
|
25
|
+
return false;
|
|
26
|
+
if (a && b) {
|
|
27
|
+
if (a.totalTime !== b.totalTime)
|
|
28
|
+
return false;
|
|
29
|
+
if (a.entries.length !== b.entries.length)
|
|
30
|
+
return false;
|
|
31
|
+
for (let i = 0; i < a.entries.length; i++) {
|
|
32
|
+
if (a.entries[i][0] !== b.entries[i][0])
|
|
33
|
+
return false;
|
|
34
|
+
if (a.entries[i][1] !== b.entries[i][1])
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
4
40
|
export class GgDebuggerUI {
|
|
5
41
|
constructor() {
|
|
6
42
|
this.ui = {
|
|
@@ -10,6 +46,15 @@ export class GgDebuggerUI {
|
|
|
10
46
|
this.statsRemoved$ = new Subject();
|
|
11
47
|
this.currentWorld = null;
|
|
12
48
|
this.debugControlsRemoved$ = new Subject();
|
|
49
|
+
this.viewUpdated$ = new Subject();
|
|
50
|
+
this.snapshot = {
|
|
51
|
+
timeScale: 1,
|
|
52
|
+
renderers: [],
|
|
53
|
+
performanceStatsEnabled: false,
|
|
54
|
+
};
|
|
55
|
+
this.perfStatsMode = 'AVG';
|
|
56
|
+
this.performanceStatsSnapshot = null;
|
|
57
|
+
this.css = "style='display:flex;align-items:center;margin:0.25rem;'";
|
|
13
58
|
}
|
|
14
59
|
get showStats() {
|
|
15
60
|
return !!this.ui.stats;
|
|
@@ -31,12 +76,12 @@ export class GgDebuggerUI {
|
|
|
31
76
|
stats.dom.style.right = '0';
|
|
32
77
|
stats.showPanel(0); // 0: fps, 1: ms, 2: mb
|
|
33
78
|
document.body.appendChild(stats.dom);
|
|
34
|
-
createInlineTickController(selectedWorld, -1)
|
|
79
|
+
createInlineTickController(selectedWorld, -1, 'fps_meter_init')
|
|
35
80
|
.pipe(takeUntil(this.statsRemoved$))
|
|
36
81
|
.subscribe(() => {
|
|
37
82
|
stats === null || stats === void 0 ? void 0 : stats.begin();
|
|
38
83
|
});
|
|
39
|
-
createInlineTickController(selectedWorld, 10000)
|
|
84
|
+
createInlineTickController(selectedWorld, 10000, 'fps_meter')
|
|
40
85
|
.pipe(takeUntil(this.statsRemoved$))
|
|
41
86
|
.subscribe(() => {
|
|
42
87
|
stats === null || stats === void 0 ? void 0 : stats.end();
|
|
@@ -64,48 +109,175 @@ export class GgDebuggerUI {
|
|
|
64
109
|
this.currentWorld = selectedWorld;
|
|
65
110
|
if (value) {
|
|
66
111
|
const debugControlsContainer = document.createElement('div');
|
|
112
|
+
document.body.appendChild(debugControlsContainer);
|
|
67
113
|
this.ui.debugControlsContainer = debugControlsContainer;
|
|
68
|
-
const debugLabelCss = "style='display:flex;align-items:center;margin:0.25rem;'";
|
|
69
114
|
debugControlsContainer.style.cssText =
|
|
70
115
|
'position:fixed;top:48px;right:0;opacity:0.9;z-index:9999;background-color:#333;color:white;display:flex;flex-direction:column';
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
116
|
+
this.snapshot = this.makeSnapshot();
|
|
117
|
+
this.performanceStatsSnapshot = this.makePerformanceStatsSnapshot();
|
|
118
|
+
this.renderControls(debugControlsContainer);
|
|
119
|
+
this.renderPerformanceStats();
|
|
120
|
+
of(undefined, animationFrameScheduler)
|
|
121
|
+
.pipe(repeat(), takeUntil(this.debugControlsRemoved$))
|
|
122
|
+
.subscribe(() => {
|
|
123
|
+
const newSnapshot = this.makeSnapshot();
|
|
124
|
+
if (!snapshotEqual(this.snapshot, newSnapshot)) {
|
|
125
|
+
this.snapshot = newSnapshot;
|
|
126
|
+
this.renderControls(debugControlsContainer);
|
|
127
|
+
}
|
|
128
|
+
const newPerformanceStats = this.makePerformanceStatsSnapshot();
|
|
129
|
+
if (!performanceStatsSnapshotEqual(this.performanceStatsSnapshot, newPerformanceStats)) {
|
|
130
|
+
this.performanceStatsSnapshot = newPerformanceStats;
|
|
131
|
+
this.renderPerformanceStats();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
this.debugControlsRemoved$.next();
|
|
137
|
+
document.body.removeChild(this.ui.debugControlsContainer);
|
|
138
|
+
this.ui.debugControlsContainer = null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
makeSnapshot() {
|
|
142
|
+
const renderers = [];
|
|
143
|
+
let performanceMeter = null;
|
|
144
|
+
for (const e of this.currentWorld.children) {
|
|
145
|
+
if (e instanceof IRendererEntity) {
|
|
146
|
+
renderers.push(e);
|
|
147
|
+
}
|
|
148
|
+
else if (e instanceof PerformanceMeterEntity) {
|
|
149
|
+
performanceMeter = e;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
timeScale: this.currentWorld.worldClock.timeScale,
|
|
154
|
+
renderers: renderers.map(r => ({
|
|
155
|
+
name: r.name,
|
|
156
|
+
entity: r,
|
|
157
|
+
physicsDebugViewActive: r.physicsDebugViewActive,
|
|
158
|
+
})),
|
|
159
|
+
performanceStatsEnabled: !!performanceMeter,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
makePerformanceStatsSnapshot() {
|
|
163
|
+
if (!this.snapshot.performanceStatsEnabled)
|
|
164
|
+
return null;
|
|
165
|
+
let performanceMeter = this.currentWorld.children.find(e => e instanceof PerformanceMeterEntity);
|
|
166
|
+
if (performanceMeter) {
|
|
167
|
+
if (this.perfStatsMode === 'AVG') {
|
|
168
|
+
return performanceMeter.avgReport;
|
|
169
|
+
}
|
|
170
|
+
else if (this.perfStatsMode === 'PEAK') {
|
|
171
|
+
return performanceMeter.peakReport;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
renderControls(debugControlsContainer) {
|
|
177
|
+
let html = '';
|
|
178
|
+
for (const { entity, physicsDebugViewActive } of this.snapshot.renderers) {
|
|
179
|
+
html += `
|
|
180
|
+
<div ${this.css}>
|
|
181
|
+
<input type='checkbox' name='checkbox' id='physics_debugger_checkbox_id_${entity.name}' value='1'${physicsDebugViewActive ? ' checked' : ''}>
|
|
182
|
+
<label for='physics_debugger_checkbox_id_${entity.name}' style='user-select: none;'>Physics debugger${this.snapshot.renderers.length > 1 ? ' (' + entity.name + ')' : ''}</label>
|
|
75
183
|
</div>`;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
184
|
+
}
|
|
185
|
+
html += `
|
|
186
|
+
<div ${this.css}>
|
|
187
|
+
<input id='time_scale_slider' type='range' min='0' max='5' step='0.01' style='flex-grow:1' value='${this.snapshot.timeScale}'/>
|
|
188
|
+
<label for='time_scale_slider' style='user-select: none;'>Time scale</label>
|
|
189
|
+
</div>`;
|
|
190
|
+
html +=
|
|
191
|
+
`
|
|
192
|
+
<div ${this.css}>
|
|
193
|
+
<input type='checkbox' name='checkbox' id='perf_stats_checkbox_id' value='1'${this.snapshot.performanceStatsEnabled ? ' checked' : ''}>
|
|
194
|
+
<label for='perf_stats_checkbox_id' style='user-select: none;'>Entities performance distribution</label>
|
|
195
|
+
</div>
|
|
196
|
+
` +
|
|
197
|
+
(this.snapshot.performanceStatsEnabled
|
|
198
|
+
? `<div ${this.css}>
|
|
199
|
+
<label for='per_mode'>Mode:</label>
|
|
200
|
+
<select id='per_mode'>
|
|
201
|
+
<option value='AVG' ${this.perfStatsMode === 'AVG' ? 'selected' : ''}>Average</option>
|
|
202
|
+
<option value='PEAK' ${this.perfStatsMode === 'PEAK' ? 'selected' : ''}>Peak</option>
|
|
203
|
+
</select>
|
|
204
|
+
</div>`
|
|
205
|
+
: '') +
|
|
206
|
+
`
|
|
207
|
+
<div style='display: contents' id='perf_stats_container'></div>`;
|
|
208
|
+
debugControlsContainer.innerHTML = html;
|
|
209
|
+
debugControlsContainer.style.minWidth = '25rem';
|
|
210
|
+
this.viewUpdated$.next();
|
|
211
|
+
for (const { entity } of this.snapshot.renderers) {
|
|
212
|
+
fromEvent(document.getElementById('physics_debugger_checkbox_id_' + entity.name), 'change')
|
|
213
|
+
.pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
|
|
85
214
|
.subscribe(e => {
|
|
86
215
|
try {
|
|
87
|
-
|
|
216
|
+
entity.physicsDebugViewActive = e.target.checked;
|
|
88
217
|
}
|
|
89
218
|
catch (err) {
|
|
90
219
|
console.error(err);
|
|
91
220
|
}
|
|
92
|
-
e.target.checked = this.currentWorld.physicsDebugViewActive;
|
|
93
221
|
});
|
|
94
|
-
// fromEvent(document.getElementById('time_scale_slider')! as HTMLInputElement, 'change')
|
|
95
|
-
// .pipe(takeUntil(this.debugControlsRemoved$))
|
|
96
|
-
// .subscribe(e => {
|
|
97
|
-
// try {
|
|
98
|
-
// this.currentWorld.physicsWorld.timeScale = +(e.target as HTMLInputElement).value;
|
|
99
|
-
// } catch (err) {
|
|
100
|
-
// console.error(err);
|
|
101
|
-
// }
|
|
102
|
-
// (e.target as HTMLInputElement).value = '' + ( this.currentWorld.physicsWorld.timeScale || 1);
|
|
103
|
-
// });
|
|
104
222
|
}
|
|
105
|
-
|
|
106
|
-
this.debugControlsRemoved
|
|
107
|
-
|
|
108
|
-
|
|
223
|
+
fromEvent(document.getElementById('time_scale_slider'), 'change')
|
|
224
|
+
.pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
|
|
225
|
+
.subscribe(e => {
|
|
226
|
+
try {
|
|
227
|
+
this.currentWorld.worldClock.timeScale = +e.target.value;
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
console.error(err);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
fromEvent(document.getElementById('perf_stats_checkbox_id'), 'change')
|
|
234
|
+
.pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
|
|
235
|
+
.subscribe(e => {
|
|
236
|
+
const entity = this.currentWorld.children.find(x => x instanceof PerformanceMeterEntity);
|
|
237
|
+
if (e.target.checked == !entity) {
|
|
238
|
+
if (entity) {
|
|
239
|
+
this.currentWorld.removeEntity(entity);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
this.currentWorld.addEntity(new PerformanceMeterEntity());
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
if (this.snapshot.performanceStatsEnabled) {
|
|
247
|
+
fromEvent(document.getElementById('per_mode'), 'change')
|
|
248
|
+
.pipe(takeUntil(this.debugControlsRemoved$), takeUntil(this.viewUpdated$))
|
|
249
|
+
.subscribe(e => {
|
|
250
|
+
this.perfStatsMode = e.target.value;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
renderPerformanceStats() {
|
|
255
|
+
const pCss = "style='display:flex;align-items:center;margin:0.25rem;max-width:25rem'";
|
|
256
|
+
const em = document.getElementById('perf_stats_container');
|
|
257
|
+
if (!em)
|
|
258
|
+
return;
|
|
259
|
+
let html = '';
|
|
260
|
+
if (this.performanceStatsSnapshot) {
|
|
261
|
+
const total = this.performanceStatsSnapshot.totalTime;
|
|
262
|
+
const labelCss = `style='user-select:none;width:3.5rem;text-align:left;flex-shrink:0;'`;
|
|
263
|
+
const progressCss = `style='width:11.5rem;margin:4px;flex-shrink:0;'`;
|
|
264
|
+
const spanCss = `style='user-select:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;width:calc(9.5rem - 8px);'`;
|
|
265
|
+
html += `
|
|
266
|
+
<div ${pCss}>
|
|
267
|
+
<label for='stat_progress' ${labelCss}>${total.toFixed(2)}ms</label>
|
|
268
|
+
<progress id="stat_progress" ${progressCss} max="16" value="${total}"></progress>
|
|
269
|
+
<span ${spanCss}>Total frame time</span>
|
|
270
|
+
</div>`;
|
|
271
|
+
for (const [i, [name, value]] of this.performanceStatsSnapshot.entries.entries()) {
|
|
272
|
+
const strVal = this.perfStatsMode == 'AVG' ? ((value * 100) / total).toFixed(2) + '%' : value.toFixed(2) + 'ms';
|
|
273
|
+
html += `
|
|
274
|
+
<div ${pCss}>
|
|
275
|
+
<label for='stat_progress_${i}' ${labelCss}>${strVal}</label>
|
|
276
|
+
<progress id='stat_progress_${i}' ${progressCss} max='${total}' value='${value}'></progress>
|
|
277
|
+
<span ${spanCss}>${name}</span>
|
|
278
|
+
</div>`;
|
|
279
|
+
}
|
|
109
280
|
}
|
|
281
|
+
em.innerHTML = html;
|
|
110
282
|
}
|
|
111
283
|
}
|
package/dist/dev/gg-static.d.ts
CHANGED
package/dist/dev/gg-static.js
CHANGED
|
@@ -30,13 +30,13 @@ export class GgStatic {
|
|
|
30
30
|
this.consoleCommands = new Map();
|
|
31
31
|
this.registerConsoleCommand(null, 'ls_commands', () => __awaiter(this, void 0, void 0, function* () {
|
|
32
32
|
return this.availableCommands
|
|
33
|
-
.map(([key, value]) => `<span style=
|
|
33
|
+
.map(([key, value]) => `<span style='color:yellow'>${key}</span>${value.doc ? '\t<span style="color:#aaa">// ' + value.doc + '</span>' : ''}`)
|
|
34
34
|
.sort()
|
|
35
35
|
.join('\n\n');
|
|
36
36
|
}), 'no args; print all available commands');
|
|
37
37
|
this.registerConsoleCommand(null, 'ls_worlds', () => __awaiter(this, void 0, void 0, function* () {
|
|
38
38
|
return GgWorld.documentWorlds
|
|
39
|
-
.map(w => (w === this.selectedWorld ? `<span style=
|
|
39
|
+
.map(w => (w === this.selectedWorld ? `<span style='color:lightgreen;'>* ${w.name}</span>` : ` ${w.name}`))
|
|
40
40
|
.join('\n');
|
|
41
41
|
}), 'no args; print all available worlds');
|
|
42
42
|
this.registerConsoleCommand(null, 'select_world', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -49,12 +49,20 @@ export class GgStatic {
|
|
|
49
49
|
}
|
|
50
50
|
return ((_a = this.selectedWorld) === null || _a === void 0 ? void 0 : _a.name) || 'null';
|
|
51
51
|
}), 'args: [string]; select world by name');
|
|
52
|
+
this.registerConsoleCommand(null, 'show_debugger', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
this.showDebugControls = ['1', 'true', '+'].includes(args[0]);
|
|
54
|
+
return '' + this.showDebugControls;
|
|
55
|
+
}), 'args: [0 or 1]; turn on/off debug panel. Default value is 0');
|
|
56
|
+
this.registerConsoleCommand(null, 'show_stats', (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
57
|
+
this.showStats = ['1', 'true', '+'].includes(args[0]);
|
|
58
|
+
return '' + this.showStats;
|
|
59
|
+
}), 'args: [0 or 1]; turn on/off stats. Default value is 0');
|
|
52
60
|
}
|
|
53
61
|
static get instance() {
|
|
54
|
-
if (!
|
|
55
|
-
|
|
62
|
+
if (!window.ggstatic) {
|
|
63
|
+
window.ggstatic = new GgStatic();
|
|
56
64
|
}
|
|
57
|
-
return
|
|
65
|
+
return window.ggstatic;
|
|
58
66
|
}
|
|
59
67
|
get devConsoleEnabled() {
|
|
60
68
|
return this._devConsoleEnabled;
|
|
@@ -126,16 +134,15 @@ export class GgStatic {
|
|
|
126
134
|
if (!action) {
|
|
127
135
|
action = (this.consoleCommands.get(this.selectedWorld) || {})[command];
|
|
128
136
|
if (!action) {
|
|
129
|
-
return `<span style=
|
|
137
|
+
return `<span style='color:red'>Unrecognized command: ${command}</span>`;
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
try {
|
|
133
141
|
return yield action.handler(...args);
|
|
134
142
|
}
|
|
135
143
|
catch (err) {
|
|
136
|
-
return `<span style=
|
|
144
|
+
return `<span style='color:red'>${err}</span>`;
|
|
137
145
|
}
|
|
138
146
|
});
|
|
139
147
|
}
|
|
140
148
|
}
|
|
141
|
-
window.ggstatic = GgStatic.instance;
|
package/dist/dev/index.d.ts
CHANGED
package/dist/dev/index.js
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GgWorld, IEntity, TickOrder } from '../base';
|
|
2
|
+
export declare class PerformanceMeterEntity extends IEntity {
|
|
3
|
+
private readonly maxSamples;
|
|
4
|
+
private readonly maxRows;
|
|
5
|
+
readonly tickOrder: TickOrder | number;
|
|
6
|
+
private sampleIndex;
|
|
7
|
+
private collectedData;
|
|
8
|
+
private _avgReport;
|
|
9
|
+
get avgReport(): {
|
|
10
|
+
totalTime: number;
|
|
11
|
+
entries: [string, number][];
|
|
12
|
+
};
|
|
13
|
+
private _peakReport;
|
|
14
|
+
get peakReport(): {
|
|
15
|
+
totalTime: number;
|
|
16
|
+
entries: [string, number][];
|
|
17
|
+
};
|
|
18
|
+
constructor(maxSamples?: number, maxRows?: number);
|
|
19
|
+
onSpawned(world: GgWorld<unknown, unknown>): void;
|
|
20
|
+
onRemoved(): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { IEntity } from '../base';
|
|
2
|
+
import { takeUntil } from 'rxjs';
|
|
3
|
+
export class PerformanceMeterEntity extends IEntity {
|
|
4
|
+
constructor(maxSamples = 60, maxRows = 15) {
|
|
5
|
+
super();
|
|
6
|
+
this.maxSamples = maxSamples;
|
|
7
|
+
this.maxRows = maxRows;
|
|
8
|
+
this.tickOrder = Number.MAX_SAFE_INTEGER;
|
|
9
|
+
this.sampleIndex = 0;
|
|
10
|
+
this.collectedData = new Map();
|
|
11
|
+
this._avgReport = { totalTime: 0, entries: [] };
|
|
12
|
+
this._peakReport = { totalTime: 0, entries: [] };
|
|
13
|
+
}
|
|
14
|
+
get avgReport() {
|
|
15
|
+
return this._avgReport;
|
|
16
|
+
}
|
|
17
|
+
get peakReport() {
|
|
18
|
+
return this._peakReport;
|
|
19
|
+
}
|
|
20
|
+
onSpawned(world) {
|
|
21
|
+
super.onSpawned(world);
|
|
22
|
+
this.sampleIndex = 0;
|
|
23
|
+
const now = () => (typeof performance === 'undefined' ? Date : performance).now();
|
|
24
|
+
this.world.tickForwardTo$.pipe(takeUntil(this.onRemoved$)).subscribe(e => {
|
|
25
|
+
if (e === this)
|
|
26
|
+
return;
|
|
27
|
+
if (!this.collectedData.has(e)) {
|
|
28
|
+
this.collectedData.set(e, [[this.sampleIndex, now(), 0]]);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
this.collectedData.get(e).push([this.sampleIndex, now(), 0]);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
this.world.tickForwardedTo$.pipe(takeUntil(this.onRemoved$)).subscribe(e => {
|
|
35
|
+
if (e === this)
|
|
36
|
+
return;
|
|
37
|
+
const report = this.collectedData.get(e);
|
|
38
|
+
report[report.length - 1][2] = now();
|
|
39
|
+
});
|
|
40
|
+
this.tick$.pipe(takeUntil(this.onRemoved$)).subscribe(() => {
|
|
41
|
+
this.sampleIndex++;
|
|
42
|
+
// remove oldest samples
|
|
43
|
+
const entries = Array.from(this.collectedData.entries());
|
|
44
|
+
for (const [k, v] of entries) {
|
|
45
|
+
while (v.length > 0 && v[0][0] < this.sampleIndex - this.maxSamples) {
|
|
46
|
+
v.shift();
|
|
47
|
+
}
|
|
48
|
+
if (v.length === 0) {
|
|
49
|
+
this.collectedData.delete(k);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// helper functions
|
|
53
|
+
const label = (e) => {
|
|
54
|
+
if (e instanceof IEntity) {
|
|
55
|
+
if (e.name === '') {
|
|
56
|
+
return e.constructor.name;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
return e.name;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (e === 'PHYSICS_WORLD') {
|
|
63
|
+
return 'Physics simulation';
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
return e;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const truncateReport = (report) => {
|
|
70
|
+
let restSum = 0;
|
|
71
|
+
let restAmount = 0;
|
|
72
|
+
for (restAmount; restAmount < report.length - 2; restAmount++) {
|
|
73
|
+
if (this.maxRows > report.length - restAmount + 1) {
|
|
74
|
+
restAmount--;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
restSum += report[report.length - restAmount - 1][1];
|
|
78
|
+
}
|
|
79
|
+
if (restAmount > 1) {
|
|
80
|
+
report = report.slice(0, report.length - restAmount);
|
|
81
|
+
report.push([`Rest (${restAmount})`, restSum]);
|
|
82
|
+
}
|
|
83
|
+
return report;
|
|
84
|
+
};
|
|
85
|
+
// build avg report
|
|
86
|
+
let totalTimeAvg = 0;
|
|
87
|
+
let avgReport = Array.from(this.collectedData.entries())
|
|
88
|
+
.map(([e, samples]) => {
|
|
89
|
+
const timeSpentAvg = samples.reduce((prev, [_, start, finish]) => prev + finish - start, 0) / this.maxSamples;
|
|
90
|
+
totalTimeAvg += timeSpentAvg;
|
|
91
|
+
return [label(e), timeSpentAvg];
|
|
92
|
+
})
|
|
93
|
+
.sort((a, b) => b[1] - a[1]);
|
|
94
|
+
avgReport = truncateReport(avgReport);
|
|
95
|
+
this._avgReport = { totalTime: totalTimeAvg, entries: avgReport };
|
|
96
|
+
// build peak report
|
|
97
|
+
let totalTimePeaks = new Map();
|
|
98
|
+
let peakReport = Array.from(this.collectedData.entries())
|
|
99
|
+
.map(([e, samples]) => {
|
|
100
|
+
let peakTime = 0;
|
|
101
|
+
for (const [sample, start, finish] of samples) {
|
|
102
|
+
const time = finish - start;
|
|
103
|
+
peakTime = Math.max(peakTime, time);
|
|
104
|
+
totalTimePeaks.set(sample, time + (totalTimePeaks.get(sample) || 0));
|
|
105
|
+
}
|
|
106
|
+
return [label(e), peakTime];
|
|
107
|
+
})
|
|
108
|
+
.sort((a, b) => b[1] - a[1]);
|
|
109
|
+
peakReport = truncateReport(peakReport);
|
|
110
|
+
let totalPeak = Array.from(totalTimePeaks.values()).reduce((prev, cur) => Math.max(prev, cur), 0);
|
|
111
|
+
this._peakReport = { totalTime: totalPeak, entries: peakReport };
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
onRemoved() {
|
|
115
|
+
super.onRemoved();
|
|
116
|
+
this.collectedData = new Map();
|
|
117
|
+
this._avgReport = { totalTime: 0, entries: [] };
|
|
118
|
+
this._peakReport = { totalTime: 0, entries: [] };
|
|
119
|
+
}
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { IDisplayObjectComponent } from '../components/rendering/i-display-object.component';
|
|
2
|
-
import { Point3 } from '../models/points';
|
|
3
|
-
import { VisualTypeDocRepo } from '../gg-world';
|
|
4
|
-
export interface IDebugPhysicsDrawer<D, R, TypeDoc extends VisualTypeDocRepo<D, R> = VisualTypeDocRepo<D, R>> extends IDisplayObjectComponent<D, R, TypeDoc> {
|
|
5
|
-
drawContactPoint(point: D, normal: D, color?: Point3): void;
|
|
6
|
-
drawLine(from: D, to: D, color?: Point3): void;
|
|
7
|
-
update(): void;
|
|
8
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|