Pixi.js: Trop de contextes WebGL actifs

Créé le 11 déc. 2015  ·  29Commentaires  ·  Source: pixijs/pixi.js

Je crée une application intégrée à React qui charge plusieurs expériences individuelles, dans pixi.js, three.js, etc. Comme je ne peux pas utiliser d'iframe pour charger les expériences, je dois nettoyer après moi.

Chaque fois qu'il y a une navigation vers une nouvelle expérience, je vérifie si le moteur de rendu existe ou non, et si c'est le cas, je le détruis).

code factice

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

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

Il semble que je nettoie correctement mon moteur de rendu webgl, mais après le chargement de 16 pages, je reçois l'avertissement indiquant WARNING: Too many active WebGL contexts. Oldest context will be lost. Cela ne devrait pas être un problème sur le bureau car nous n'avons qu'un avertissement, mais sur l'ipad, il se bloque le navigateur et un message apparaît : X A problem occurred with this web page, so it was reloaded. .

Je regardais threejs et comment ils implémentent une solution pour cela, et ils ont le code suivant

Est-ce que je fais une bêtise ?

Commentaire le plus utile

Je pense avoir trouvé une bonne solution. Cela me permet d'avoir des composants React de superposition de carte qui n'ont pas à se connaître et à React de posséder le DOM.

Il utilise cette technique pour forcer la perte de contexte :

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

Voici un exemple complet :

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);
}

Peut-être que PIXI devrait ajouter cela à sa propre méthode de "destruction" ?

Tous les 29 commentaires

@andrevenancio
Je ne suis pas un expert en Pixi vu que je ne l'utilise que depuis lundi ou mardi, cependant. Si vous avez plusieurs moteurs de rendu et canevas en raison de nombreuses expériences ( comme celle-ci ) dans l'idée qu'un seul doit être accessible à la fois, j'aurais un moteur de rendu global ou un autre, et je conserverais les expériences dans un tableau. Ensuite, lorsque vous devez l'afficher, passez simplement l'étape de chaque expérience au moteur de rendu.

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 cette façon, vous ne créez le moteur de rendu qu'une seule fois et vous ne faites que passer l'étape de chaque expérience. Bien que cela ne fonctionne que si vous essayez de n'afficher qu'une seule page et d'avoir toutes les expériences dessus plutôt que d'avoir beaucoup de pages de test différentes.

AVIS DE NON-RESPONSABILITÉ : je suis nouveau sur pixi et je n'ai pas essayé cela, votre kilométrage peut varier. De plus, il semble toujours que vous ayez trouvé une sorte de bogue que les développeurs voudront peut-être examiner ou donner des conseils.

Avoir un seul moteur de rendu et plusieurs scènes devrait être bien. 1 moteur de rendu === 1 élément de canevas.

Salut les gars, merci pour vos retours. @samuraiseoul Ce que vous suggérez n'est pas idiot du tout, et c'est ainsi que je procéderais normalement pour avoir plusieurs expériences dans un cadre ... cependant, je devrai faire une autre solution car les expériences peuvent être effectuées en utilisant pixi.js threejs ou webgl. De plus, en plus de cela, en raison du type de transitions déjà défini dans la structure de réaction, à un moment donné, vous verrez l'ancienne expérience et la nouvelle expérience en même temps, rendues simultanément, donc l'idée d'un moteur de rendu a gagné ça marche pas....

De plus... il s'agit d'un contournement du problème, pas d'une solution en soi... Si la méthode .destroy() ne détruit pas réellement la référence au contexte webgl... à quoi ça sert ? :(

Toi mon pote ! Tout à fait d'accord pour dire que la fonction de destruction ne fait pas exactement ce qu'elle est censée faire si les contextes ne sont pas détruits :)

Je me promène, utilisez-vous autodetectRenderer ? Il se peut que le contexte de test que nous créons pour détecter si le navigateur est compatible WebGL ne soit pas détruit.

