2017-06-08 2 views
1

J'essaie de créer des étiquettes textuelles. J'ai besoin d'utiliser du matériau shader pour mieux contrôler l'étiquette pendant le rendu.three.js matériau de shader de gestion de la mémoire

J'ai remarqué que la mémoire ne cesse d'augmenter même si je nettoie les vieilles étiquettes.

J'ai créé un exemple jsFiddle qui est semblable à: https://threejs.org/examples/#webgl_test_memory

Le code suivant utilise un objet de toile pour générer une texture qui contient le texte à représenter comme une étiquette:

S'il vous plaît être Attention, ces calculs sont lourds et rendent l'onglet très insensible.

var container; 
 

 
var camera, scene, renderer; 
 

 
var labels; \t \t \t 
 

 
var canvas; 
 

 
init(); 
 
animate(); 
 

 
function init() { 
 

 
    container = document.createElement('div'); 
 
    document.body.appendChild(container); 
 

 
    camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 1, 10000); 
 
    camera.position.z = 200; 
 

 
    scene = new THREE.Scene(); 
 

 
    renderer = new THREE.WebGLRenderer(); 
 
    renderer.setPixelRatio(window.devicePixelRatio); 
 
    renderer.setSize(window.innerWidth, window.innerHeight); 
 
    container.appendChild(renderer.domElement); 
 

 
    labels = new THREE.Object3D(); 
 

 
    canvas = document.createElement('canvas'); 
 
    var context = canvas.getContext('2d'); 
 

 
    // get text metrics 
 
    var fontface = 'Arial'; 
 
    var fontSize = 60; 
 
    context.font = fontSize + "px " + fontface; 
 

 
    var width = context.measureText(text).width; 
 

 
    // add text 
 
    var text = 'abcdef'; 
 
    canvas.width = width; 
 
    canvas.height = fontSize*1.3; 
 
    context.textAlign = "center"; 
 
    context.font = fontSize + "px " + fontface; 
 
    context.fillStyle = "white"; \t \t \t \t 
 
    context.fillText(text, canvas.width/2, canvas.height/2); 
 
} 
 

 
function createLabels() { \t \t \t \t \t \t \t 
 
    for(var i = 0; i < 10000 ; i++) { 
 
    createTextMesh(); 
 
    } 
 

 
    scene.add(labels); 
 
} 
 

 
function createTextMesh() { 
 
    // canvas contents will be used for a texture 
 
    var texture = new THREE.Texture(canvas); 
 
    texture.needsUpdate = true; 
 

 
    var uniforms = { 
 
    text: { 
 
     type: 't', 
 
     value: texture 
 
    } 
 
    }; 
 

 
    var material = new THREE.ShaderMaterial({ 
 
    uniforms: uniforms, 
 
    vertexShader: document.getElementById('vertex-shader').textContent, 
 
    fragmentShader: document.getElementById('fragment-shader').textContent 
 
    }); 
 

 
    var geometry = new THREE.PlaneBufferGeometry(15, 15); 
 

 
    var label = new THREE.Mesh(geometry, material); 
 

 
    labels.add(label); 
 
} 
 

 
function clearLabels() { 
 
    for(var i = 0; i < labels.children.length; i++) { 
 
    var label = labels.children[i]; 
 

 
    if(label.material.uniforms) { 
 
     label.material.uniforms.text.value.dispose(); 
 
    } 
 
    label.material.dispose(); 
 
    label.geometry.dispose(); 
 

 
    labels.remove(label); \t \t \t \t \t 
 
    } 
 

 
    scene.remove(labels); 
 
} 
 

 
function animate() { 
 
    requestAnimationFrame(animate); 
 
    render(); 
 
} 
 

 
function render() { 
 
    // build GL objects 
 
    createLabels(); 
 

 
    renderer.render(scene, camera); 
 

 
    // clean up \t 
 
    clearLabels(); 
 
}
body { 
 
    margin:0; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/85/three.min.js"></script> \t \t \t \t \t 
 
<script id="fragment-shader" type="x-shader/x-fragment"> 
 
    uniform sampler2D text; 
 

 
    varying vec2 vUv; 
 

 
    void main() { 
 
    vec4 finalColor = texture2D(text, vUv); \t \t \t 
 
    gl_FragColor = finalColor; 
 
    } 
 
</script> \t \t 
 
<script id="vertex-shader" type="x-shader/x-fragment"> 
 
    varying vec2 vUv; 
 

 
    void main() { 
 
    vUv = uv; 
 
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); 
 
    gl_Position = projectionMatrix * mvPosition; 
 
    } 
 
</script> \t \t \t 
 
<canvas></canvas>

Vous pouvez utiliser les chromes de dev outils pour évaluer l'augmentation de l'utilisation de la mémoire perceptuelle.

Je vous recommande d'utiliser quelque chose comme le propre gestionnaire de tâches de Windows pour voir l'augmentation de la mémoire.

Vous pouvez réduire la vitesse de création d'étiquettes, bien que cela signifie naturellement que l'affichage de l'onglet prendra plus de temps. Est-ce que je fais le nettoyage de la ressource à tort?

