Pixi.js: Demasiados contextos WebGL activos

Creado en 11 dic. 2015  ·  29Comentarios  ·  Fuente: pixijs/pixi.js

Estoy creando una aplicación integrada con React que carga varios experimentos individuales, en pixi.js, three.js, etc. Como no puedo usar un iframe para cargar los experimentos, necesito limpiar después de mí.

Cada vez que hay una navegación a un nuevo experimento, verifico si existe un renderizador o no, y si existe, lo estoy destruyendo).

código ficticio

if renderer
    renderer.destroy(true);
    renderer = null

renderer = new PIXI.autoDetectRenderer ......

Parece que estoy limpiando correctamente mi renderizador webgl, sin embargo, después de que se cargan 16 páginas, aparece la advertencia que dice WARNING: Too many active WebGL contexts. Oldest context will be lost. Esto no debería ser un problema en el escritorio ya que solo tenemos una advertencia, pero en el ipad se bloquea el navegador y aparece un mensaje: X A problem occurred with this web page, so it was reloaded. .

Estaba viendo threejs y cómo implementan una solución para esto, y tienen el siguiente código

¿Estoy haciendo algo tonto?

Comentario más útil

Creo que encontré una buena solución. Esto me permite tener componentes de React superpuestos en el mapa que no tienen que conocerse entre sí y permiten que React posea el DOM.

Utiliza esta técnica para forzar la pérdida de contexto:

gl.getExtension('WEBGL_lose_context').loseContext();

Aquí tienes un ejemplo completo:

var document = require('global/document');
var PIXI = require('pixi.js');

// I want to keep this canvas / renderer around. For example, this might be a bottom layer PIXI / React map overlay.
var canvas1 = document.createElement('canvas');
document.body.appendChild(canvas1);
createRenderer(0xff0000, canvas1);

function createRenderer(color, canvas) {
  canvas = canvas || document.createElement('canvas');
  var renderer = new PIXI.WebGLRenderer(800, 600, {view: canvas});
  var stage = new PIXI.Container();
  var graphics = new PIXI.Graphics();
  graphics.beginFill(color, 0.5);
  graphics.drawCircle(0, 0, 200);
  graphics.endFill();
  stage.addChild(graphics);
  renderer.render(stage);
  return {renderer: renderer, stage: stage, graphics: graphics};
}

// Simulate frequent adding / removing of lots of PIXI / React map overlays on top.
for (var i = 0; i < 16; i++) {
  var canvas = document.createElement('canvas');
  document.body.appendChild(canvas);
  var scene = createRenderer(0x00ff00, canvas);
  // Uncomment to see that the original canvas isn't removed.
  /* scene.renderer.currentRenderTarget.gl
      .getExtension('WEBGL_lose_context').loseContext(); */
  scene.renderer.destroy();
  scene.stage.removeChild(scene.graphics);
  document.body.removeChild(canvas);
}

¿Quizás PIXI debería agregar esto a su propio método de "destrucción"?

Todos 29 comentarios

@andrevenancio
Sin embargo, no soy un experto en Pixi, ya que solo lo he estado usando desde el lunes o martes. Si tiene varios renderizadores y lienzos debido a muchos experimentos ( como este ) en una idea de que solo se necesita acceder a uno a la vez, tendría un renderizador global u otro, y mantendría los experimentos en una matriz. Luego, cuando necesite mostrarlo, simplemente pase la etapa de cada experimento al renderizador.

var renderer = PIXI.autoDetectRenderer({blah});
var experiments = [];
experiments.push(experiment1);
experiments.push(experiment2);
experiments.push(experiment3);

var currentExperiment = $('#experminetSelector').val();
renderer.render(experiments[currentExperiment]);

De esta manera, solo crea el renderizador una vez y lo pasa por la etapa de cada experimento. Aunque esto solo funciona si está tratando de renderizar solo una página y tiene todos los experimentos en ella en lugar de tener muchas páginas de experimentos diferentes.

DESCARGO DE RESPONSABILIDAD: Soy nuevo en pixi y no probé esto, su millaje puede variar. Además, todavía suena como si encontraras algún tipo de error que tal vez los desarrolladores quieran investigar o dar alguna orientación.

Tener un solo renderizador y múltiples escenas debería estar bien. 1 renderizador === 1 elemento de lienzo.

Hola chicos, gracias por tus comentarios. @samuraiseoul Lo que estás sugiriendo no es tonto en absoluto, y así es como normalmente procedería para tener varios experimentos en un marco ... sin embargo, tendré que hacer otra solución ya que los experimentos se pueden hacer usando pixi.js threejs o webgl simple. Además de eso, debido al tipo de transiciones ya definidas en la estructura de reacción, en un punto verá el experimento antiguo y el nuevo experimento al mismo tiempo, renderizándose simultáneamente, por lo que la idea de un renderizador ganó no funciona ...

Además ... esto es una solución para el problema, no una solución en sí misma ... Si el método .destroy () en realidad no destruye la referencia al contexto webgl ... ¿cuál es el punto? :(

¡Amigo! Totalmente de acuerdo en que la función de destrucción no está haciendo exactamente lo que se supone que debe hacer si los contextos no se están destruyendo :)