Quelques infos supplémentaires : vous ne pouvez pas détruire un contexte webgl, ils sont ramassés (à moins que quelque chose n'ait changé ?)

Je pense que nous supprimons les références que nous détenons et supprimons éventuellement l'élément canvas (qui est requis pour le nettoyage du contexte), mais c'est ensuite au navigateur de faire le nettoyage. Il est possible que des contextes fuient ou qu'ils soient créés plus rapidement que le navigateur ne le souhaite. Honnêtement je ne suis pas sûr.

Les outils de développement indiquent-ils des fuites dans des contextes comme celui-ci ? Peut-être que ça peut aider ? On dirait que quelque chose garde le contexte autour. Peut-être qu'une référence js au contexte ou une référence js au canevas sous-jacent?

C'est pourquoi j'aime toujours les iframes, quoi qu'il arrive.

@GoodBoyDigital J'utilise le autodetectRenderer comme ça

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

Je gare ça pour l'instant, je vous ferai savoir quelle est la solution quand j'en ai trouvé une. Je pensais juste que forcer ce contexte perdu forcerait peut-être la collecte des ordures à le nettoyer. :)

J'ai le même problème. Ce que je construis nécessite que Pixi soit détruit et reconstruit assez souvent et je suis préoccupé par les implications sur la mémoire. Chaque instance WebGL est assez petite pour le moment, mais elle deviendra beaucoup plus grande au fur et à mesure que nous continuerons à la développer.

Aucun progrès?

Pour être clair, j'utilise Phaser, mais il semble appeler la méthode renderer.destroy de Pixi.

pas encore de chance. Si vous pouvez soit utiliser un moteur de rendu global et vous y rendre simplement différentes scènes, soit charger simplement le contenu via une iframe et cela élimine correctement tout ce qui est suspendu.

Hey peeps, oui c'est un peu délicat vraiment. Autant que je sache, nous faisons tout notre possible pour détruire un contexte. Donc toutes les textures/programmes arraybuffers ect sont détruits. Ce qu'on pourrait faire, c'est peut-être mutualiser les contextes une fois détruits pour les réutiliser plutôt que d'en créer un nouveau à chaque fois ?

hey @GoodBoyDigital dans mon cas, j'avais une animation https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L289

Sinon pas de soucis mec, j'ai déjà changé l'architecture de mon application... iframe FTW :)

Bonhomme de trucs! Je vais certainement envisager d'ajouter l'astuce three.js aussi.

J'aimerais utiliser un rendu global. En fait, c'est mon objectif ultime. Cependant, j'aurais besoin d'un moyen de diffuser du code dans le contexte existant.

En d'autres termes, l'utilisateur écrit le code. Ce n'est pas pré-écrit.

Je pense avoir trouvé une bonne solution. Cela me permet d'avoir des composants React de superposition de carte qui n'ont pas à se connaître et à React de posséder le DOM.

Il utilise cette technique pour forcer la perte de contexte :

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

Voici un exemple complet :

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);
}

Peut-être que PIXI devrait ajouter cela à sa propre méthode de "destruction" ?

Cela a du sens si nous possédons le contexte, ce que je pense pour le moment, nous supposons que nous le faisons (v3).

Dans la fonction isWebGLSupported() (source/core/utils/index.js) remplaçant
return !!(gl && gl.getContextAttributes().stencil);

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

a résolu le problème pour moi. C'était le plus important dans Google Chrome - Firefox fonctionnait dans les deux cas. Comme je suis un peu un noob WebGL, je serais vraiment reconnaissant de commenter cette approche.

Bon appel à perdre le contexte de force lors de la vérification, c'est une excellente idée puisque nous savons (100%) que nous possédons ce contexte.

Cela a-t-il été fusionné ? Nous avons le même problème.

ping @englercj je pense que cela peut être fermé maintenant

gl.getExtension('WEBGL_lose_context').loseContext() peut supprimer l'avertissement mais la spécification dit qu'il simule simplement la perte du contexte, jusqu'à ce que WEBGL_lose_context.restoreContext() soit appelé - ce qui ne peut restaurer le contexte que s'il n'a pas été vraiment perdu en premier lieu.