Vive

+0

@marques: qu'est-ce que vous voulez exactement? Pouvez-vous s'il vous plaît spécifier clairement. – spankajd

+0

le problème que j'essaie de résoudre nécessite l'application pour mettre à jour plusieurs étiquettes de texte par minute. J'ai remarqué qu'après un certain temps l'onglet mourrait, mais je ne pouvais pas comprendre pourquoi. Je crois qu'il y a une fuite de mémoire quelque part et je l'ai réduit à cette fonctionnalité de three.js. – rmarques

+0

Non, il y a un problème avec votre code. vous utilisez la fonction requestAnimationFrame qui appelle en continu la fonction animate et dans cette fonction, vous utilisez pour loop qui itère 1000 fois. – spankajd

Répondre

0

Salut essayer le code suivant comme je l'ai ajouté intervalle de temps en fonction Animer qui appelle la fonction rendre.

il y a une variable fps actuellement 24, vous pouvez le changer.

<!DOCTYPE html> 
 
<html lang="en"> 
 
\t <head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
 
\t \t <title>three.js - shader material memory leak</title> \t \t 
 
\t \t <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> 
 
\t \t <style> 
 
\t \t \t body { 
 
\t \t \t \t background:#fff; 
 
\t \t \t \t padding:0; 
 
\t \t \t \t margin:0; 
 
\t \t \t \t overflow:hidden; 
 
\t \t \t } 
 
\t \t </style> 
 
\t </head> 
 
\t <body> 
 
\t \t <script src="https://threejs.org/build/three.js"></script> \t \t \t \t \t 
 
\t \t <script id="fragment-shader" type="x-shader/x-fragment"> 
 
\t \t \t uniform sampler2D text; 
 

 
\t \t \t varying vec2 vUv; 
 

 
\t \t \t void main() { 
 
\t \t \t \t vec4 finalColor = texture2D(text, vUv); \t \t \t 
 
\t \t \t \t gl_FragColor = finalColor; 
 
\t \t \t } 
 
\t \t </script> \t \t 
 
\t \t <script id="vertex-shader" type="x-shader/x-fragment"> 
 
\t \t \t varying vec2 vUv; 
 

 
\t \t \t void main() { 
 
\t \t \t \t vUv = uv; 
 
\t \t \t \t vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); 
 
\t \t \t \t gl_Position = projectionMatrix * mvPosition; 
 
\t \t \t } 
 
\t \t </script> \t \t \t 
 
\t \t <script> 
 
\t \t \t var container, 
 
       camera, scene, renderer, 
 
       labels, 
 
       canvas, 
 
       lastTime, fps = 24; 
 
\t \t \t 
 
\t \t \t init(); 
 
\t \t \t animate(); 
 

 
\t \t \t function init() { 
 

 
\t \t \t \t container = document.createElement('div'); 
 
\t \t \t \t document.body.appendChild(container); 
 

 
\t \t \t \t camera = new THREE.PerspectiveCamera(60, window.innerWidth/window.innerHeight, 1, 10000); 
 
\t \t \t \t camera.position.z = 200; 
 

 
\t \t \t \t scene = new THREE.Scene(); 
 

 
\t \t \t \t renderer = new THREE.WebGLRenderer(); 
 
\t \t \t \t renderer.setPixelRatio(window.devicePixelRatio); 
 
\t \t \t \t renderer.setSize(window.innerWidth, window.innerHeight); 
 
\t \t \t \t container.appendChild(renderer.domElement); 
 
\t \t \t \t 
 
\t \t \t \t labels = new THREE.Object3D(); 
 
\t \t \t \t 
 
\t \t \t \t canvas = document.createElement('canvas'); 
 
\t \t \t \t var context = canvas.getContext('2d'); 
 

 
\t \t \t \t // get text metrics 
 
\t \t \t \t var fontface = 'Arial'; 
 
\t \t \t \t var fontSize = 60; 
 
\t \t \t \t context.font = fontSize + "px " + fontface; 
 

 
\t \t \t \t var width = context.measureText(text).width; 
 

 
\t \t \t \t // add text 
 
\t \t \t \t var text = 'abcdef'; 
 
\t \t \t \t canvas.width = width; 
 
\t \t \t \t canvas.height = fontSize*1.3; 
 
\t \t \t \t context.textAlign = "center"; 
 
\t \t \t \t context.font = fontSize + "px " + fontface; 
 
\t \t \t \t context.fillStyle = "white"; \t \t \t \t 
 
\t \t \t \t context.fillText(text, canvas.width/2, canvas.height/2); 
 
\t \t \t } 
 

 
\t \t \t function createLabels() { \t \t \t \t \t \t \t 
 
\t \t \t \t for(var i = 0; i < 10000 ; i++) { 
 
\t \t \t \t \t createTextMesh(); 
 
\t \t \t \t } 
 
\t \t \t \t 
 
\t \t \t \t scene.add(labels); 
 
\t \t \t } 
 
\t \t \t 
 
