WebGL fuera del hilo principal

7 abril, 2016 4:27 por

Esta es una traducción del artículo original publicado en el blog de Mozilla Hacks.

Estamos felices de anunciar WebGL en los Web Workers, en Firefox 44+! Usando el nuevo API OffScreenCanvas puedes crear un contexto WebGL fuera del hilo principal.

Para seguir adelante, necesitarás una copia de Firefox 44 o superior. Tienes que activar esta API, navegando a about:config en la configuración, y buscando gfx.offscreencanvas.enabled y dándole un valor de true. Puedes conseguir el código de ejemplo desde GitHub o verlo desde aquí en Firefox 44+ con la configuración activa.

Casos de uso

Esta API es la primera que permite un hilo aparte del principal cambiar lo que el usuario ve. Esto permite al renderizado progresar sin importar lo que este pasando en el hilo principal. Puedes ver más casos de uso en las especificaciones en curso del grupo de trabajo.

Cambios de código

Vamos a mirar un poco un ejemplo básico de animación en WebGL de mi charla de Raw WebGL. Vamos a portar el código para correrlo en un Worker, en vez del hilo principal.

Rotación 3D

El primer paso es mover todo el código desde el contexto de WebGL para dibujar las llamadas en un archivo separado.

  // hilo principal
  var canvas = document.getElementById('myCanvas');
  ...
  gl.useProgram(program);
  ...

Se convierte en:

// hilo principal
var canvas = document.getElementById('myCanvas');
if (!('transferControlToOffscreen' in canvas)) {
  throw new Error('No hay soporte de webgl en workers');
}
var offscreen = canvas.transferControlToOffscreen();
var worker = new Worker('worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
...

Cabe destacar que estamos llamando HTMLCanvasElement.prototype.transferControlToOffscreen, y luego transfiriendo eso a un nuevo hilo worker. transferControlToOffscreen retorna un nuevo objeto el cual es una instancia de OffscreenCanvas, en vez de HTMLCanvasElement. Mientras es similar, no puedes acceder a propiedades como offscreen.clientWidth y offscreen.clientHeight, pero puedes acceder a offscreen.width y offscreen.height. Pasándole como segundo argumento a postMessage,  transferimos el dominio de la variable al segundo hilo.

Ahora en el segundo hilo, debemos esperar a recibir el mensaje desde el hilo principal con el elemento canvas, antes de intentar obtener un contexto WebGL. El código para obtener un contexto WebGL, crear y llenar buffers, obtener configuraciones, atributos, y uniformidades, y dibujar, no cambia.

// thread de worker
importScripts('gl-matrix.js');

onmessage = function (e) {
  if (e.data.canvas) {
    createContext(e.data.canvas);
  }
};

function createContext (canvas) {
  var gl = canvas.getContext('webgl');
  ...

OffScreenCanvas simplemente agrega un nuevo método a WebGLRenderingContext.prototype llamado commit. El método commit va a enviar la imagen renderizada al elemento canvas que creó el OffscreenCanvas usado por el contexto WebGL.

Sincronización de animación

Ahora para agregar animación al código, podemos hacer una petición a los tiempos de  requestAnimationFrame desde el hilo principal al Worker con postMessage.

// hilo principal
(function tick (t) {
  worker.postMessage({ rAF: t });
  requestAnimationFrame(tick);
})(performance.now());

y el mensaje en el Worker se convierte en:

// hilo de worker
onmessage = function (e) {
  if (e.data.rAF && render) {
    render(e.data.rAF);
  } else if (e.data.canvas) {
    createContext(e.data.canvas);
  }
};

Y nuestra función de render, ahora tiene un final gl.commit(); en lugar de configurar otro loop de requestAnimationFrame.

// hilo principal
function render (dt) {
  // actualizar
  ...
  // renderizar
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, n);
  requestAnimationFrame(render);
};

Se convierte en:

// hilo de worker
function render (dt) {
  // actualizar
  ...
  // renderizar
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, n);
  gl.commit(); // nuevo para webgl en Workers
};

Limitaciones de este método

Mientras que en el código de ejemplo no estoy haciendo una animación basada en velocidad, (no usando los valores pasados desde requestAnimationFrame, estoy haciendo los cuadros por segundo, dependiendo de la animación, en contraposición a la animación correcta, basada en velocidad, que es independiente de los cuadros por segundo), aún tenemos un inconveniente con este método.

Asumamos que movemos la lógica de renderizado fuera del hilo principal, para evitar pausas del recolector de basura de la máquina virtual JavaScript (pausas GC). Las pausas GC en el hilo principal van a hacer más lentas las invocaciones de requestAnimationFrame. Dado que los llamados a  requestAnimationFramegl.drawArrays son activados asíncronamente en el hilo del worker, por postMessage en un loop requestAnimationFrame del hilo principal, las pausas GC en el hilo principal bloquean el renderizado en el hilo del worker. Nota: las pausas GC en el hilo principal no deberían bloquear el progreso en el hilo del worker (al menos no lo hacen en la máquina virtual de SpiderMonkey en Firefox). Las pausas GC suceden por cada Worker en SpiderMonkey.

Mientras que podemos intentar hacer algo inteligente con el Worker para mejorar esto, la solución sería hacer requestAnimationFrame disponible en el contexto del Worker. El tracker de bugs de este trabajo esta aquí.

Resumiendo

Los desarrolladores pueden ahora renderizar en la pantalla, sin bloquear el hilo principal, gracias al nuevo API OffscreenCanvas. Hay más trabajo qué hacer para obtener requestAnimationFrame  en los Workers. Pude portar código existente WebGL para correr en un worker en unos cuantos minutos. En comparación vean animation.html vs. animation-worker.html y worker.js.

The following two tabs change content below.
Si, mi biografia esta vacia. 🙂

Compartir artículo:

Join the discussion at foro.mozilla-hispano.org

  • ¡Participa!

    Firefox Friends »
    Agrega botones de Firefox en tu sitio web y comparte tu amor por Mozilla Firefox.
    Armada alucinante »
    Ayuda a otros usuarios en Twitter.
    Colabora con la comunidad »
    En Mozilla lo importante son las personas. Descubre cómo puedes colaborar.

    Boletín Firefox

    Suscríbete al boletín de novedades de Firefox.

  • Descargas

    Descarga los programas de Mozilla.

    Lo más visto

    cc-by-sa