Cela peut être utile : Lose_context() n'est qu'une simulation (selon les spécifications) mais dans ce fil ( Public WebGL : WEBGL_lose_context nous apprenons (du développeur de mozilla Jeff Gilbert) que dans Firefox loseContext() est un synonyme de " libérer ce contexte et sesressources" .

Le problème est que les autres navigateurs gèrent cela différemment. Il y a des commentaires d'un développeur de Google (Ken Russell) recommandant : Veuillez utiliser les API de suppression explicite* sur le WebGLRenderingContext pour libérer les ressources GPU que votre application n'utilise plus.

Je dois admettre que je n'ai rien expérimenté de tout cela, mais il semble que toute solution sera spécifique au navigateur.

Note de mise en œuvre FF intéressante. En effet, le conseil de Ken sur la suppression de ressources individuelles ressemble à la méthode standard, FF n'est qu'un des nombreux navigateurs populaires et même le comportement de FF peut changer sans préavis. Je n'ai pas non plus testé par navigateur parce que je ne voulais pas me fier aux détails de la mise en œuvre et à la chaîne d'agent utilisateur falsifié ou au reniflage du navigateur et aux chemins de code supplémentaires lorsqu'il existe une alternative.

Je faisais des tests pour mon application aujourd'hui sur iOS et j'ai rencontré cette erreur. Existe-t-il une solution de contournement connue ? Ma vue pixi est un sous-composant d'un écran sur mon application, elle est donc détruite lorsque l'utilisateur quitte cette page. Je pensais, cela fonctionnerait-il de détacher/rattacher le même moteur de rendu au canevas de la vue pour contourner le problème?

La solution de contournement consiste à ne pas créer plusieurs contextes. Réutilisez simplement ceux que vous avez. Si vous parcourez les pages, peu importe ce qui a été créé sur la page précédente.

Hmm, alors comment ferais-je pour réutiliser un contexte ? J'utilise angularJS, donc je travaille dans un cadre de modèles. Par exemple, pourrais-je détacher l'élément canvas du DOM et le stocker, puis, lorsque l'utilisateur revient sur la page, le rattacher à sa place dans la page ? Ce serait génial s'il y avait un moyen dans PIXI de dire "utiliser ce moteur de rendu existant + contexte et les attacher à ce nouveau canevas", mais je ne comprends pas comment cela se passe sous le capot.

La raison pour laquelle je m'en soucie est que j'utilise une grande vue pixi comme arrière-plan de mon application, et j'en ai une plus petite que j'utilise sur ma page de paramètres. Si l'utilisateur accède plusieurs fois à la page des paramètres, il tue l'arrière-plan principal car il a été créé en premier.

On dirait que cela poserait un problème avec l'utilisation de PIXI dans n'importe quelle application d'une seule page.

L'enregistrement de l'instance de votre PIXI.Application/PIXI.WebGLRenderer/PIXI.CanvasRenderer enregistrera également l'instance de votre élément canvas. Par exemple, dans PIXI.Application, vous pouvez obtenir la propriété view qui est le HTMLCanvasElement. Vous pouvez insérer cet élément de canevas et le supprimer de votre application AngularJS/React. Dans PIXI, vous pouvez indiquer au Renderer quel élément de canevas utiliser en le passant comme option, cependant, vous ne devriez pas l'utiliser. Au lieu de cela, autorisez PIXI à créer son propre élément de canevas en interne et ajoutez simplementChild à un conteneur 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, merci, je vais essayer ça. Ça a l'air pas mal du tout en fait.

D'accord, détacher et réajouter la vue au lieu de la détruire a résolu mon problème, merci pour l'aide les gars.

Ce fil a été automatiquement verrouillé car il n'y a eu aucune activité récente après sa fermeture. Veuillez ouvrir un nouveau problème pour les bogues liés.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

sntiagomoreno picture sntiagomoreno  ·  3Commentaires

zcr1 picture zcr1  ·  3Commentaires

Darker picture Darker  ·  3Commentaires

samueller picture samueller  ·  3Commentaires

Makio64 picture Makio64  ·  3Commentaires