skinning perf

Benchmark created on


Description

test 3d javascript performance

Preparation HTML

<script>
  function stdCalVector4(x, y, z, w) {
   this.x = x || 0.0;
   this.y = y || 0.0;
   this.z = z || 0.0;
   this.w = w || 0.0;
  }
  
  // 3x3 transform matrix plus a translation 3-vector (stored in the w components
  // of the rows.  This struct needs to be 16-byte aligned for SSE.
  
  function stdBoneTransform() {
   this.rowx_x = 0.0;
   this.rowx_y = 0.0;
   this.rowx_z = 0.0;
   this.rowx_w = 0.0;
   this.rowy_x = 0.0;
   this.rowy_y = 0.0;
   this.rowy_z = 0.0;
   this.rowy_w = 0.0;
   this.rowz_x = 0.0;
   this.rowz_y = 0.0;
   this.rowz_z = 0.0;
   this.rowz_w = 0.0;
  }
  
  function stdInfluence() {
   this.boneId = -1;
   this.weight = 0;
   this.lastInfluenceForThisVertex = false;
  }
  
  function stdVertex() {
   this.position = new stdCalVector4;
   this.normal = new stdCalVector4;
  }
  
  
  function stdScaleMatrix(result, mat, s) {
   result.rowx_x = s * mat.rowx_x;
   result.rowx_y = s * mat.rowx_y;
   result.rowx_z = s * mat.rowx_z;
   result.rowx_w = s * mat.rowx_w;
   result.rowy_x = s * mat.rowy_x;
   result.rowy_y = s * mat.rowy_y;
   result.rowy_z = s * mat.rowy_z;
   result.rowy_w = s * mat.rowy_w;
   result.rowz_x = s * mat.rowz_x;
   result.rowz_y = s * mat.rowz_y;
   result.rowz_z = s * mat.rowz_z;
   result.rowz_w = s * mat.rowz_w;
  }
  
  function stdAddScaledMatrix(result, mat, s) {
   result.rowx_x += s * mat.rowx_x;
   result.rowx_y += s * mat.rowx_y;
   result.rowx_z += s * mat.rowx_z;
   result.rowx_w += s * mat.rowx_w;
   result.rowy_x += s * mat.rowy_x;
   result.rowy_y += s * mat.rowy_y;
   result.rowy_z += s * mat.rowy_z;
   result.rowy_w += s * mat.rowy_w;
   result.rowz_x += s * mat.rowz_x;
   result.rowz_y += s * mat.rowz_y;
   result.rowz_z += s * mat.rowz_z;
   result.rowz_w += s * mat.rowz_w;
  }
  
  function stdTransformPoint(result, m, v) {
   result.x = m.rowx_x * v.x + m.rowx_y * v.y + m.rowx_z * v.z + m.rowx_w;
   result.y = m.rowy_x * v.x + m.rowy_y * v.y + m.rowy_z * v.z + m.rowy_w;
   result.z = m.rowz_x * v.x + m.rowz_y * v.y + m.rowz_z * v.z + m.rowz_w;
  }
  
  function stdTransformVector(result, m, v) {
   result.x = m.rowx_x * v.x + m.rowx_y * v.y + m.rowx_z * v.z;
   result.y = m.rowy_x * v.x + m.rowy_y * v.y + m.rowy_z * v.z;
   result.z = m.rowz_x * v.x + m.rowz_y * v.y + m.rowz_z * v.z;
  }
  
  function assert(cond) {
   if (!cond) {
    alert("assert failed");
    throw 'assert failed';
   }
  }
  
  function stdcalculateVerticesAndNormals(
  boneTransforms, vertices, influences, output_vertex) {
   var vertexCount = vertices.length;
   assert(vertices.length == influences.length);
  
   const total_transform = new stdBoneTransform;
  
   var influenceId = 0;
   var vertexId = 0;
   var outputVertexId = 0;
  
   // calculate all submesh vertices
   while (vertexCount--) {
    stdScaleMatrix(total_transform, boneTransforms[influences[influenceId].boneId], influences[influenceId].weight);
  
    while (!influences[influenceId].lastInfluenceForThisVertex) {
     ++influenceId;
     stdAddScaledMatrix(total_transform, boneTransforms[influences[influenceId].boneId], influences[influenceId].weight);
    }
  
    stdTransformPoint(output_vertex[outputVertexId++], total_transform, vertices[vertexId].position);
    stdTransformVector(output_vertex[outputVertexId++], total_transform, vertices[vertexId].normal);
    ++vertexId;
   }
  }
  
  const stdN = 10000;
  
  const stdv = []; // Vertex
  const stdi = []; // Influence
  for (var k = 0; k < stdN; ++k) {
   const stdvertex = new stdVertex;
   stdvertex.position = new stdCalVector4(1.0, 2.0, 3.0);
   stdvertex.normal = new stdCalVector4(0.0, 0.0, 1.0);
   stdv.push(stdvertex);
  
   const stdinfluence = new stdInfluence;
   stdinfluence.boneId = 0;
   stdinfluence.weight = 1.0;
   stdinfluence.lastInfluenceForThisVertex = true;
   stdi.push(stdinfluence);
  }
  
  var stdbt = [new stdBoneTransform];
  
  var stdoutput = new Array(stdN * 2);
  for (var k = 0; k < stdN * 2; ++k) {
   stdoutput[k] = new stdCalVector4;
  }
  
  
  
  ////////// Typed Array
  "use strict";
  
  function taInfluence() {
   this.boneId = -1;
   this.weight = 0;
   this.lastInfluenceForThisVertex = false;
  }
  
  function taScaleMatrix(result, mat, s) {
   result[0] = s * mat[0];
   result[1] = s * mat[1];
   result[2] = s * mat[2];
   result[3] = s * mat[3];
   result[4] = s * mat[4];
   result[5] = s * mat[5];
   result[6] = s * mat[6];
   result[7] = s * mat[7];
   result[8] = s * mat[8];
   result[9] = s * mat[9];
   result[10] = s * mat[10];
   result[11] = s * mat[11];
  }
  
  function taAddScaledMatrix(result, mat, s) {
   result[0] += s * mat[0];
   result[1] += s * mat[1];
   result[2] += s * mat[2];
   result[3] += s * mat[3];
   result[4] += s * mat[4];
   result[5] += s * mat[5];
   result[6] += s * mat[6];
   result[7] += s * mat[7];
   result[8] += s * mat[8];
   result[9] += s * mat[9];
   result[10] += s * mat[10];
   result[11] += s * mat[11];
  }
  
  function taTransformPoint(result, m, v, offset) {
   //alert(v[offset] + ',' + v[offset+1] + ',' + v[offset+2]);
   result[offset] = m[0] * v[offset] + m[1] * v[offset + 1] + m[2] * v[offset + 2] + m[3];
   result[offset + 1] = m[4] * v[offset] + m[5] * v[offset + 1] + m[6] * v[offset + 2] + m[7];
   result[offset + 2] = m[8] * v[offset] + m[9] * v[offset + 1] + m[10] * v[offset + 2] + m[11];
  }
  
  function taTransformVector(result, m, v, offset) {
   //alert(v[offset] + ',' + v[offset+1] + ',' + v[offset+2]);
   result[offset] = m[0] * v[offset] + m[1] * v[offset + 1] + m[1] * v[offset + 2];
   result[offset + 1] = m[4] * v[offset] + m[6] * v[offset + 1] + m[7] * v[offset + 2];
   result[offset + 2] = m[8] * v[offset] + m[9] * v[offset + 1] + m[10] * v[offset + 2];
  }
  
  
  function tacalculateVerticesAndNormals(
  boneTransforms, vertices, influences, output_vertex) {
   var vertexCount = influences.length;
   //assert(vertices.length == influences.length);
   const total_transform = new Float32Array(12);
  
   var influenceId = 0;
   var vertexOffset = 0;
  
   vertices = new Float32Array(vertices);
  
   // calculate all submesh vertices
   while (vertexCount--) {
    taScaleMatrix(total_transform, boneTransforms[influences[influenceId].boneId], influences[influenceId].weight);
  
    while (!influences[influenceId].lastInfluenceForThisVertex) {
     ++influenceId;
     taAddScaledMatrix(total_transform, boneTransforms[influences[influenceId].boneId], influences[influenceId].weight);
    }
  
    taTransformPoint(output_vertex, total_transform, vertices, vertexOffset);
    vertexOffset += 4;
    taTransformVector(output_vertex, total_transform, vertices, vertexOffset);
    vertexOffset += 4;
   }
  
  }
  
  const taN = 10000;
  
  const taVertexSize = 4 + 4;
  const tav = new ArrayBuffer(taN * taVertexSize * 4); // floats
  const tai = []; // Influence
  for (var k = 0; k < taN; ++k) {
   var taposition = new Float32Array(tav, k * taVertexSize * 4, 4);
   taposition[0] = 1.0;
   taposition[1] = 2.0;
   taposition[2] = 3.0;
   taposition[3] = 1.0;
  
   var tanormal = new Float32Array(tav, k * taVertexSize * 4 + 4 * 4, 4);
   tanormal[0] = 0.0;
   tanormal[1] = 0.0;
   tanormal[2] = 1.0;
   tanormal[3] = 0.0;
  
   const tainfluence = new taInfluence;
   tainfluence.boneId = 0;
   tainfluence.weight = 1.0;
   tainfluence.lastInfluenceForThisVertex = true;
   tai.push(tainfluence);
  }
  
  var tabt = [new Float32Array(4 * 3)];
  var taoutput = new Float32Array(taN * 2 * 4);
</script>

Test runner

Ready to run.

Testing in
TestOps/sec
standard array
stdcalculateVerticesAndNormals(stdbt, stdv, stdi, stdoutput);
ready
typed Array
tacalculateVerticesAndNormals(tabt, tav, tai, taoutput);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.