Managing memory in three.js through shader materials

My goal is to create textual labels using shader material for better control during rendering.

I've observed that the memory usage keeps increasing despite cleaning up old labels.

I have a jsfiddle example similar to:

The code below utilizes a canvas object to generate a texture containing the text to be displayed as a label:

Please note, these computations are intensive and may cause the tab to become unresponsive.

var container;

var camera, scene, renderer;

var labels;

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";
  context.fillText(text, canvas.width / 2, canvas.height / 2);
}

function createLabels() {
  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);
  }

  scene.remove( labels );
}

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

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

  renderer.render( scene, camera );

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

  varying vec2 vUv;

  void main() {
    vec4 finalColor = texture2D(text, vUv);
    gl_FragColor = finalColor;
  }
</script>
<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>
<canvas></canvas>

You can use Chrome's dev tools to monitor the memory usage increase.

I suggest using a tool like Windows Task Manager to track memory changes.

You can reduce the label creation speed to prevent the tab from running out of memory quickly.

Am I handling resource cleanup incorrectly?

Cheers

Answer №1

To optimize performance, avoid creating a new mesh 1000 times every tick by pooling them instead. Create just one geometry for all similar planes and pass it to multiple meshes. When using textures, try not to create a new texture from the canvas context each time. Create it once and update the canvas as needed.

Additionally, minimize unnecessary object creation in loops for better performance.

UPDATE

It seems like you are generating a large amount of data with a label width of 263 being resized to 256x64, potentially leading to browser crashes due to excessive memory usage. Three.js shows warnings about NPOT textures, resulting in numerous console logs being written out unnecessarily.

Answer №2

Check out the code below to see how I added a time gap in the animate function, which then calls the render function.

You have the option to change the current variable fps (currently set at 24).

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

varying vec2 vUv;

void main() {
vec4 finalColor = texture2D(text, vUv);
gl_FragColor = finalColor;
}
</script>
<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>
...

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Create a shop without relying on Vuex

I'm currently trying to wrap my head around Vuex, but as I'm still new to the world of VueJS, I'm finding it difficult to grasp. As a result, I'm on the hunt for an alternative solution. My goal is to efficiently share data between tw ...

Show information from a table row within a modal box using Bootstrap 4

Is there a way to show table row td data in a Bootstrap 4 modal after clicking on the show data link? Here is the HTML structure: <table class="table table-bordered"> <tbody> <tr> <td data-title=& ...

What is the best way to retrieve the scope of ng-repeat from another directive located on the same element as ng-repeat?

Is it possible to access a property from the ng-repeat scope in another directive within the same element as the ng-repeat directive? For instance, I would like to be able to use the child.class property in this scenario: <div ng-class="{{ child.class ...

Utilize Chart.js to showcase vertical axis labels on a line chart

Is there a way to include a Y-axis label for a line graph created using chart.js and angular-chart.js? I am looking to add a y-axis label to my graph. HTML <ion-content ng-controller="AgeController"> <canvas id="line" class="chart chart-line" da ...

Permanently removing artwork from the Canvas

I am facing an issue with my canvas drawing function. Whenever I clear the drawings on the background image by clicking a button, the old drawings reappear when I try to draw again. How can I permanently delete the cleared drawings so that I can start fr ...

Error message: Invalid input for directive, only numeric values are accepted

I need help with a directive that restricts non-numeric symbols in an input field. Below is the code for the directive: import { NgControl } from "@angular/forms"; import { HostListener, Directive } from "@angular/core"; @Direct ...

Add a checkbox element to a web API using ReactJS

I'm currently learning react and encountering an issue with checkboxes that I can't seem to resolve. I am working on a modal for updating and inserting data in a .net core web api, which is functioning correctly. However, within the app, I'm ...

Tips for ensuring my buttons are non-functional to clicks

I am looking to create buttons with the class btnd that are active but not clickable by users. You can view the current output here: http://jsfiddle.net/6VrXD/44/ ...

Implementing Ajax calls to dynamically update Datatable results

Currently integrating Datatables.js with an ajax call for data retrieval. The json response I'm receiving is as follows: "success":true,"message":"","items":[{"id":"1","ip_address":"127.0.0.1","email... My data is stored within the data.items array ...

jquery persistently selects element even after altering the class

Having an issue with a button that needs to change its function after meeting a certain condition. I am attempting to select the button by its class, remove that class when the condition is met, add a new class, and then perform another action. However, i ...

Implementing a filter on retrieved data from an API in a React application

I'm retrieving information from this Api After fetching the data, I need to check if the gender is Male in order to display a Male Icon. How can I perform this check on the Api data and return some HTML data like an img tag or something to show the i ...

Issue encountered while utilizing JQueryUI alongside TypeScript and the definition file from DefinitelyTyped

Currently, I'm attempting to incorporate JQueryUI with TypeScript by first installing JQueryUI using npm install jquery-ui-dist, and then installing JQuery with npm install jquery. Additionally, I have included the definition files from DefinitelyType ...

Troubleshooting why passing $var within @{{ }} in Vue.js + Laravel is not functioning as expected

I am integrating Algolia Vue-InstantSearch into a Laravel Project. Since we are working with a blade file, the {{ }} symbols will be recognized by the php template engine. To ensure that they are only understood by Vue in JavaScript, we can add an @ prefix ...

Implement the same reference functionality in a React Function component that is seen in a React Class component

Is it possible to replicate the same functionality of class component ref in a function component? Here's an example: const AnotherComponent = () => { const DoSomething = () => console.log(something); return <div> There is some co ...

In TypeScript, both 'module' and 'define' are nowhere to be found

When I transpile my TypeScript using "-m umd" for a project that includes server, client, and shared code, I encounter an issue where the client-side code does not work in the browser. Strangely, no errors are displayed in the browser console, and breakpoi ...

Altering information with the use of history.pushState throughout time

I've created a template that I want to load using the jQuery .load() function. However, during testing, I discovered that it's not loading at all. Here is the code I'm trying to use for loading: function open() { history.p ...

Including additional data in JSON objects

For example: I have an object named tempobj with sample data at indexes tempobj[0] and tempobj[1]. Now, I would like to include additional information like name and status in this object. tempobj["info"]["name"] = "title"; tempobj["info"]["id"] = "23243 ...

Chart Vue, Component

I am currently working with a chart implemented using vue-chart. I am facing a challenge where I need to populate the mydata array with data retrieved from the store.js file. I want to automatically push this data into the mydata array without the need for ...

Asynchronous POST request in Chrome Extension's popup.js

Apologies for asking again, but has anyone encountered a similar issue? I have a popup.js file with a login form that sends a request to an API to retrieve an API key for user authentication. When I set the request as synchronous (async: false), everythin ...

Tips for Allowing Multiple Exports in a Vue 3 Single File Component

I am currently working on a Vue3 Single File Component for a customized list. Within this single file component, my objective is to export the main default Vue component along with an enum that declares the type of list it represents: child: <template& ...