在我的WebGl程序中,帧率开始高,然后慢慢减少,内存使用量随着时间的推移而等效增加。帧率不会无限下降,但在某些时候保持一致。 IE和IE中的问题可以忽略不计。 FF,但在Chrome中,我的帧率从30降至10。
我缩小了问题的范围: 它是由创建正在绘制的数据的函数(在原始程序中)引起的。 出于测试目的,我创建了一个将测试数据写入全局变量的函数。测试数据 NOT 以任何方式使用。 fps减少仍然存在。
var testData;
function createTestData() {
testData = [];
for (var i = 0; i < 5000; i++) {
testData[i] = [];
for (var j = 0; j < 1000; j++) {
testData[i][j] = 1;
}
}
}
如果我注释掉对该函数的一次调用,那么一切都按预期工作,没有帧率下降。
在运行时调用createTestData()会导致fps像开始时一样上升,然后再慢慢下降。
我的程序流程的Chrome内存使用量不到200mb,远不及Chrome应该遇到的问题。
我使用的是Win 7&amp; 8,Chrome 35&amp; 36和多个电脑设置。
这感觉就像是一个Chrome错误,但我找不到其他人遇到这个问题,所以这可能是我身上的一些愚蠢错误。
强烈简化版本的完整代码:
的index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>WebGl Framedrop Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="sylvester.js"></script>
<script type="text/javascript" src="glpsutilskap8.js"></script>
<script type="text/javascript" src="JS_Main.js"></script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
uniform mat4 mvpMatrix;
void main(void)
{
gl_Position = mvpMatrix * vec4(aVertexPosition, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
void main(void)
{
}
</script>
<style type="text/css">
body {
margin-left: 0px;
margin-top: 0px;
}
canvas {
margin-left: 0px;
margin-top: 0px;
}
</style>
</head>
<body onload="Main()">
<table>
<td style="vertical-align: top;">
<canvas id="WebGL-canvas" style="border: none;" width="800" height="600" ></canvas>
<input type="button" style="margin-left:5px;" value="call createTestData() again" onclick="createTestData()" />
<p>current fps:</p>
<p id="fpsDisplay" style="color:red">default</p>
<p>fps last min:</p>
<p id="fpsMinuteDisplay" style="color:red">default</p>
</table>
</body>
</html>
JS_Main.js
var testData;
function createTestData() {
testData = [];
for (var i = 0; i < 5000; i++) {
testData[i] = [];
for (var j = 0; j < 1000; j++) {
testData[i][j] = 1;
}
}
}
var fZnear = 0.1;
var fZfar = 3000;
var g_fOpenViewAngle = 45;
var gl;
var shaderProgram;
var mMatrix;
var vMatrix;
var pMatrix;
var mvMatrix;
var mvpMatrix;
var requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| window.oRequestAnimationFrame
;
var wall = {};
wall.vertices = new Array();
wall.triangles = new Array();
var g_nCanvasWidth = 800;
var g_nCanvasHeight = 600;
var fpsCounter;
function Main() {
initGL();
initShaders();
gl.clearColor(0.5, 0.75, 1.0, 1.0);
initBuffers();
createTestData();
fpsCounter = new FpsCounter();
setInterval(setIntervalLoop, 0);
}
function drawScene() {
fpsCounter.update();
document.getElementById('fpsDisplay').innerHTML = fpsCounter.getCountPerSecond();
document.getElementById('fpsMinuteDisplay').innerHTML = fpsCounter.getCountPerMinute();
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
pMatrix = createPerspectiveMatrix(g_fOpenViewAngle, g_nCanvasWidth / g_nCanvasHeight, fZnear, fZfar);
vMatrix = translationMatrix(-100, -100, -100);
drawWall();
}
function drawWall() {
gl.bindBuffer(gl.ARRAY_BUFFER, wall.vertexPositionBufferID);
gl.vertexAttribPointer(vertexPositionAttribute, wall.vertexPositionBufferID.itemSize, gl.FLOAT, false, 0, 0);
for (var i = 0; i < 1000; i++) {
mMatrix = translationMatrix(Math.random() * 200, Math.random() * 200, Math.random() * 200).x(Matrix.I(4));
mvMatrix = vMatrix.x(mMatrix);
mvpMatrix = pMatrix.x(mvMatrix);
setMatrixUniforms();
gl.drawArrays(gl.TRIANGLES, 0, wall.vertexPositionBufferID.numItems);
}
}
function translationMatrix(x, y, z) {
var ret = Matrix.I(4);
ret.elements[0][3] = x;
ret.elements[1][3] = y;
ret.elements[2][3] = z;
return ret;
}
;
function setIntervalLoop() {
drawScene();
}
function initBuffers() {
wall.vertexPositionBufferID = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, wall.vertexPositionBufferID);
wall.vertices[0] = Vector.create([1.0, -0.25, 1.0]);
wall.vertices[1] = Vector.create([-1.0, -0.25, 1.0]);
wall.vertices[2] = Vector.create([-1.0, 0.25, 1.0]);
wall.vertices[3] = Vector.create([1.0, 0.25, 1.0]);
wall.vertices[4] = Vector.create([1.0, -0.25, -1.0]);
wall.vertices[5] = Vector.create([-1.0, -0.25, -1.0]);
wall.vertices[6] = Vector.create([-1.0, 0.25, -1.0]);
wall.vertices[7] = Vector.create([1.0, 0.25, -1.0]);
wall.triangles[0] = new IndexedVertexTriangle(wall.vertices, 4, 0, 1);
wall.triangles[1] = new IndexedVertexTriangle(wall.vertices, 4, 1, 5);
wall.triangles[2] = new IndexedVertexTriangle(wall.vertices, 3, 7, 6);
wall.triangles[3] = new IndexedVertexTriangle(wall.vertices, 3, 6, 2);
wall.triangles[4] = new IndexedVertexTriangle(wall.vertices, 7, 3, 0);
wall.triangles[5] = new IndexedVertexTriangle(wall.vertices, 7, 0, 4);
wall.triangles[6] = new IndexedVertexTriangle(wall.vertices, 5, 1, 2);
wall.triangles[7] = new IndexedVertexTriangle(wall.vertices, 5, 2, 6);
wall.triangles[8] = new IndexedVertexTriangle(wall.vertices, 0, 3, 2);
wall.triangles[9] = new IndexedVertexTriangle(wall.vertices, 0, 2, 1);
wall.triangles[10] = new IndexedVertexTriangle(wall.vertices, 7, 4, 5);
wall.triangles[11] = new IndexedVertexTriangle(wall.vertices, 7, 5, 6);
var vertices = [];
for (var i = 0; i < wall.triangles.length; i++) {
vertices[9 * i] = wall.vertices[wall.triangles[i].index1].elements[0];
vertices[9 * i + 1] = wall.vertices[wall.triangles[i].index1].elements[1];
vertices[9 * i + 2] = wall.vertices[wall.triangles[i].index1].elements[2];
vertices[9 * i + 3] = wall.vertices[wall.triangles[i].index2].elements[0];
vertices[9 * i + 4] = wall.vertices[wall.triangles[i].index2].elements[1];
vertices[9 * i + 5] = wall.vertices[wall.triangles[i].index2].elements[2];
vertices[9 * i + 6] = wall.vertices[wall.triangles[i].index3].elements[0];
vertices[9 * i + 7] = wall.vertices[wall.triangles[i].index3].elements[1];
vertices[9 * i + 8] = wall.vertices[wall.triangles[i].index3].elements[2];
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
wall.vertexPositionBufferID.itemSize = 3;
wall.vertexPositionBufferID.numItems = wall.triangles.length * 3;
}
function FpsCounter() {
this.count = 0;
this.fps = 0;
this.prevSecond;
this.minuteBuffer = new OverrideRingBuffer(60);
}
FpsCounter.prototype.update = function() {
if (!this.prevSecond) {
this.prevSecond = new Date().getTime();
this.count = 1;
}
else {
var currentTime = new Date().getTime();
var difference = currentTime - this.prevSecond;
if (difference > 1000) {
this.prevSecond = currentTime;
this.fps = this.count;
this.minuteBuffer.push(this.count);
this.count = 0;
}
else {
this.count++;
}
}
};
FpsCounter.prototype.getCountPerMinute = function() {
return this.minuteBuffer.getAverage();
};
FpsCounter.prototype.getCountPerSecond = function() {
return this.fps;
};
function OverrideRingBuffer(size) {
this.size = size;
this.head = 0;
this.buffer = new Array();
}
;
OverrideRingBuffer.prototype.push = function(value) {
if (this.head >= this.size)
this.head -= this.size;
this.buffer[this.head] = value;
this.head++;
};
OverrideRingBuffer.prototype.getAverage = function() {
if (this.buffer.length === 0)
return 0;
var sum = 0;
for (var i = 0; i < this.buffer.length; i++) {
sum += this.buffer[i];
}
return (sum / this.buffer.length).toFixed(1);
};
glpsutilskap8.js
function createPerspectiveMatrix(fFoVVy,
fAspect,
fZnear,
fZfar) {
var test = (Matrix.create([
[fAspect / Math.tan(fFoVVy * Math.PI / 180.0), 0, 0, 0],
[0, 1 / Math.tan(fFoVVy * Math.PI / 180.0), 0, 0],
[0, 0, (fZnear + fZfar) / (fZnear - fZfar), 2 * fZnear * fZfar / (fZnear - fZfar)],
[0, 0, -1, 0]]));
return test;
return test;
}
;
Matrix.prototype.flatten = function() {
var result = [];
if (this.elements.length == 0)
return [];
for (var j = 0; j < this.elements[0].length; j++)
for (var i = 0; i < this.elements.length; i++)
result.push(this.elements[i][j]);
return result;
};
function initGL() {
canvas = document.getElementById("WebGL-canvas");
try {
gl = canvas.getContext("experimental-webgl");
}
catch (e) {
}
if (!gl) {
try {
gl = canvas.getContext("webgl");
}
catch (e) {
}
}
if (!gl) {
try {
gl = canvas.getContext("webkit-3d");
}
catch (e) {
}
}
if (!gl) {
try {
gl = canvas.getContext("moz-webgl");
}
catch (e) {
}
}
if (!gl) {
alert("WebGL not found. Please use an up to date browser and update your graphics driver.");
}
}
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript)
return null;
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3)
str += k.textContent;
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
}
else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
}
else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
// attributes
vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
}
function setMatrixUniforms() {
var mvpUniform = gl.getUniformLocation(shaderProgram, "mvpMatrix");
gl.uniformMatrix4fv(mvpUniform, false, new Float32Array(mvpMatrix.flatten()));
}
function IndexedVertexTriangle(vectorArray, index1, index2, index3)
{
this.index1 = index1;
this.index2 = index2;
this.index3 = index3;
this.vectorArray = vectorArray;
this.normal = null;
this.getNormal = function()
{
if (this.normal == null)
{
var sideOne = this.vectorArray[this.index2].subtract(this.vectorArray[this.index1]);
var sideTwo = this.vectorArray[this.index3].subtract(this.vectorArray[this.index1]);
this.normal = sideOne.cross(sideTwo);
this.normal = this.normal.toUnitVector();
}
return(this.normal);
};
this.getVertex = function(localIndex)
{
if (localIndex > 3)
{
return(0);
}
if (localIndex == 1)
return(this.vectorArray[index1]);
if (localIndex == 2)
return(this.vectorArray[index2]);
if (localIndex == 3)
return(this.vectorArray[index3]);
};
}
Vector.prototype.flatten = function()
{
var result = [];
if (this.elements.length == 0)
return [];
for (var i = 0; i < this.elements.length; i++)
result.push(this.elements[i]);
return result;
};
Sylvester.js
可从http://sylvester.jcoglan.com/#download
下载修改
使用Chrome时间轴和内容:
首先垃圾收集(GC)占用25%的CPU,后来占60%。
对于每次draw()调用,GC几乎发生一次(清理7.5mb)。关于每20次抽奖()呼叫,没有GC。没有GC(约400次抽取()调用)约20次后,会发生更大的GC(30-40mb)。
堆分配快照:存在直到最后的数据仅在开始时分配一次。如预期的那样。
TestData占堆的94%。
所以,GC出了问题,但我仍然不知道为什么和为什么。由于94%的TestData,镀铬是否有可能使我的记忆过多?那么GC放慢了速度?
我会尝试更熟悉这些工具,也许会发布更新,但仍然会感谢您的帮助。
答案 0 :(得分:3)
终于找到了问题: 大多数浏览器的垃圾收集,特别是Chrome在处理类型数组时遇到问题。
这是一个相关的Chromium bug: https://code.google.com/p/chromium/issues/detail?id=232415
要“修复”此问题,只需使用普通数组而不是类型化数组:
旧代码:
function setMatrixUniforms() {
var mvpUniform = gl.getUniformLocation(shaderProgram, "mvpMatrix");
gl.uniformMatrix4fv(mvpUniform, false, new Float32Array(mvpMatrix.flatten()));
}
新代码:
function setMatrixUniforms() {
var mvpUniform = gl.getUniformLocation(shaderProgram, "mvpMatrix");
gl.uniformMatrix4fv(mvpUniform, false, mvpMatrix.flatten());
}