การเปรียบเทียบ FPS ระหว่างอินสแตนซ์และผสาน Drawcall และ Native

ฉันกำลังทดสอบ FPS กับแล็ปท็อปโดยใช้การ์ด Intel(R) Iris(R) Plus Graphics 655 เพื่อทดสอบตัวอย่าง threeJS ด้วยการเรนเดอร์อินสแตนซ์และการเรนเดอร์แบบ Merge-drawcall

ดังนั้นฉันจึงใช้ทั้งโมเดล QRCode_buffergeometry.json และโมเดล suzanne_buffergeometry.json สำหรับ QRCode_buffergeometry.json: จุดสุดยอด: 12852 ใบหน้า: 4284 และสำหรับ suzanne_buffergeometry.json: จุดสุดยอด: 1515 ใบหน้า: 967

จากนั้น FPS สำหรับ suzanne_buffergeometry ที่มีจำนวน 8000:

ตัวอย่าง: 36

รวมเข้าด้วยกัน: 43

NATIVE: จาก 23 ถึง 35 โดยการหมุน

สำหรับโมเดล QRCode_buffergeometry ที่มีจำนวน 8000:

ตัวอย่าง: 9

รวม: 15-17

พื้นเมือง: 17-19

ฉันสับสนมากกับการแสดงนี้ 1. เท่าที่ฉันเข้าใจ ไม่ว่าฉันจะใช้ instance หรือ Merge-drawcallก็ตาม Drawcall ถูกกำหนดให้เป็น 1 และจำนวนหน้าทั้งหมดที่จะจั่วเท่ากัน ทำไม Merged-drawcall ถึงดีกว่า Instance? เนื่องจากจำนวนใบหน้าและจุดยอดเท่ากัน ฉันคิดว่าสิ่งที่เกิดขึ้นในเชเดอร์จุดยอดสำหรับการแปลงจุดยอดก็ควรจะเหมือนกันด้วย แล้วเหตุใดการผสานจึงเร็วกว่า

  1. สำหรับโมเดล QRCode_buffergeometry นั้น Native เกือบจะเหมือนกับการผสานและดีกว่าอินสแตนซ์ ดังนั้นฉันเดาว่า CPU ไม่ใช่คอขวด แต่เป็น GPU อย่างไรก็ตามข้อมูลการวาดสุดท้ายควรจะเหมือนกัน ฉันหมายถึงในที่สุดหมายเลขหน้าที่จะวาด ควรจะเหมือนกัน เหตุใดเนทิฟจึงเร็วกว่า อินสแตนซ์นั้นควรจะเป็นวิธีที่ดีที่สุดไม่ใช่หรือ ฉันค่อนข้างแน่ใจว่ากล้องทั้งระยะใกล้และไกลนั้นใหญ่เพียงพอ ดังนั้นจึงไม่น่าจะมีปัญหาในการคัดแยกใดๆ

  2. เมื่อฉันพยายามปรับฉากใหญ่ๆ ให้เหมาะสม ฉันควรเลือกผสานเมื่อใด ควรเลือกอินสแตนซ์เมื่อใด และเมื่อบางทีการไม่ทำอะไรเลยจะดีกว่า?

ความช่วยเหลือใด ๆ ?

ขอบคุณมาก~~~

แนบรหัสสำหรับตัวอย่างอยู่ที่นี่

body { margin: 0; }
<div id="container"></div>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
import Stats from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/libs/stats.module.js';
import {
  GUI
} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/libs/dat.gui.module.js';
import {
  OrbitControls
} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js';
import {
  BufferGeometryUtils
} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/utils/BufferGeometryUtils.js';
var container, stats, gui, guiStatsEl;
var camera, controls, scene, renderer, material;

// gui
var Method = {
  INSTANCED: 'INSTANCED',
  MERGED: 'MERGED',
  NAIVE: 'NAIVE'
};

var api = {
  method: Method.INSTANCED,
  mesh_number: 1,
  count_per_mesh: 1000
};

var modelName = 'suzanne_buffergeometry.json';
var modelScale = (modelName === 'suzanne_buffergeometry.json' ? 1 : 0.01);
var modelVertex = (modelName === 'suzanne_buffergeometry.json' ? 1515 : 12852);
var modelFace = (modelName === 'suzanne_buffergeometry.json' ? 967 : 4284);

//
init();
initMesh();
animate();

//
function clean() {
  var meshes = [];
  scene.traverse(function(object) {
    if (object.isMesh) meshes.push(object);
  });

  for (var i = 0; i < meshes.length; i++) {
    var mesh = meshes[i];
    mesh.material.dispose();
    mesh.geometry.dispose();
    scene.remove(mesh);
  }
}

