mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-22 10:05:37 -04:00
227 lines
6.4 KiB
TypeScript
227 lines
6.4 KiB
TypeScript
namespace gdjs {
|
|
/**
|
|
* @category Debugging > Profiler
|
|
*/
|
|
export type ProfilerStats = {
|
|
framesCount: integer;
|
|
};
|
|
|
|
/**
|
|
* @category Debugging > Profiler
|
|
*/
|
|
export type FrameMeasure = {
|
|
parent: FrameMeasure | null;
|
|
time: float;
|
|
lastStartTime: float;
|
|
subsections: Record<string, FrameMeasure>;
|
|
};
|
|
|
|
/**
|
|
* A basic profiling tool that can be used to measure time spent in sections of the engine.
|
|
* @category Debugging > Profiler
|
|
*/
|
|
export class Profiler {
|
|
/** All the measures for the last frames */
|
|
_framesMeasures: Array<FrameMeasure> = [];
|
|
|
|
_currentFrameIndex: float = 0;
|
|
|
|
/** The measures being done */
|
|
_currentFrameMeasure: FrameMeasure = {
|
|
parent: null,
|
|
time: 0,
|
|
lastStartTime: 0,
|
|
subsections: {},
|
|
};
|
|
|
|
/** The section being measured */
|
|
_currentSection: FrameMeasure | null = null;
|
|
|
|
_maxFramesCount: number = 600;
|
|
|
|
/** The number of frames that have been measured */
|
|
_framesCount: number = 0;
|
|
|
|
/** A function to get the current time. If available, corresponds to performance.now(). */
|
|
_getTimeNow: () => float;
|
|
|
|
constructor() {
|
|
while (this._framesMeasures.length < this._maxFramesCount) {
|
|
this._framesMeasures.push({
|
|
parent: null,
|
|
time: 0,
|
|
lastStartTime: 0,
|
|
subsections: {},
|
|
});
|
|
}
|
|
this._getTimeNow =
|
|
window.performance && typeof window.performance.now === 'function'
|
|
? window.performance.now.bind(window.performance)
|
|
: Date.now;
|
|
}
|
|
|
|
beginFrame(): void {
|
|
this._currentFrameMeasure = {
|
|
parent: null,
|
|
time: 0,
|
|
lastStartTime: this._getTimeNow(),
|
|
subsections: {},
|
|
};
|
|
this._currentSection = this._currentFrameMeasure;
|
|
}
|
|
|
|
begin(sectionName: string): void {
|
|
if (this._currentSection === null)
|
|
throw new Error(
|
|
'Impossible to call Profiler.begin() when not profiling a frame!'
|
|
);
|
|
|
|
// Push the new section
|
|
const subsections = this._currentSection.subsections;
|
|
const subsection = (subsections[sectionName] = subsections[
|
|
sectionName
|
|
] || {
|
|
parent: this._currentSection,
|
|
time: 0,
|
|
lastStartTime: 0,
|
|
subsections: {},
|
|
});
|
|
this._currentSection = subsection;
|
|
|
|
// Start the timer
|
|
this._currentSection.lastStartTime = this._getTimeNow();
|
|
}
|
|
|
|
end(sectionName?: string): void {
|
|
if (this._currentSection === null)
|
|
throw new Error(
|
|
'Impossible to call Profiler.end() when not profiling a frame!'
|
|
);
|
|
|
|
// Stop the timer
|
|
const sectionTime =
|
|
this._getTimeNow() - this._currentSection.lastStartTime;
|
|
this._currentSection.time =
|
|
(this._currentSection.time || 0) + sectionTime;
|
|
|
|
// Pop the section
|
|
if (this._currentSection.parent !== null)
|
|
this._currentSection = this._currentSection.parent;
|
|
}
|
|
|
|
endFrame(): void {
|
|
if (this._currentSection === null)
|
|
throw new Error(
|
|
'Impossible to end profiling a frame when profiling has not started a frame!'
|
|
);
|
|
if (this._currentSection.parent !== null) {
|
|
throw new Error(
|
|
'Mismatch in profiler, endFrame should be called on root section'
|
|
);
|
|
}
|
|
this.end();
|
|
this._framesCount++;
|
|
if (this._framesCount > this._maxFramesCount) {
|
|
this._framesCount = this._maxFramesCount;
|
|
}
|
|
this._framesMeasures[this._currentFrameIndex] = this
|
|
._currentFrameMeasure as FrameMeasure;
|
|
this._currentFrameIndex++;
|
|
if (this._currentFrameIndex >= this._maxFramesCount) {
|
|
this._currentFrameIndex = 0;
|
|
}
|
|
}
|
|
|
|
static _addAverageSectionTimes(
|
|
section: FrameMeasure,
|
|
destinationSection: FrameMeasure,
|
|
totalCount: integer,
|
|
i: integer
|
|
): void {
|
|
destinationSection.time =
|
|
(destinationSection.time || 0) + section.time / totalCount;
|
|
for (const sectionName in section.subsections) {
|
|
if (section.subsections.hasOwnProperty(sectionName)) {
|
|
const destinationSubsections = destinationSection.subsections;
|
|
const destinationSubsection = (destinationSubsections[sectionName] =
|
|
destinationSubsections[sectionName] || {
|
|
parent: destinationSection,
|
|
time: 0,
|
|
subsections: {},
|
|
});
|
|
Profiler._addAverageSectionTimes(
|
|
section.subsections[sectionName],
|
|
destinationSubsection,
|
|
totalCount,
|
|
i
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the measures for all the section of the game during the frames
|
|
* captured.
|
|
*/
|
|
getFramesAverageMeasures(): FrameMeasure {
|
|
const framesAverageMeasures = {
|
|
parent: null,
|
|
time: 0,
|
|
lastStartTime: 0,
|
|
subsections: {},
|
|
};
|
|
for (let i = 0; i < this._framesCount; ++i) {
|
|
Profiler._addAverageSectionTimes(
|
|
this._framesMeasures[i],
|
|
framesAverageMeasures,
|
|
this._framesCount,
|
|
i
|
|
);
|
|
}
|
|
return framesAverageMeasures;
|
|
}
|
|
|
|
/**
|
|
* Get stats measured during the frames captured.
|
|
*/
|
|
getStats(): ProfilerStats {
|
|
return { framesCount: this._framesCount };
|
|
}
|
|
|
|
/**
|
|
* Convert measures for a section into texts.
|
|
* Useful for ingame profiling.
|
|
*
|
|
* @param sectionName The name of the section
|
|
* @param profilerSection The section measures
|
|
* @param outputs The array where to push the results
|
|
*/
|
|
static getProfilerSectionTexts(
|
|
sectionName: string,
|
|
profilerSection: any,
|
|
outputs: any
|
|
): void {
|
|
const percent =
|
|
profilerSection.parent && profilerSection.parent.time !== 0
|
|
? (
|
|
(profilerSection.time / profilerSection.parent.time) *
|
|
100
|
|
).toFixed(1)
|
|
: '100%';
|
|
const time = profilerSection.time.toFixed(2);
|
|
outputs.push(sectionName + ': ' + time + 'ms (' + percent + ')');
|
|
const subsectionsOutputs = [];
|
|
for (const subsectionName in profilerSection.subsections) {
|
|
if (profilerSection.subsections.hasOwnProperty(subsectionName)) {
|
|
Profiler.getProfilerSectionTexts(
|
|
subsectionName,
|
|
profilerSection.subsections[subsectionName],
|
|
subsectionsOutputs
|
|
);
|
|
}
|
|
}
|
|
outputs.push.apply(outputs, subsectionsOutputs);
|
|
}
|
|
}
|
|
}
|