174 lines
4.7 KiB
JavaScript
174 lines
4.7 KiB
JavaScript
|
/**
|
||
|
* A basic profiling tool that can be used to measure time spent in sections of the engine.
|
||
|
* @class Profiler
|
||
|
* @see gdjs.RuntimeGame
|
||
|
* @memberof gdjs
|
||
|
*/
|
||
|
gdjs.Profiler = function() {
|
||
|
this._framesMeasures = []; // All the measures for the last frames
|
||
|
this._currentFrameIndex = 0;
|
||
|
this._currentFrameMeasure = null; // The measures being done
|
||
|
this._currentSection = null; // The section being measured
|
||
|
|
||
|
this._maxFramesCount = 600;
|
||
|
this._framesCount = 0; // The number of frames that have been measured
|
||
|
while (this._framesMeasures.length < this._maxFramesCount) {
|
||
|
this._framesMeasures.push({
|
||
|
parent: null,
|
||
|
time: 0,
|
||
|
subsections: {},
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this._getTimeNow =
|
||
|
window.performance && typeof window.performance.now === 'function'
|
||
|
? window.performance.now.bind(window.performance)
|
||
|
: Date.now;
|
||
|
};
|
||
|
|
||
|
gdjs.Profiler.prototype.beginFrame = function() {
|
||
|
this._currentFrameMeasure = {
|
||
|
parent: null,
|
||
|
time: 0,
|
||
|
lastStartTime: this._getTimeNow(),
|
||
|
subsections: {},
|
||
|
};
|
||
|
this._currentSection = this._currentFrameMeasure;
|
||
|
};
|
||
|
|
||
|
gdjs.Profiler.prototype.begin = function(sectionName) {
|
||
|
// Push the new section
|
||
|
var subsections = this._currentSection.subsections;
|
||
|
var subsection = (subsections[sectionName] = subsections[sectionName] || {
|
||
|
parent: this._currentSection,
|
||
|
time: 0,
|
||
|
lastStartTime: 0,
|
||
|
subsections: {},
|
||
|
});
|
||
|
this._currentSection = subsection;
|
||
|
|
||
|
// Start the timer
|
||
|
this._currentSection.lastStartTime = this._getTimeNow();
|
||
|
};
|
||
|
|
||
|
gdjs.Profiler.prototype.end = function(sectionName) {
|
||
|
// Stop the timer
|
||
|
var sectionTime = this._getTimeNow() - this._currentSection.lastStartTime;
|
||
|
this._currentSection.time = (this._currentSection.time || 0) + sectionTime;
|
||
|
|
||
|
// Pop the section
|
||
|
this._currentSection = this._currentSection.parent;
|
||
|
};
|
||
|
|
||
|
gdjs.Profiler.prototype.endFrame = function() {
|
||
|
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;
|
||
|
this._currentFrameIndex++;
|
||
|
if (this._currentFrameIndex >= this._maxFramesCount)
|
||
|
this._currentFrameIndex = 0;
|
||
|
};
|
||
|
|
||
|
gdjs.Profiler._addAverageSectionTimes = function(
|
||
|
section,
|
||
|
destinationSection,
|
||
|
totalCount,
|
||
|
i
|
||
|
) {
|
||
|
destinationSection.time =
|
||
|
(destinationSection.time || 0) + section.time / totalCount;
|
||
|
for (var sectionName in section.subsections) {
|
||
|
if (section.subsections.hasOwnProperty(sectionName)) {
|
||
|
var destinationSubsections = destinationSection.subsections;
|
||
|
var destinationSubsection = (destinationSubsections[
|
||
|
sectionName
|
||
|
] = destinationSubsections[sectionName] || {
|
||
|
parent: destinationSection,
|
||
|
time: 0,
|
||
|
subsections: {},
|
||
|
});
|
||
|
|
||
|
gdjs.Profiler._addAverageSectionTimes(
|
||
|
section.subsections[sectionName],
|
||
|
destinationSubsection,
|
||
|
totalCount,
|
||
|
i
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return the measures for all the section of the game during the frames
|
||
|
* captured.
|
||
|
*/
|
||
|
gdjs.Profiler.prototype.getFramesAverageMeasures = function() {
|
||
|
var framesAverageMeasures = {
|
||
|
parent: null,
|
||
|
time: 0,
|
||
|
subsections: {},
|
||
|
};
|
||
|
|
||
|
for (var i = 0; i < this._framesCount; ++i) {
|
||
|
gdjs.Profiler._addAverageSectionTimes(
|
||
|
this._framesMeasures[i],
|
||
|
framesAverageMeasures,
|
||
|
this._framesCount,
|
||
|
i
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return framesAverageMeasures;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Get stats measured during the frames captured.
|
||
|
*/
|
||
|
gdjs.Profiler.prototype.getStats = function() {
|
||
|
return {
|
||
|
framesCount: this._framesCount,
|
||
|
};
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Convert measures for a section into texts.
|
||
|
* Useful for ingame profiling.
|
||
|
*
|
||
|
* @param {string} sectionName The name of the section
|
||
|
* @param {s} profilerSection The section measures
|
||
|
* @param {*} outputs The array where to push the results
|
||
|
*/
|
||
|
gdjs.Profiler.getProfilerSectionTexts = function(
|
||
|
sectionName,
|
||
|
profilerSection,
|
||
|
outputs
|
||
|
) {
|
||
|
var percent =
|
||
|
profilerSection.parent && profilerSection.parent.time !== 0
|
||
|
? ((profilerSection.time / profilerSection.parent.time) * 100).toFixed(1)
|
||
|
: '100%';
|
||
|
var time = profilerSection.time.toFixed(2);
|
||
|
outputs.push(sectionName + ': ' + time + 'ms (' + percent + ')');
|
||
|
var subsectionsOutputs = [];
|
||
|
|
||
|
for (var subsectionName in profilerSection.subsections) {
|
||
|
if (profilerSection.subsections.hasOwnProperty(subsectionName)) {
|
||
|
gdjs.Profiler.getProfilerSectionTexts(
|
||
|
subsectionName,
|
||
|
profilerSection.subsections[subsectionName],
|
||
|
subsectionsOutputs
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
outputs.push.apply(outputs, subsectionsOutputs);
|
||
|
};
|