var randomizeMatrix = function() {
  var position = new THREE.Vector3();
  var rotation = new THREE.Euler();
  var quaternion = new THREE.Quaternion();
  var scale = new THREE.Vector3();

  return function(matrix) {
    position.x = Math.random() * 40 - 20;
    position.y = Math.random() * 40 - 20;
    position.z = Math.random() * 40 - 20;
    rotation.x = Math.random() * 2 * Math.PI;
    rotation.y = Math.random() * 2 * Math.PI;
    rotation.z = Math.random() * 2 * Math.PI;
    quaternion.setFromEuler(rotation);
    scale.x = scale.y = scale.z = Math.random() * modelScale;
    matrix.compose(position, quaternion, scale);
  };
}();

function initMesh() {
  clean();

  console.time(api.method + ' (build)');
  for (var i = 0; i < api.mesh_number; i++) {
    // make instances
    new THREE.BufferGeometryLoader()
      .setPath('https://threejs.org/examples/models/json/')
      .load(modelName, function(geometry) {
        material = new THREE.MeshNormalMaterial();
        geometry.computeVertexNormals();

        switch (api.method) {
          case Method.INSTANCED:
            makeInstanced(geometry);
            break;
          case Method.MERGED:
            makeMerged(geometry);
            break;
          case Method.NAIVE:
            makeNaive(geometry);
            break;
        }
      });
  }
  console.timeEnd(api.method + ' (build)');
  var drawCalls = 0;
  switch (api.method) {
    case Method.INSTANCED:
    case Method.MERGED:
      drawCalls = api.mesh_number;
      break;
    case Method.NAIVE:
      drawCalls = api.mesh_number * api.count_per_mesh;
      break;
  }
  guiStatsEl.innerHTML = [
    '<i>GPU draw calls</i>: ' + drawCalls,
    '<i>Face Number</i>: ' + (modelFace * api.mesh_number * api.count_per_mesh),
    '<i>Vertex Number</i>: ' + (modelVertex * api.mesh_number * api.count_per_mesh)
  ].join('<br/>');
}

function makeInstanced(geometry, idx) {
  var matrix = new THREE.Matrix4();
  var mesh = new THREE.InstancedMesh(geometry, material, api.count_per_mesh);

  for (var i = 0; i < api.count_per_mesh; i++) {
    randomizeMatrix(matrix);
    mesh.setMatrixAt(i, matrix);
  }
  scene.add(mesh);
}

function makeMerged(geometry, idx) {
  var instanceGeometry;
  var geometries = [];
  var matrix = new THREE.Matrix4();
  for (var i = 0; i < api.count_per_mesh; i++) {
    randomizeMatrix(matrix);
    var instanceGeometry = geometry.clone();
    instanceGeometry.applyMatrix(matrix);
    geometries.push(instanceGeometry);
  }

  var mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries);
  scene.add(new THREE.Mesh(mergedGeometry, material));
}

function makeNaive(geometry, idx) {
  var matrix = new THREE.Matrix4();
  for (var i = 0; i < api.count_per_mesh; i++) {
    randomizeMatrix(matrix);
    var mesh = new THREE.Mesh(geometry, material);
    mesh.applyMatrix(matrix);
    scene.add(mesh);
  }
}

function init() {
  var width = window.innerWidth;
  var height = window.innerHeight;

  // camera
  camera = new THREE.PerspectiveCamera(70, width / height, 1, 100);
  camera.position.z = 30;

  // renderer
  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  renderer.outputEncoding = THREE.sRGBEncoding;
  container = document.getElementById('container');
  container.appendChild(renderer.domElement);

  // scene
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);

  // controls
  controls = new OrbitControls(camera, renderer.domElement);
  controls.autoRotate = true;

  // stats
  stats = new Stats();
  container.appendChild(stats.dom);

  // gui
  gui = new GUI();
  gui.add(api, 'method', Method).onChange(initMesh);
  gui.add(api, 'count_per_mesh', 1, 20000).step(1).onChange(initMesh);
  gui.add(api, 'mesh_number', 1, 200).step(1).onChange(initMesh);
  var perfFolder = gui.addFolder('Performance');
  guiStatsEl = document.createElement('li');
  guiStatsEl.classList.add('gui-stats');
  perfFolder.__ul.appendChild(guiStatsEl);
  perfFolder.open();
  // listeners
  window.addEventListener('resize', onWindowResize, false);
  Object.assign(window, {
    scene
  });
}

//
function onWindowResize() {
  var width = window.innerWidth;
  var height = window.innerHeight;
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
}

function animate() {
  requestAnimationFrame(animate);
  controls.update();
  stats.update();
  render();
}

function render() {
  renderer.render(scene, camera);
}

