From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../MotionMark/resources/debug-runner/graph.js | 615 +++++++++++++++++++++ 1 file changed, 615 insertions(+) create mode 100644 third_party/webkit/PerformanceTests/MotionMark/resources/debug-runner/graph.js (limited to 'third_party/webkit/PerformanceTests/MotionMark/resources/debug-runner/graph.js') diff --git a/third_party/webkit/PerformanceTests/MotionMark/resources/debug-runner/graph.js b/third_party/webkit/PerformanceTests/MotionMark/resources/debug-runner/graph.js new file mode 100644 index 0000000000..4803936219 --- /dev/null +++ b/third_party/webkit/PerformanceTests/MotionMark/resources/debug-runner/graph.js @@ -0,0 +1,615 @@ +Utilities.extendObject(window.benchmarkController, { + updateGraphData: function(testResult, testData, options) + { + var element = document.getElementById("test-graph-data"); + element.innerHTML = ""; + element._testResult = testResult; + element._options = options; + + var margins = new Insets(30, 30, 30, 40); + var size = Point.elementClientSize(element); + size.y = window.innerHeight - element.offsetTop; + size = size.subtract(margins.size); + + // Convert from compact JSON output to propertied data + var samplesWithProperties = {}; + [Strings.json.controller, Strings.json.complexity, Strings.json.complexityAverage].forEach(function(seriesName) { + var series = testData[Strings.json.samples][seriesName]; + samplesWithProperties[seriesName] = series.toArray(); + }) + + this.createTimeGraph(testResult, samplesWithProperties[Strings.json.controller], testData[Strings.json.marks], testData[Strings.json.controller], options, margins, size); + this.onTimeGraphOptionsChanged(); + + this._showOrHideNodes(true, "form[name=graph-type]"); + document.forms["graph-type"].elements["type"] = "complexity"; + this.createComplexityGraph(testResult, testData[Strings.json.controller], samplesWithProperties, options, margins, size); + this.onComplexityGraphOptionsChanged(); + + this.onGraphTypeChanged(); + }, + + _addRegressionLine: function(parent, xScale, yScale, points, range, isAlongYAxis) + { + var polygon = []; + var line = [] + var xRange = isAlongYAxis ? range : 0; + var yRange = isAlongYAxis ? 0 : range; + for (var i = 0; i < points.length; ++i) { + var point = points[i]; + var x; + if (xRange instanceof Array) + x = xRange[0]; + else + x = point[0] + xRange; + polygon.push(xScale(x), yScale(point[1] + yRange)); + line.push(xScale(point[0]), yScale(point[1])); + } + for (var i = points.length - 1; i >= 0; --i) { + var point = points[i]; + var x; + if (xRange instanceof Array) + x = xRange[1]; + else + x = point[0] - xRange; + polygon.push(xScale(x), yScale(point[1] - yRange)); + } + parent.append("polygon") + .attr("points", polygon.join(",")); + parent.append("line") + .attr("x1", line[0]) + .attr("y1", line[1]) + .attr("x2", line[2]) + .attr("y2", line[3]); + }, + + _addRegression: function(data, svg, xScale, yScale) + { + svg.append("circle") + .attr("cx", xScale(data.segment1[1][0])) + .attr("cy", yScale(data.segment1[1][1])) + .attr("r", 5); + this._addRegressionLine(svg, xScale, yScale, data.segment1, data.stdev); + this._addRegressionLine(svg, xScale, yScale, data.segment2, data.stdev); + }, + + createComplexityGraph: function(result, timeRegressions, data, options, margins, size) + { + var svg = d3.select("#test-graph-data").append("svg") + .attr("id", "complexity-graph") + .attr("class", "hidden") + .attr("width", size.width + margins.left + margins.right) + .attr("height", size.height + margins.top + margins.bottom) + .append("g") + .attr("transform", "translate(" + margins.left + "," + margins.top + ")"); + + var timeSamples = data[Strings.json.controller]; + + var xMin = 100000, xMax = 0; + if (timeRegressions) { + timeRegressions.forEach(function(regression) { + for (var i = regression.startIndex; i <= regression.endIndex; ++i) { + xMin = Math.min(xMin, timeSamples[i].complexity); + xMax = Math.max(xMax, timeSamples[i].complexity); + } + }); + } else { + xMin = d3.min(timeSamples, function(s) { return s.complexity; }); + xMax = d3.max(timeSamples, function(s) { return s.complexity; }); + } + + var xScale = d3.scale.linear() + .range([0, size.width]) + .domain([xMin, xMax]); + var yScale = d3.scale.linear() + .range([size.height, 0]) + .domain([1000/20, 1000/60]); + + var xAxis = d3.svg.axis() + .scale(xScale) + .orient("bottom"); + var yAxis = d3.svg.axis() + .scale(yScale) + .tickValues([1000/20, 1000/25, 1000/30, 1000/35, 1000/40, 1000/45, 1000/50, 1000/55, 1000/60]) + .tickFormat(function(d) { return (1000 / d).toFixed(0); }) + .orient("left"); + + // x-axis + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + size.height + ")") + .call(xAxis); + + // y-axis + svg.append("g") + .attr("class", "y axis") + .call(yAxis); + + // time result + var mean = svg.append("g") + .attr("class", "mean complexity"); + var timeResult = result[Strings.json.controller]; + var yMin = yScale.domain()[0], yMax = yScale.domain()[1]; + this._addRegressionLine(mean, xScale, yScale, [[timeResult.average, yMin], [timeResult.average, yMax]], timeResult.stdev, true); + + // regression + this._addRegression(result[Strings.json.complexity], svg.append("g").attr("class", "regression raw"), xScale, yScale); + this._addRegression(result[Strings.json.complexityAverage], svg.append("g").attr("class", "regression average"), xScale, yScale); + + var bootstrapResult = result[Strings.json.complexity][Strings.json.bootstrap]; + if (bootstrapResult) { + var histogram = d3.layout.histogram() + .bins(xScale.ticks(100))(bootstrapResult.data); + var yBootstrapScale = d3.scale.linear() + .range([size.height/2, 0]) + .domain([0, d3.max(histogram, function(d) { return d.y; })]); + group = svg.append("g").attr("class", "bootstrap"); + var bar = group.selectAll(".bar") + .data(histogram) + .enter().append("g") + .attr("class", "bar") + .attr("transform", function(d) { return "translate(" + xScale(d.x) + "," + yBootstrapScale(d.y) + ")"; }); + bar.append("rect") + .attr("x", 1) + .attr("y", size.height/2) + .attr("width", xScale(histogram[1].x) - xScale(histogram[0].x) - 1) + .attr("height", function(d) { return size.height/2 - yBootstrapScale(d.y); }); + group = group.append("g").attr("class", "median"); + this._addRegressionLine(group, xScale, yScale, [[bootstrapResult.median, yMin], [bootstrapResult.median, yMax]], [bootstrapResult.confidenceLow, bootstrapResult.confidenceHigh], true); + group.append("circle") + .attr("cx", xScale(bootstrapResult.median)) + .attr("cy", yScale(1000/60)) + .attr("r", 5); + } + + // series + group = svg.append("g") + .attr("class", "series raw") + .selectAll("line") + .data(data[Strings.json.complexity]) + .enter(); + group.append("line") + .attr("x1", function(d) { return xScale(d.complexity) - 3; }) + .attr("x2", function(d) { return xScale(d.complexity) + 3; }) + .attr("y1", function(d) { return yScale(d.frameLength) - 3; }) + .attr("y2", function(d) { return yScale(d.frameLength) + 3; }); + group.append("line") + .attr("x1", function(d) { return xScale(d.complexity) - 3; }) + .attr("x2", function(d) { return xScale(d.complexity) + 3; }) + .attr("y1", function(d) { return yScale(d.frameLength) + 3; }) + .attr("y2", function(d) { return yScale(d.frameLength) - 3; }); + + group = svg.append("g") + .attr("class", "series average") + .selectAll("circle") + .data(data[Strings.json.complexityAverage]) + .enter(); + group.append("circle") + .attr("cx", function(d) { return xScale(d.complexity); }) + .attr("cy", function(d) { return yScale(d.frameLength); }) + .attr("r", 3) + group.append("line") + .attr("x1", function(d) { return xScale(d.complexity); }) + .attr("x2", function(d) { return xScale(d.complexity); }) + .attr("y1", function(d) { return yScale(d.frameLength - d.stdev); }) + .attr("y2", function(d) { return yScale(d.frameLength + d.stdev); }); + + // Cursor + var cursorGroup = svg.append("g").attr("class", "cursor hidden"); + cursorGroup.append("line") + .attr("class", "x") + .attr("x1", 0) + .attr("x2", 0) + .attr("y1", yScale(yAxis.scale().domain()[0]) + 10) + .attr("y2", yScale(yAxis.scale().domain()[1])); + cursorGroup.append("line") + .attr("class", "y") + .attr("x1", xScale(xAxis.scale().domain()[0]) - 10) + .attr("x2", xScale(xAxis.scale().domain()[1])) + .attr("y1", 0) + .attr("y2", 0) + cursorGroup.append("text") + .attr("class", "label x") + .attr("x", 0) + .attr("y", yScale(yAxis.scale().domain()[0]) + 15) + .attr("baseline-shift", "-100%") + .attr("text-anchor", "middle"); + cursorGroup.append("text") + .attr("class", "label y") + .attr("x", xScale(xAxis.scale().domain()[0]) - 15) + .attr("y", 0) + .attr("baseline-shift", "-30%") + .attr("text-anchor", "end"); + // Area to handle mouse events + var area = svg.append("rect") + .attr("fill", "transparent") + .attr("x", 0) + .attr("y", 0) + .attr("width", size.width) + .attr("height", size.height); + + area.on("mouseover", function() { + document.querySelector("#complexity-graph .cursor").classList.remove("hidden"); + }).on("mouseout", function() { + document.querySelector("#complexity-graph .cursor").classList.add("hidden"); + }).on("mousemove", function() { + var location = d3.mouse(this); + var location_domain = [xScale.invert(location[0]), yScale.invert(location[1])]; + cursorGroup.select("line.x") + .attr("x1", location[0]) + .attr("x2", location[0]); + cursorGroup.select("text.x") + .attr("x", location[0]) + .text(location_domain[0].toFixed(1)); + cursorGroup.select("line.y") + .attr("y1", location[1]) + .attr("y2", location[1]); + cursorGroup.select("text.y") + .attr("y", location[1]) + .text((1000 / location_domain[1]).toFixed(1)); + }); + }, + + createTimeGraph: function(result, samples, marks, regressions, options, margins, size) + { + var svg = d3.select("#test-graph-data").append("svg") + .attr("id", "time-graph") + .attr("width", size.width + margins.left + margins.right) + .attr("height", size.height + margins.top + margins.bottom) + .append("g") + .attr("transform", "translate(" + margins.left + "," + margins.top + ")"); + + // Axis scales + var x = d3.scale.linear() + .range([0, size.width]) + .domain([ + Math.min(d3.min(samples, function(s) { return s.time; }), 0), + d3.max(samples, function(s) { return s.time; })]); + var complexityMax = d3.max(samples, function(s) { + if (s.time > 0) + return s.complexity; + return 0; + }); + + var yLeft = d3.scale.linear() + .range([size.height, 0]) + .domain([0, complexityMax]); + var yRight = d3.scale.linear() + .range([size.height, 0]) + .domain([1000/20, 1000/60]); + + // Axes + var xAxis = d3.svg.axis() + .scale(x) + .orient("bottom") + .tickFormat(function(d) { return (d/1000).toFixed(0); }); + var yAxisLeft = d3.svg.axis() + .scale(yLeft) + .orient("left"); + var yAxisRight = d3.svg.axis() + .scale(yRight) + .tickValues([1000/20, 1000/25, 1000/30, 1000/35, 1000/40, 1000/45, 1000/50, 1000/55, 1000/60]) + .tickFormat(function(d) { return (1000/d).toFixed(0); }) + .orient("right"); + + // x-axis + svg.append("g") + .attr("class", "x axis") + .attr("fill", "rgb(235, 235, 235)") + .attr("transform", "translate(0," + size.height + ")") + .call(xAxis) + .append("text") + .attr("class", "label") + .attr("x", size.width) + .attr("y", -6) + .attr("fill", "rgb(235, 235, 235)") + .style("text-anchor", "end") + .text("time"); + + // yLeft-axis + svg.append("g") + .attr("class", "yLeft axis") + .attr("fill", "#7ADD49") + .call(yAxisLeft) + .append("text") + .attr("class", "label") + .attr("transform", "rotate(-90)") + .attr("y", 6) + .attr("fill", "#7ADD49") + .attr("dy", ".71em") + .style("text-anchor", "end") + .text(Strings.text.complexity); + + // yRight-axis + svg.append("g") + .attr("class", "yRight axis") + .attr("fill", "#FA4925") + .attr("transform", "translate(" + size.width + ", 0)") + .call(yAxisRight) + .append("text") + .attr("class", "label") + .attr("x", 9) + .attr("y", -20) + .attr("fill", "#FA4925") + .attr("dy", ".71em") + .style("text-anchor", "start") + .text(Strings.text.frameRate); + + // marks + var yMin = yRight(yAxisRight.scale().domain()[0]); + var yMax = yRight(yAxisRight.scale().domain()[1]); + for (var markName in marks) { + var mark = marks[markName]; + var xLocation = x(mark.time); + + var markerGroup = svg.append("g") + .attr("class", "marker") + .attr("transform", "translate(" + xLocation + ", 0)"); + markerGroup.append("text") + .attr("transform", "translate(10, " + (yMin - 10) + ") rotate(-90)") + .style("text-anchor", "start") + .text(markName) + markerGroup.append("line") + .attr("x1", 0) + .attr("x2", 0) + .attr("y1", yMin) + .attr("y2", yMax); + } + + if (Strings.json.controller in result) { + var complexity = result[Strings.json.controller]; + var regression = svg.append("g") + .attr("class", "complexity mean"); + this._addRegressionLine(regression, x, yLeft, [[samples[0].time, complexity.average], [samples[samples.length - 1].time, complexity.average]], complexity.stdev); + } + if (Strings.json.frameLength in result) { + var frameLength = result[Strings.json.frameLength]; + var regression = svg.append("g") + .attr("class", "fps mean"); + this._addRegressionLine(regression, x, yRight, [[samples[0].time, 1000/frameLength.average], [samples[samples.length - 1].time, 1000/frameLength.average]], frameLength.stdev); + } + + // right-target + if (options["controller"] == "adaptive") { + var targetFrameLength = 1000 / options["frame-rate"]; + svg.append("line") + .attr("x1", x(0)) + .attr("x2", size.width) + .attr("y1", yRight(targetFrameLength)) + .attr("y2", yRight(targetFrameLength)) + .attr("class", "target-fps marker"); + } + + // Cursor + var cursorGroup = svg.append("g").attr("class", "cursor"); + cursorGroup.append("line") + .attr("x1", 0) + .attr("x2", 0) + .attr("y1", yMin) + .attr("y2", yMin); + + // Data + var allData = samples; + var filteredData = samples.filter(function (sample) { + return "smoothedFrameLength" in sample; + }); + + function addData(name, data, yCoordinateCallback, pointRadius, omitLine) { + var svgGroup = svg.append("g").attr("id", name); + if (!omitLine) { + svgGroup.append("path") + .datum(data) + .attr("d", d3.svg.line() + .x(function(d) { return x(d.time); }) + .y(yCoordinateCallback)); + } + svgGroup.selectAll("circle") + .data(data) + .enter() + .append("circle") + .attr("cx", function(d) { return x(d.time); }) + .attr("cy", yCoordinateCallback) + .attr("r", pointRadius); + + cursorGroup.append("circle") + .attr("class", name) + .attr("r", pointRadius + 2); + } + + addData("complexity", allData, function(d) { return yLeft(d.complexity); }, 2); + addData("rawFPS", allData, function(d) { return yRight(d.frameLength); }, 1); + addData("filteredFPS", filteredData, function(d) { return yRight(d.smoothedFrameLength); }, 2); + + // regressions + var regressionGroup = svg.append("g") + .attr("id", "regressions"); + if (regressions) { + var complexities = []; + regressions.forEach(function (regression) { + if (!isNaN(regression.segment1[0][1]) && !isNaN(regression.segment1[1][1])) { + regressionGroup.append("line") + .attr("x1", x(regression.segment1[0][0])) + .attr("x2", x(regression.segment1[1][0])) + .attr("y1", yRight(regression.segment1[0][1])) + .attr("y2", yRight(regression.segment1[1][1])); + } + if (!isNaN(regression.segment2[0][1]) && !isNaN(regression.segment2[1][1])) { + regressionGroup.append("line") + .attr("x1", x(regression.segment2[0][0])) + .attr("x2", x(regression.segment2[1][0])) + .attr("y1", yRight(regression.segment2[0][1])) + .attr("y2", yRight(regression.segment2[1][1])); + } + // inflection point + regressionGroup.append("circle") + .attr("cx", x(regression.segment1[1][0])) + .attr("cy", yLeft(regression.complexity)) + .attr("r", 5); + complexities.push(regression.complexity); + }); + if (complexities.length) { + var yLeftComplexities = d3.svg.axis() + .scale(yLeft) + .tickValues(complexities) + .tickSize(10) + .orient("left"); + svg.append("g") + .attr("class", "complexity yLeft axis") + .call(yLeftComplexities); + } + } + + // Area to handle mouse events + var area = svg.append("rect") + .attr("fill", "transparent") + .attr("x", 0) + .attr("y", 0) + .attr("width", size.width) + .attr("height", size.height); + + var timeBisect = d3.bisector(function(d) { return d.time; }).right; + var statsToHighlight = ["complexity", "rawFPS", "filteredFPS"]; + area.on("mouseover", function() { + document.querySelector("#time-graph .cursor").classList.remove("hidden"); + document.querySelector("#test-graph nav").classList.remove("hide-data"); + }).on("mouseout", function() { + document.querySelector("#time-graph .cursor").classList.add("hidden"); + document.querySelector("#test-graph nav").classList.add("hide-data"); + }).on("mousemove", function() { + var form = document.forms["time-graph-options"].elements; + + var mx_domain = x.invert(d3.mouse(this)[0]); + var index = Math.min(timeBisect(allData, mx_domain), allData.length - 1); + var data = allData[index]; + var cursor_x = x(data.time); + var cursor_y = yAxisRight.scale().domain()[1]; + var ys = [yRight(yAxisRight.scale().domain()[0]), yRight(yAxisRight.scale().domain()[1])]; + + document.querySelector("#test-graph nav .time").textContent = (data.time / 1000).toFixed(4) + "s (" + index + ")"; + statsToHighlight.forEach(function(name) { + var element = document.querySelector("#test-graph nav ." + name); + var content = ""; + var data_y = null; + switch (name) { + case "complexity": + content = data.complexity; + data_y = yLeft(data.complexity); + break; + case "rawFPS": + content = (1000/data.frameLength).toFixed(2); + data_y = yRight(data.frameLength); + break; + case "filteredFPS": + if ("smoothedFrameLength" in data) { + content = (1000/data.smoothedFrameLength).toFixed(2); + data_y = yRight(data.smoothedFrameLength); + } + break; + } + + element.textContent = content; + + if (form[name].checked && data_y !== null) { + ys.push(data_y); + cursorGroup.select("." + name) + .attr("cx", cursor_x) + .attr("cy", data_y); + document.querySelector("#time-graph .cursor ." + name).classList.remove("hidden"); + } else + document.querySelector("#time-graph .cursor ." + name).classList.add("hidden"); + }); + + if (form["rawFPS"].checked) + cursor_y = Math.max(cursor_y, data.frameLength); + cursorGroup.select("line") + .attr("x1", cursor_x) + .attr("x2", cursor_x) + .attr("y1", Math.min.apply(null, ys)) + .attr("y2", Math.max.apply(null, ys)); + + }); + }, + + _showOrHideNodes: function(isShown, selector) { + var nodeList = document.querySelectorAll(selector); + if (isShown) { + for (var i = 0; i < nodeList.length; ++i) + nodeList[i].classList.remove("hidden"); + } else { + for (var i = 0; i < nodeList.length; ++i) + nodeList[i].classList.add("hidden"); + } + }, + + onComplexityGraphOptionsChanged: function() { + var form = document.forms["complexity-graph-options"].elements; + benchmarkController._showOrHideNodes(form["series-raw"].checked, "#complexity-graph .series.raw"); + benchmarkController._showOrHideNodes(form["series-average"].checked, "#complexity-graph .series.average"); + benchmarkController._showOrHideNodes(form["regression-time-score"].checked, "#complexity-graph .mean.complexity"); + benchmarkController._showOrHideNodes(form["bootstrap-score"].checked, "#complexity-graph .bootstrap"); + benchmarkController._showOrHideNodes(form["complexity-regression-aggregate-raw"].checked, "#complexity-graph .regression.raw"); + benchmarkController._showOrHideNodes(form["complexity-regression-aggregate-average"].checked, "#complexity-graph .regression.average"); + }, + + onTimeGraphOptionsChanged: function() { + var form = document.forms["time-graph-options"].elements; + benchmarkController._showOrHideNodes(form["markers"].checked, ".marker"); + benchmarkController._showOrHideNodes(form["averages"].checked, "#test-graph-data .mean"); + benchmarkController._showOrHideNodes(form["complexity"].checked, "#complexity"); + benchmarkController._showOrHideNodes(form["rawFPS"].checked, "#rawFPS"); + benchmarkController._showOrHideNodes(form["filteredFPS"].checked, "#filteredFPS"); + benchmarkController._showOrHideNodes(form["regressions"].checked, "#regressions"); + }, + + onGraphTypeChanged: function() { + var form = document.forms["graph-type"].elements; + var testResult = document.getElementById("test-graph-data")._testResult; + var isTimeSelected = form["graph-type"].value == "time"; + + benchmarkController._showOrHideNodes(isTimeSelected, "#time-graph"); + benchmarkController._showOrHideNodes(isTimeSelected, "form[name=time-graph-options]"); + benchmarkController._showOrHideNodes(!isTimeSelected, "#complexity-graph"); + benchmarkController._showOrHideNodes(!isTimeSelected, "form[name=complexity-graph-options]"); + + var score = "", mean = ""; + if (isTimeSelected) { + score = testResult[Strings.json.score].toFixed(2); + + var regression = testResult[Strings.json.controller]; + mean = [ + "mean: ", + regression.average.toFixed(2), + " ± ", + regression.stdev.toFixed(2), + " (", + regression.percent.toFixed(2), + "%)"]; + if (regression.concern) { + mean = mean.concat([ + ", worst 5%: ", + regression.concern.toFixed(2)]); + } + mean = mean.join(""); + } else { + var complexityRegression = testResult[Strings.json.complexity]; + var complexityAverageRegression = testResult[Strings.json.complexityAverage]; + + document.getElementById("complexity-regression-aggregate-raw").textContent = complexityRegression.complexity.toFixed(2) + ", ±" + complexityRegression.stdev.toFixed(2) + "ms"; + document.getElementById("complexity-regression-aggregate-average").textContent = complexityAverageRegression.complexity.toFixed(2) + ", ±" + complexityAverageRegression.stdev.toFixed(2) + "ms"; + + var bootstrap = complexityRegression[Strings.json.bootstrap]; + if (bootstrap) { + score = bootstrap.median.toFixed(2); + mean = [ + (bootstrap.confidencePercentage * 100).toFixed(0), + "% CI: ", + bootstrap.confidenceLow.toFixed(2), + "–", + bootstrap.confidenceHigh.toFixed(2) + ].join(""); + } + } + + sectionsManager.setSectionScore("test-graph", score, mean); + } +}); -- cgit v1.2.3