\t \t \t function createTextMesh() { 
 
\t \t \t \t // canvas contents will be used for a texture 
 
\t \t \t \t var texture = new THREE.Texture(canvas); 
 
\t \t \t \t texture.needsUpdate = true; 
 
\t \t \t \t 
 
\t \t \t \t var uniforms = { 
 
\t \t \t \t \t text: { 
 
\t \t \t \t \t \t type: 't', 
 
\t \t \t \t \t \t value: texture 
 
\t \t \t \t \t } 
 
\t \t \t \t }; 
 

 
\t \t \t \t var material = new THREE.ShaderMaterial({ 
 
\t \t \t \t \t uniforms: uniforms, 
 
\t \t \t \t \t vertexShader: document.getElementById('vertex-shader').textContent, 
 
\t \t \t \t \t fragmentShader: document.getElementById('fragment-shader').textContent 
 
\t \t \t \t }); 
 
\t \t \t \t 
 
\t \t \t \t var geometry = new THREE.PlaneBufferGeometry(15, 15); 
 

 
\t \t \t \t var label = new THREE.Mesh(geometry, material); 
 
\t \t \t \t 
 
\t \t \t \t labels.add(label); 
 
\t \t \t } 
 
\t \t \t 
 
\t \t \t function clearLabels() { 
 
\t \t \t \t for(var i = 0; i < labels.children.length; i++) { 
 
\t \t \t \t \t var label = labels.children[i]; 
 

 
\t \t \t \t \t if(label.material.uniforms) { 
 
\t \t \t \t \t \t label.material.uniforms.text.value.dispose(); 
 
\t \t \t \t \t } 
 
\t \t \t \t \t label.material.dispose(); 
 
\t \t \t \t \t label.geometry.dispose(); 
 
\t \t \t \t 
 
\t \t \t \t \t labels.remove(label); \t \t \t \t \t 
 
\t \t \t \t } 
 
\t \t \t \t 
 
\t \t \t \t scene.remove(labels); 
 
\t \t \t } 
 
\t \t \t // Add time gap render will call with 24 fps. or you can slow down speed to check. 
 
\t \t \t function animate() { 
 
\t \t \t \t   var curTime = new Date(); 
 
          if(lastTime === undefined || (Math.round(curTime - lastTime)/1000) >= fps) 
 
          { 
 
\t \t \t \t     render(); 
 
            lastTime = curTime; 
 
          } 
 
          requestAnimationFrame(animate); 
 
\t \t \t } 
 

 
\t \t \t function render() { 
 
\t \t \t \t // build GL objects 
 
\t \t \t \t createLabels(); 
 

 
\t \t \t \t renderer.render(scene, camera); 
 

 
\t \t \t \t // clean up \t 
 
\t \t \t \t clearLabels(); 
 
\t \t \t } 
 

 
\t \t </script> 
 
\t \t <div> 
 
\t \t \t <canvas width="1920" height="974" style="width: 1920px; height: 974px;"></canvas> 
 
\t \t </div> 
 
\t </body> 
 
</html>

+0

Votre code génère toujours une fuite de mémoire et l'onglet meurt toujours. Cela prend juste plus de temps pour le faire. – rmarques

0

Ne pas créer un nouveau maillage 1000 fois chaque tick, les mettre en commun. Ne créez pas 1000 géométries de même plan. C'est probablement le plus grand coupable ici. Créez-en un seul, et transmettez-le aux maillages. Textures, je ne suis pas si sûr de celui-là. Je pense que vous n'êtes pas censé créer une nouvelle texture à partir du contexte de toile, créer une fois et mettre à jour le canevas. Cela va disparaître avec les changements, mais il est bon de noter que pour les performances, vous voudriez éviter de créer également cet objet uniforme dans la boucle.

EDIT

vous créez beaucoup de choses. La largeur de l'étiquette est de 263, et si le journal de la console de trois doit être approuvé, il est redimensionné en 256x64. Donc, vous vous retrouvez avec 480mb de données, bien que, avec le canal alpha, il pourrait être 600mb. Je suppose que votre démo n'atteint jamais la partie élimination. Il a juste écrasé le navigateur de ma part. Trois se plaint également des textures étant NPOT, ainsi il essaye d'écrire dix mille consoles de console.

+0

Ce code a été conçu pour tester l'existence d'une fuite de mémoire, et de le faire rapidement. Ce n'est pas une application de scénario du monde réel. Il a été construit pour imiter la structure de: https://threejs.org/examples/#webgl_test_memory. – rmarques

+0

Je vois, vous essayez de stress tester ceci et confirmer que vous disposez des ressources correctement. Cela semble être la bonne façon de disposer des choses. – pailhead

+0

Je ne connais pas grand-chose à ce sujet mais ne pourrais-je pas dire que quelque chose ne se déclenche pas assez rapidement? Vous émettez beaucoup de commandes GL avec ce code. Avec javascripts GC, si vous libérez des objets correctement, il se bloquerait à un certain point, exécuterait le GC plus longtemps, et ne planterait pas? J'imagine que la gestion du GPU ne fait pas partie de cela, que d'une manière ou d'une autre elle se heurte? – pailhead