Yo deambulo, ¿estás usando autodetectRenderer ? Podría ser que el contexto de prueba que creamos para detectar si el navegador es compatible con webGL no se está destruyendo.

Alguna información adicional: no puede destruir un contexto webgl, son basura recolectada (a menos que algo haya cambiado)

Creo que eliminamos las referencias que tenemos y, opcionalmente, eliminamos el elemento de lienzo (que es necesario para la limpieza del contexto), pero luego depende del navegador hacer la limpieza. Es posible que los contextos se filtren o que se creen más rápido de lo que desea el navegador. Sinceramente, no estoy seguro.

¿Las herramientas de desarrollo indican fugas en contextos como este? ¿Quizás eso pueda ayudar? Parece que algo está manteniendo el contexto alrededor ... ¿Quizás una referencia js al contexto o una referencia js al lienzo subyacente?

Es por eso que todavía amo los iframes pase lo que pase.

@GoodBoyDigital Estoy usando autodetectRenderer así

<strong i="9">@renderer</strong> = new PIXI.autoDetectRenderer canvas.width, canvas.height, { view: canvas, antialias: false, backgroundColor: 0xffffff }

Estoy estacionando esto por ahora, les haré saber cuál es la solución cuando encontré una ... Solo estaba pensando que forzar la pérdida de ese contexto, tal vez obligaría a la recolección de basura a limpiarlo ... pero tampoco estoy seguro. :)

Tengo el mismo problema. Lo que estoy construyendo requiere que Pixi sea destruido y reconstruido con bastante frecuencia y me preocupan las implicaciones de la memoria. Cada instancia de WebGL es bastante pequeña en este momento, pero se hará mucho más grande a medida que continuemos construyéndola.

¿Cualquier progreso?

Para ser claros, estoy usando Phaser, pero parece estar llamando al método renderer.destroy de Pixi.

sin suerte todavía. Si puede usar un renderizador global y simplemente renderiza diferentes escenas en él, o simplemente carga contenido a través de un iframe y eso elimina adecuadamente lo que esté colgando.

Oigan, sí, esto es un poco complicado. Por lo que puedo ver, hacemos todo lo posible para destruir un contexto. Entonces, todas las texturas / programas, arraybuffers, etc., se destruyen. Lo que podríamos hacer es agrupar los contextos una vez que se hayan destruido para reutilizarlos en lugar de crear uno nuevo cada vez.

hey @GoodBoyDigital en mi caso tenía una animación de entrada / salida, así que siempre necesitaría 2 contextos ... ese es el problema con la integración con React y no hacerlo pixi completo ... de todos modos, no estoy seguro de que este sea un error que puedas arreglar por decir ... creo que es más como si el navegador administra los contextos ... ni siquiera sé si este truco de three.js funciona ... tal vez pruébelo en su V4 e intente abrir más de 16 pestañas con pixi https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L289

De lo contrario, no se preocupe amigo, ya cambié la arquitectura de mi aplicación ... iframe FTW :)

¡Buen material hombre! Definitivamente también buscaré agregar el truco de three.js.

Me encantaría usar uno renderizado global. De hecho, ese es mi objetivo final. Sin embargo, necesitaría una forma de transmitir código al contexto existente.

En otras palabras, el usuario está escribiendo el código. No está escrito de antemano.

Creo que encontré una buena solución. Esto me permite tener componentes de React superpuestos en el mapa que no tienen que conocerse entre sí y permiten que React posea el DOM.

Utiliza esta técnica para forzar la pérdida de contexto:

gl.getExtension('WEBGL_lose_context').loseContext();

Aquí tienes un ejemplo completo:

var document = require('global/document');
var PIXI = require('pixi.js');

// I want to keep this canvas / renderer around. For example, this might be a bottom layer PIXI / React map overlay.
var canvas1 = document.createElement('canvas');
document.body.appendChild(canvas1);
createRenderer(0xff0000, canvas1);

function createRenderer(color, canvas) {
  canvas = canvas || document.createElement('canvas');
  var renderer = new PIXI.WebGLRenderer(800, 600, {view: canvas});
  var stage = new PIXI.Container();
  var graphics = new PIXI.Graphics();
  graphics.beginFill(color, 0.5);
  graphics.drawCircle(0, 0, 200);
  graphics.endFill();
  stage.addChild(graphics);
  renderer.render(stage);
  return {renderer: renderer, stage: stage, graphics: graphics};
}

// Simulate frequent adding / removing of lots of PIXI / React map overlays on top.
for (var i = 0; i < 16; i++) {
  var canvas = document.createElement('canvas');
  document.body.appendChild(canvas);
  var scene = createRenderer(0x00ff00, canvas);
  // Uncomment to see that the original canvas isn't removed.
  /* scene.renderer.currentRenderTarget.gl
      .getExtension('WEBGL_lose_context').loseContext(); */
  scene.renderer.destroy();
  scene.stage.removeChild(scene.graphics);
  document.body.removeChild(canvas);
}

