Files
gtav-src/tools_ng/web/cert/capture_stats/js/report.js
T
2025-09-29 00:52:08 +02:00

778 lines
19 KiB
JavaScript
Executable File

var maxResults = 10,
valueLimit = 0.5,
diffLimit = 0.025,
restUrl,
legendText,
svgHeight = 600,
navPosition = 1,
xAxisLabel,
reportTypeValue;
//Array to store the fps graph data
var fpsGraphArrays;
function initPage() {
initHeaderAndFilters(null); // no header
populatePerfBuilds();
populatePerfChangelists();
$("#sub-header select#cs-changelist").change(function () {
if ($("#sub-header input[name=report-type-radio]:radio:checked").val() == "Changelist")
generateReport();
});
$("#sub-header select#cs-build").change(function () {
if ($("#sub-header input[name=report-type-radio]:radio:checked").val() == "Build")
generateReport();
});
$("#sub-header input[name=report-type-radio]:radio").change(function () {
generateReport();
});
generateReport();
}
function updateReportInputs() {
fpsGraphArrays = []
var urlVars = getUrlVars();
if (urlVars.hasOwnProperty("Changelist"))
setReportVars("Changelist", urlVars["Changelist"]);
else if (urlVars.hasOwnProperty("Build"))
setReportVars("Build", urlVars["Build"]);
else if ($("#sub-header input[name=report-type-radio]:radio:checked").val() == "Changelist") {
setReportVars("Changelist", $("#cs-changelist").val())
}
else if ($("#sub-header input[name=report-type-radio]:radio:checked").val() == "Build") {
setReportVars("Build", $("#cs-build").val())
}
else
return;
}
function setReportVars(reportType, metricValue) {
var restSubUrl;
if (reportType == "Changelist")
restSubUrl = config.captureStatsChangelistPath;
else if (reportType == "Build")
restSubUrl = config.captureStatsBuildsPath;
restUrl = config.restHost + config.captureStats
+ restSubUrl
+ metricValue + "/"
+ config.captureStatsZonesPath;
legendText = "Capture Stats :: Per " + reportType + " Report #" + metricValue;
xAxisLabel = reportType;
reportTypeValue = metricValue;
}
function generateReport() {
$("#report").empty();
updateReportInputs();
if (!reportTypeValue)
return;
$("#report")
.append(
$("<legend />")
.append(
$("<span />")
.text(legendText)
)
);
block();
$.ajax({
url: restUrl,
type: "GET",
dataType: "json",
data: {},
success: function(jsonZones, textStatus, jqXHR) {
var fpsGraphId = "fps_graph";
$("#report")
.append(
$("<fieldset />")
.attr("id", fpsGraphId)
.addClass("report-graph")
.append(
$("<legend />")
.html("Average FPS (Click to render)")
)
.click(function() {
if ($(this).find("svg").size() == 0) {
initSvg(fpsGraphId, svgHeight);
drawLinegraph(fpsGraphArrays, fpsGraphId, xAxisLabel, "Average FPS");
generateGraphTable(fpsGraphArrays, fpsGraphId);
$(this).css("height", "auto");
$(this).find("svg").css("height", svgHeight);
//console.log(fpsGraphArrays);
}
})
);
// For each zone
$.each(jsonZones.Items, function(i, zone) {
generateZoneReport(zone.Hash, i+1);
});
repopulateNavigation(jsonZones.Items);
},
error: function (xhr, ajaxOptions, thrownError) {
Sexy.alert(this.url + "\n" + ajaxOptions + " " + xhr.status + " " + thrownError);
},
complete: function() {
unBlock();
}
});
} // end of generateReport()
function generateZoneReport(zoneHash, index) {
//block();
var fixedZoneName;
$.ajax({
url: restUrl + "/"
+ zoneHash + "/"
+ config.captureStatsHistoricalPath,
type: "GET",
dataType: "json",
async: false, // synchronise each zone job
data: {
maxResults: maxResults
},
success: function(jsonZone, textStatus, jqXHR) {
populateZoneTables(jsonZone, index);
},
error: function (xhr, ajaxOptions, thrownError) {
console.error("Failed loading content");
},
complete: function() {
//unBlock();
}
});
} // end of generateZoneReport()
function populateZoneTables(zone, index) {
var zoneTitle = index + ". " + zone.ZoneName;
var fixedZoneName = replaceNonWord(zone.ZoneName);
$("#report")
.append(
$("<table>")
.attr("id", index)
.append(
$("<tr>")
.append(
$("<th>")
.addClass("title")
.append(
$("<a/>")
.click(function(e) {
e.preventDefault();
$("#" + fixedZoneName).toggle("blind", {}, "slow");
})
.attr("title", zoneTitle)
.attr("href", "#")
.text(zoneTitle)
)
)
)
)
.append(
$("<fieldset />")
.attr("id", fixedZoneName)
//.addClass("collapsed")
.append(
$("<ul />")
.addClass("tab-nav")
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_fps")
.attr("title", "FPS")
.text("FPS")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_fps", {duration: "slow"})
})
)
)
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_threads")
.attr("title", "Threads")
.text("Threads")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_threads", {duration: "slow"})
})
)
)
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_Gpu")
.attr("title", "GPU")
.text("GPU")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_Gpu", {duration: "slow"})
})
)
)
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_Main", {duration: "slow"})
.attr("title", "Main")
.text("Main")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_Main", {duration: "slow"})
})
)
)
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_Render", {duration: "slow"})
.attr("title", "Render")
.text("Render")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_Render", {duration: "slow"})
})
)
)
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_gpu_graph")
.attr("title", "GPU Graph")
.text("GPU Graph")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_gpu_graph", {duration: "slow"})
})
)
)
.append(
$("<li />")
.append(
$("<a />")
.attr("href", "#" + fixedZoneName + "_cpu_graph", {duration: "slow"})
.attr("title", "Main Graph")
.text("Main Graph")
.click(function(e) {
e.preventDefault();
$.scrollTo("#" + fixedZoneName + "_cpu_graph", {duration: "slow"})
})
)
)
)
);
// FPS
var fpsCurrent = getLastValue(zone.FpsResults.Values),
fpsPrevious = getPenultimateValue(zone.FpsResults.Values),
fpsDiff = (fpsCurrent - fpsPrevious).toFixed(config.captureStatsDecimals);
fpsGraphArrays.push({
values: zone.FpsResults.Values,
color: config.colourRange(Math.random()*20),
key: zone.ZoneName,
});
$("#" + fixedZoneName)
.append(
$("<div />")
.attr("id", fixedZoneName + "_fps")
.append(
$("<table />")
.append(
$("<tr>")
.append(
$("<th>")
.addClass("subtitle")
.attr("colspan", 3)
.text("FPS")
)
)
.append(
$("<tr>")
.append(
$("<th>").text("Current")
)
.append(
$("<th>").text("Previous (std)")
)
.append(
$("<th>").text("Diff")
)
)
.append(
$("<tr>")
.append(
$("<td>").text(fpsCurrent)
)
.append(
$("<td>").text(
fpsPrevious
+ " ("
+ zone.FpsResults.StandardDeviation.toFixed(config.captureStatsDecimals)
+ ")"
)
)
.append(
$("<td>")
.css("color", getColour(fpsDiff))
.text(fpsDiff)
)
)
)
);
// End of FPS
// Threads Report
$("#" + fixedZoneName)
.append(
$("<div />")
.attr("id", fixedZoneName + "_threads")
.append(
$("<table />")
.addClass("title-only")
.append(
$("<tr>")
.append(
$("<th>")
.addClass("subtitle")
.attr("colspan", 4)
.text("Threads")
)
)
)
.append(
$("<table />")
//.attr("id", fixedZoneName + "_threads")
.addClass("tablesorter")
.append(
$("<thead>")
.append(
$("<tr>")
.append(
$("<th>").text("Item")
)
.append(
$("<th>").text("Current")
)
.append(
$("<th>").text("Previous (std)")
)
.append(
$("<th>").text("Diff")
)
)
)
)
);
$.each(zone.ThreadResults.Results, function(i, item) {
var threadCurrent = getLastValue(item.Value.Values),
threadPrevious = getPenultimateValue(item.Value.Values),
threadDiff = (threadCurrent - threadPrevious).toFixed(config.captureStatsDecimals);
//if ((threadCurrent <= valueLimit) || (threadPrevious <= valueLimit) || (Math.abs(threadDiff) <= diffLimit))
// return;
$("#" + fixedZoneName + "_threads " + " table.tablesorter")
.append(
$("<tr>")
.append(
$("<td>").text(item.Key)
)
.append(
$("<td>").text(threadCurrent)
)
.append(
$("<td>").text(
threadPrevious
+ " ("
+ item.Value.StandardDeviation.toFixed(config.captureStatsDecimals)
+ ")"
)
)
.append(
$("<td>")
.css("color", getColour(threadDiff))
.text(threadDiff)
)
);
});
if ($("#" + fixedZoneName + "_threads " + " table.tablesorter").find("tr").length > 1) {
$("#" + fixedZoneName + "_threads " + " table.tablesorter").tablesorter({
//Sort on the first column ASC
sortList: [[0, 0]]
});
}
// End of Threads
// CPU
var cpuGraphArrays = [];
var gpuGraphArrays = [];
$.each(zone.CpuResults.PerSetResults, function(i, timebarItem) {
$("#" + fixedZoneName)
.append(
$("<div />")
.attr("id", fixedZoneName + "_" + timebarItem.Key)
.append(
$("<table />")
.addClass("title-only")
.append(
$("<tr>")
.append(
$("<th>")
.addClass("subtitle")
.attr("colspan", 4)
.text(timebarItem.Key)
)
)
)
.append(
$("<table />")
.addClass("tablesorter")
.append(
$("<thead>")
.append(
$("<tr>")
.append(
$("<th>").text("Item")
)
.append(
$("<th>").text("Current")
)
.append(
$("<th>").text("Previous (std)")
)
.append(
$("<th>").text("Diff")
)
)
)
)
);
$.each(timebarItem.Value.PerMetricResults, function(i, metricItem) {
var metricCurrent = getLastValue(metricItem.Value.Values),
metricPrevious = getPenultimateValue(metricItem.Value.Values),
metricDiff = (metricCurrent - metricPrevious).toFixed(config.captureStatsDecimals);
if ((metricCurrent <= valueLimit) || (metricPrevious <= valueLimit) || (Math.abs(metricDiff) <= diffLimit))
return;
$("#" + fixedZoneName + "_" + timebarItem.Key + " table.tablesorter")
.append(
$("<tr>")
.append(
$("<td>").text(metricItem.Key)
)
.append(
$("<td>").text(metricCurrent)
)
.append(
$("<td>").text(
metricPrevious
+ " ("
+ metricItem.Value.StandardDeviation.toFixed(config.captureStatsDecimals)
+ ")"
)
)
.append(
$("<td>")
.css("color", getColour(metricDiff))
.text(metricDiff)
)
);
if (timebarItem.Key == "Gpu") {
var metricAvg = d3.mean(metricItem.Value.Values, function(d) {return d.Value});
gpuGraphArrays.push({
values: metricItem.Value.Values,
color: config.colourRange(Math.random()*20),
key: metricItem.Key + " (" + metricAvg.toFixed(config.captureStatsDecimals) + ")",
});
}
else if (timebarItem.Key == "Main") {
var metricAvg = d3.mean(metricItem.Value.Values, function(d) {return d.Value});
cpuGraphArrays.push({
values: metricItem.Value.Values,
color: config.colourRange(Math.random()*20),
key: metricItem.Key + " (" + metricAvg.toFixed(config.captureStatsDecimals) + ")",
});
}
}); // end of each perMetricResult
if ($("#" + fixedZoneName + "_" + timebarItem.Key + " table.tablesorter").find("tr").length > 1) {
$("#" + fixedZoneName + "_" + timebarItem.Key + " table.tablesorter").tablesorter({
// //Sort on the first column ASC
sortList: [[0, 0]]
});
}
//console.log(timebarItem.Key);
//console.log($("#" + fixedZoneName + "_" + timebarItem.Key + " table.tablesorter").find("tr").length);
}); // end of each cpu results
var gpuGraphId = fixedZoneName + "_gpu_graph";
$("#" + fixedZoneName)
.append(
$("<fieldset />")
.attr("id", gpuGraphId)
.addClass("report-graph")
.append(
$("<legend />")
.html("GPU Graph (Click to render)")
)
.click(function() {
if ($(this).find("svg").size() == 0) {
$(this).css("height", svgHeight);
initSvg(gpuGraphId, svgHeight);
drawLinegraph(gpuGraphArrays, gpuGraphId, xAxisLabel, "Average Time (ms)");
}
})
);
var cpuGraphId = fixedZoneName + "_cpu_graph";
$("#" + fixedZoneName)
.append(
$("<fieldset />")
.attr("id", cpuGraphId)
.addClass("report-graph")
.append(
$("<legend />")
.html("Main Graph (Click to render)")
)
.click(function() {
if ($(this).find("svg").size() == 0) {
$(this).css("height", svgHeight);
initSvg(cpuGraphId, svgHeight);
drawLinegraph(cpuGraphArrays, cpuGraphId, xAxisLabel, "Average Time (ms)");
}
})
);
}
function drawLinegraph(datum, htmlId, xLabel, yLabel) {
if (datum.length < 1)
return;
nv.addGraph(function() {
var chart = nv.models.lineChart()
//.x(function(d) { return d.Key; })
.x(function(d, i) { return i; })
.y(function(d) { return Number(d.Value); })
.margin({top: 50, right: 40, bottom: 100, left: 80})
.tooltips(true);
chart.xAxis
.axisLabel(xLabel)
//.tickValues(datum[0].values.map(function(d) {return d.Key; }))
//.tickFormat(function(d, i) {return d;})
.tickValues(d3.range(datum[0].values.length))
.tickFormat(function(d, i) {
return (datum[0].values[parseInt(d)] ? datum[0].values[parseInt(d)].Key : d);
})
.rotateLabels(-45);
chart.yAxis
.axisLabel(yLabel)
.tickFormat(d3.format(".02f"));
//chart.tooltipContent(function(key, x, y, e, graph) {
// return "<h3>" + key + "</h3>" +
// "<p>" + y + " at " + (new Date(x)).toDateString() + "</p>";
//});
d3.select("#" + htmlId + " svg")
.datum(datum)
.transition()
.duration(config.transitionDuration)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
}
function generateGraphTable(datum, elementId) {
if (datum.length < 1)
return;
var keys = {}; // save all the builds or the changelists found in the data
$.each(datum, function(i, zone) {
$.each(zone.values, function(i, zoneValue) {
if (!keys.hasOwnProperty(zoneValue.Key))
keys[zoneValue.Key] = null;
})
});
var table = $("<table />");
var trh = $("<tr />").append(
$("<th />")
.css("font-style", "italic")
.html(xAxisLabel)
);
$.each(keys, function(key) {
trh.append(
$("<th />")
.html(key)
)
});
table.append(trh);
$.each(datum, function(i, zone) {
var tr = $("<tr />");
tr.append(
$("<td />").html(zone.key)
)
$.each(keys, function(key) {
var zoneValue = "-";
$.each(zone.values, function(i, value) {
if (value.Key == key) {
zoneValue = value.Value;
}
});
tr.append(
$("<td />").html(zoneValue)
)
});
table.append(tr);
});
$("#" + elementId).append(table);
}
function getLastValue(array) {
return array[array.length - 1].Value.toFixed(config.captureStatsDecimals);
}
function getPenultimateValue(array) {
return ((array.length > 1) ? array[array.length - 2].Value : 0).toFixed(config.captureStatsDecimals);
}
function getColour(value) {
if (value < 0)
return "green";
else
return "red";
}
function replaceNonWord(string) {
return string.replace(/[\W]/gi, '_');
}
function repopulateNavigation(zones) {
if (navigationItems[navPosition].title != "Report")
console.error("Navigation item not found in position" + navPosition);
navigationItems[navPosition].href = "#";
navigationItems[navPosition].subItems = [];
$.each(zones, function(i, zone) {
navigationItems[navPosition].subItems.push(
{
title: (i+1) + ". " + zone.Name,
href: "#" + (i+1),
display: "inherit"
}
);
});
emptyNavigation();
populateNavigation(navigationItems);
var navElement = $("#navigation #nav-ul").children("li").get(navPosition);
$(navElement).children("a").addClass("active");
$(navElement).children("a").next().slideToggle();
$(navElement).find("li a").click(function(e) {
e.preventDefault();
$.scrollTo($(this).attr("href"), {duration: "slow"})
$(this).parent().parent().find("a").removeClass("active");
$(this).addClass("active");
});
}
function populatePerfBuilds() {
$.ajax({
url: config.restHost + config.captureStatsBuilds,
type: "GET",
dataType: "json",
data: {},
async: false,
success: function(builds, textStatus, jqXHR) {
$.each(builds, function(i, build) {
$("#cs-build").append(
$("<option />")
.text(build)
.val(build)
);
});
},
error: function (xhr, ajaxOptions, thrownError) {
console.error(this.url + "\n" + ajaxOptions + " " + xhr.status + " " + thrownError);
}
});
}
function populatePerfChangelists() {
$.ajax({
url: config.restHost + config.captureStatsChangelists,
type: "GET",
dataType: "json",
data: {},
async: false,
success: function(changelists, textStatus, jqXHR) {
$.each(changelists, function(i, changelist) {
$("#cs-changelist").append(
$("<option />")
.text(changelist)
.val(changelist)
);
});
},
error: function (xhr, ajaxOptions, thrownError) {
console.error(this.url + "\n" + ajaxOptions + " " + xhr.status + " " + thrownError);
}
});
}