//
function getGeometryByteLength(geometry) {
  var total = 0;
  if (geometry.index) total += geometry.index.array.byteLength;
  for (var name in geometry.attributes) {
    total += geometry.attributes[name].array.byteLength;
  }
  return total;
}
// Source: https://stackoverflow.com/a/18650828/1314762
function formatBytes(bytes, decimals) {
  if (bytes === 0) return '0 bytes';
  var k = 1024;
  var dm = decimals < 0 ? 0 : decimals;
  var sizes = ['bytes', 'KB', 'MB'];
  var i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
</script>


person caxieyou110    schedule 20.01.2020    source แหล่งที่มา
comment
AFAIK GPU ส่วนใหญ่ไม่ได้ทำอินสแตนซ์การวาดภาพในฮาร์ดแวร์ เป็นการเพิ่มประสิทธิภาพซอฟต์แวร์ คุณต้องโทรเข้าระบบเพียงครั้งเดียวเพื่อดึง N สิ่งผ่านการโทร N ไดรเวอร์ภายในยังคงตั้งค่า GPU หนึ่งครั้งสำหรับแต่ละอินสแตนซ์ ดังนั้นจึงสามารถทำการเรียก N Draw ภายในได้อย่างมีประสิทธิภาพ... ด้วยรูปทรงเรขาคณิตที่ผสานเข้าด้วยกัน คุณจะทำการเรียกเพียงครั้งเดียว ไดรเวอร์จะทำการตั้งค่าเพียงครั้งเดียว ดังนั้นการผสานโดยทั่วไปจะเร็วขึ้น สำหรับเจ้าของภาษาที่เร็วกว่านั้นฟังดูผิดปกติ โดยเฉพาะถ้าคุณวาดวัตถุ 8000 ชิ้น โพสต์โค้ด ในคำถาม   -  person gman    schedule 21.01.2020
comment
ขอบคุณสำหรับการตอบกลับ ตามที่คุณพูด ดูเหมือนว่าการ์ดกราฟิกแบบรวมไม่ได้ช่วยอะไรกับอินสแตนซ์นี้ ฉันควรข้ามไปทั้งหมดเลยดีไหม ฉันได้แนบโค้ดนี้มาด้วย ซึ่งเป็นเพียงการอัปเดตง่ายๆ สำหรับโค้ดการทดสอบเริ่มต้น เข้ากับประสิทธิภาพจริงๆ   -  person caxieyou110    schedule 21.01.2020
comment
ดูวิธีการทำให้โค้ดทำงานได้สำหรับคำถามในอนาคต   -  person gman    schedule 21.01.2020


คำตอบ (1)


นี่เป็นเพียงการคาดเดาเท่านั้น

  1. โดยค่าเริ่มต้น Three.js จะคัดเลือกหากสิ่งต่าง ๆ อยู่นอก frustum

    เราสามารถปิดสิ่งนี้ได้ด้วย mesh.frustumCulled = false ฉันไม่ได้สังเกตเห็นความแตกต่างและสิ่งนี้ควรจะปรากฏในจำนวนการจับรางวัล

  2. Three.js โดยค่าเริ่มต้นจะเรียงลำดับวัตถุทึบแสงกลับไปด้านหน้า

    ซึ่งหมายความว่าทุกสิ่งทุกอย่างที่เท่ากันและเรียงลำดับจะทำงานได้เร็วกว่าไม่เรียงลำดับเนื่องจากการทดสอบเชิงลึก ถ้าฉันตั้งค่าการทดสอบเชิงลึกเป็นเสมอ

    material.depthFunc = THREE.AlwaysDepth
    

    ดูเหมือนว่าฉันจะเรนเดอร์เร็วขึ้นเล็กน้อยด้วยอินสแตนซ์เทียบกับเนทิฟ แน่นอนว่าทุกสิ่งทุกอย่างไม่เท่ากัน

  3. ปัญหาใน Chrome

    ถ้าฉันทำงานใน Firefox หรือ Safari ฉันจะได้รับผลลัพธ์ตามที่คาดหวัง ผสาน > อินสแตนซ์ > ดั้งเดิม

    อาจเป็นข้อบกพร่องหรืออาจเป็นได้ว่าพวกเขากำลังแก้ไขปัญหาไดรเวอร์หรือปัญหาด้านความปลอดภัยที่เบราว์เซอร์อื่นไม่ใช่ คุณจะต้องถาม

person gman    schedule 21.01.2020
comment
ดูเหมือนว่าในกรณีนี้ ฉันหมายถึงว่าฉันพยายามใช้ Firefox แล้วประสิทธิภาพดูเหมือนจะถูกต้อง นี่เป็นข้อบกพร่องของ Chrome ใช่ไหม นอกจากนี้ ฉันพยายามใช้ Chrome กับการ์ดกราฟิกแยก ดูเหมือนว่าจะเป็นแบบผสาน › อินสแตนซ์ › เนทิฟด้วย มีสายจังเลย - person caxieyou110; 21.01.2020