¿Quizás PIXI debería agregar esto a su propio método de "destrucción"?

Eso tiene sentido si somos dueños del contexto, que creo que ahora asumimos que lo hacemos (v3).

En la función isWebGLSupported () (source / core / utils / index.js) reemplazando
return !!(gl && gl.getContextAttributes().stencil);

con
var success = !!(gl && gl.getContextAttributes().stencil); gl.getExtension('WEBGL_lose_context').loseContext(); gl = undefined; return success;

solucionó el problema por mí. Fue más prominente en Google Chrome: Firefox funcionó de cualquier manera. Como soy un poco novato en WebGL, estaría muy agradecido por comentar sobre este enfoque.

Lo bueno es perder el contexto por la fuerza al verificar, esa es una gran idea ya que sabemos (100%) que somos dueños de ese contexto.

¿Esto se fusionó? Estamos teniendo el mismo problema.

ping @englercj creo que esto se puede cerrar ahora

gl.getExtension('WEBGL_lose_context').loseContext() puede suprimir la advertencia, pero la especificación dice que simplemente simula la pérdida del contexto, hasta que se llama a WEBGL_lose_context.restoreContext() , lo que puede restaurar el contexto solo si no se perdió realmente en primer lugar.

Esto podría ser útil: lose_context () es solo una simulación (de acuerdo con la especificación) pero en este hilo ( Public WebGL: WEBGL_lose_context aprendemos (del desarrollador de mozilla Jeff Gilbert) que en Firefox loseContext() es sinónimo de " liberar este contexto y surecursos " .

El problema es que otros navegadores manejan esto de manera diferente. Hay comentarios de un desarrollador de Google (Ken Russell) que recomienda: Utilice las API de eliminación explícita * en WebGLRenderingContext para liberar recursos de GPU que su aplicación ya no usa.

Debo admitir que no he experimentado con nada de esto, pero parece que cualquier solución será específica del navegador.

Interesante nota de implementación de FF. De hecho, el consejo de Ken sobre la eliminación de recursos individuales parece el camino estándar a seguir, FF es uno de los muchos navegadores populares e incluso el comportamiento de FF puede cambiar sin previo aviso. Tampoco he probado por navegador porque no quiero depender de los detalles de implementación y la cadena de agente de usuario falsificable o el rastreo del navegador y las rutas de código adicionales cuando hay una alternativa.

Hoy estaba haciendo algunas pruebas para mi aplicación en iOS y encontré este error. ¿Existe una solución alternativa conocida? Mi vista de pixi es un subcomponente de una pantalla en mi aplicación, por lo que se destruye cuando el usuario navega fuera de esa página. Estaba pensando, ¿funcionaría de alguna manera separar / volver a adjuntar el mismo renderizador al lienzo de la vista para solucionar el problema?

La solución alternativa es no crear varios contextos. Solo reutiliza los que tienes. Si navega por las páginas, no debería importar lo que se creó en la página anterior.

Hmm, entonces, ¿cómo haría para reutilizar un contexto? Estoy usando angularJS, así que estoy trabajando dentro de un marco de plantillas. Por ejemplo, ¿podría separar el elemento de lienzo del DOM y almacenarlo, luego, cuando el usuario regrese a la página, volver a adjuntarlo a su lugar en la página? Sería genial si hubiera una forma dentro de PIXI de decir "usar este renderizador + contexto existente y adjuntarlos a este nuevo lienzo", pero no entiendo cómo sucede bajo el capó.

La razón por la que me importa es que estoy usando una gran vista de pixi como fondo de mi aplicación, y tengo una más pequeña que uso en mi página de configuración. Si el usuario va a la página de configuración varias veces, elimina el fondo principal porque se creó primero.

Parece que esto sería un problema con el uso de PIXI dentro de cualquier aplicación de una sola página.

Guardar la instancia de su PIXI.Application / PIXI.WebGLRenderer / PIXI.CanvasRenderer también guardará la instancia de su elemento de lienzo. Por ejemplo, en PIXI.Application, puede obtener la propiedad view que es HTMLCanvasElement. Puede insertar este elemento de lienzo y eliminarlo de su aplicación AngularJS / React. En PIXI puede decirle al Renderer qué elemento de lienzo usar pasándolo como una opción, sin embargo, no debe usar esto. En su lugar, permita que PIXI cree su propio elemento de lienzo internamente y simplemente agregueChild a algún contenedor DOM.

<div id="pixi-container"></div>
// maintain the reference to this Application even as your SPA view gets destroyed
const app = new PIXI.Application();

// Insert in the DOM or whatever the equivalent is in Angular
document.querySelector("#pixi-container").appendChild(app.view);

Ok, gracias, lo intentaré. En realidad, no se ve nada mal.

Muy bien, separar y reaparecer la vista en lugar de destruirla resolvió mi problema, gracias por la ayuda, chicos.

Este hilo se ha bloqueado automáticamente ya que no ha habido ninguna actividad reciente después de que se cerró. Abra un nuevo problema para errores relacionados.

¿Fue útil esta página
0 / 5 - 0 calificaciones