Pixi.js: Zu viele aktive WebGL-Kontexte

Erstellt am 11. Dez. 2015  ·  29Kommentare  ·  Quelle: pixijs/pixi.js

Ich baue eine in React integrierte App, die mehrere einzelne Experimente lädt, in pixi.js, three.js usw. Da ich keinen iframe zum Laden der Experimente verwenden kann, muss ich selbst aufräumen.

Jedes Mal, wenn es eine Navigation zu einem neuen Experiment gibt, überprüfe ich, ob der Renderer vorhanden ist oder nicht, und wenn ja, zerstöre ich ihn).

Dummy-Code

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

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

Es scheint, dass ich meinen Webgl-Renderer korrekt bereinige, aber nachdem 16 Seiten geladen wurden, erhalte ich die Warnung WARNING: Too many active WebGL contexts. Oldest context will be lost. Dies sollte auf dem Desktop kein Problem sein, da wir nur eine Warnung haben, aber auf dem iPad stürzt es ab im Browser und eine Meldung erscheint: X A problem occurred with this web page, so it was reloaded. .

Ich habe mir Threejs angesehen und wie sie eine Lösung dafür implementieren, und sie haben den folgenden Code

Mache ich etwas Dummes?

Hilfreichster Kommentar

Ich glaube, ich habe eine gute Lösung gefunden. Dies ermöglicht es mir, React-Komponenten mit Kartenüberlagerung zu verwenden, die nicht voneinander wissen müssen, und React kann das DOM besitzen.

Es verwendet diese Technik , um den Kontextverlust zu erzwingen:

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

Hier ist ein vollständiges Beispiel:

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

Vielleicht sollte PIXI dies zu seiner eigenen Methode "Zerstören" hinzufügen?

Alle 29 Kommentare

@andrevenancio
Ich bin kein Experte für Pixi, da ich es aber erst seit Montag oder Dienstag benutze. Wenn Sie aufgrund vieler Experimente ( wie diesem ) mehrere Renderer und Leinwände haben, in der Idee, dass nur auf einen gleichzeitig zugegriffen werden muss, würde ich einen globalen Renderer oder einen anderen haben und die Experimente in einem Array aufbewahren. Wenn Sie es dann anzeigen müssen, übergeben Sie einfach die Bühne jedes Experiments an den Renderer.

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

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

Auf diese Weise erstellen Sie den Renderer nur einmal und passieren ihn nur bei jedem Experiment. Dies funktioniert jedoch nur, wenn Sie versuchen, nur eine Seite zu rendern und alle Experimente darauf zu haben, anstatt viele verschiedene Experimentseiten zu haben.

HAFTUNGSAUSSCHLUSS: Ich bin neu bei pixi und habe dies nicht ausprobiert. Ihre Laufleistung kann variieren. Außerdem klingt es immer noch so, als ob Sie einen Fehler gefunden hätten, den die Entwickler vielleicht untersuchen oder Anleitungen geben würden.

Ein einzelner Renderer und mehrere Szenen sollten in Ordnung sein. 1 Renderer === 1 Canvas-Element.

Hey Leute, danke für euer Feedback. @samuraiseoul Was Sie vorschlagen, ist überhaupt nicht albern, und so würde ich normalerweise vorgehen, um mehrere Experimente in einem Framework durchzuführen ... ich muss jedoch eine andere Lösung finden, da die Experimente mit pixi.js threejs oder einfaches webgl. Darüber hinaus sehen Sie aufgrund der Art der Übergänge, die bereits in der Reaktionsstruktur definiert sind, an einem Punkt das alte Experiment und das neue Experiment gleichzeitig und rendern gleichzeitig, so dass die Idee eines Renderers gewonnen hat funktioniert nicht....

Außerdem.... dies ist ein Workaround für das Problem, keine Lösung selbst... Wenn die Methode .destroy() den Verweis auf den Webgl-Kontext tatsächlich nicht zerstört... was ist der Sinn? :(

Yo Kumpel! Stimme voll und ganz zu, dass die Destroy-Funktion nicht genau das tut, was sie soll, wenn die Kontexte nicht zerstört werden :)

Ich wandere, verwendest du autodetectRenderer ? Es könnte sein, dass der Testkontext, den wir erstellen, um zu erkennen, ob der Browser webGL-fähig ist, nicht zerstört wird.

Einige zusätzliche Informationen: Sie können einen Webgl-Kontext nicht zerstören, er wird auf Müll gesammelt (es sei denn, es hat sich etwas geändert?)

Ich denke, wir entfernen die Referenzen, die wir halten, und entfernen optional das Canvas-Element (das für die Kontextbereinigung erforderlich ist), aber dann liegt es am Browser, die Bereinigung durchzuführen. Es ist möglich, dass Kontexte undicht sind oder schneller erstellt werden, als der Browser es möchte. Ehrlich gesagt bin ich mir nicht sicher.

Zeigen Entwicklungstools Lecks in Kontexten wie diesem an? Vielleicht kann das helfen? Klingt so, als ob etwas den Kontext beibehält. Vielleicht ein js-Ref auf den Kontext oder ein js-Ref auf die zugrunde liegende Leinwand?

Deshalb liebe ich iframes immer noch, egal was passiert.

@GoodBoyDigital Ich verwende das autodetectRenderer so

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

Ich stelle das hier vorerst ab und werde euch wissen lassen, was die Lösung ist, wenn ich eine gefunden habe :)

Ich habe das gleiche Problem. Was ich baue, erfordert, dass Pixi ziemlich oft zerstört und wieder aufgebaut wird, und ich bin besorgt über die Auswirkungen auf das Gedächtnis. Jede WebGL-Instanz ist im Moment ziemlich klein, aber sie wird viel größer, wenn wir sie weiter ausbauen.

Irgendein Fortschritt?

Um es klar zu sagen, ich verwende Phaser, aber es scheint die renderer.destroy-Methode von Pixi aufzurufen.

noch kein glück. Wenn Sie entweder einen globalen Renderer verwenden und einfach verschiedene Szenen darin rendern können, oder einfach Inhalte über einen Iframe laden und dieser alles, was hängt, ordnungsgemäß beseitigt.

Hey Leute, ja, das ist wirklich ein bisschen schwierig. Soweit ich sehen kann, tun wir alles, um einen Kontext zu zerstören. So werden alle Texturen/Programme, Arraybuffer ect zerstört. Was wir vielleicht tun könnten, ist, die einmal zerstörten Kontexte zu bündeln, damit wir sie wiederverwenden, anstatt jedes Mal einen neuen zu erstellen?

hey @GoodBoyDigital in meinem Fall hatte ich eine Animation in / out, also brauchte ich immer 2 Kontexte ... das ist das Problem mit der Integration in React und nicht mit vollständigen Pixeln ... jedenfalls https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L289

Ansonsten keine Sorge, ich habe die Architektur meiner App bereits geändert ... iframe FTW :)

Guter Stoff Mann! Ich werde auf jeden Fall auch den Three.js-Trick hinzufügen.

Ich würde gerne ein global gerendertes verwenden. Tatsächlich ist das mein ultimatives Ziel. Ich bräuchte jedoch eine Möglichkeit, Code in den vorhandenen Kontext zu streamen.

Mit anderen Worten, der Benutzer schreibt den Code. Es ist nicht vorgefertigt.

Ich glaube, ich habe eine gute Lösung gefunden. Dies ermöglicht es mir, React-Komponenten mit Kartenüberlagerung zu verwenden, die nicht voneinander wissen müssen, und React kann das DOM besitzen.

Es verwendet diese Technik , um den Kontextverlust zu erzwingen:

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

Hier ist ein vollständiges Beispiel:

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

Vielleicht sollte PIXI dies zu seiner eigenen Methode "Zerstören" hinzufügen?

Das macht Sinn, wenn wir den Kontext besitzen, von dem ich denke, dass wir im Moment davon ausgehen, dass wir dies tun (v3).

In der Funktion isWebGLSupported() (source/core/utils/index.js) ersetzt
return !!(gl && gl.getContextAttributes().stencil);

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

hat das Problem bei mir behoben. Am prominentesten war es in Google Chrome - Firefox funktionierte so oder so. Da ich ein bisschen ein WebGL-Noob bin, wäre ich sehr dankbar für einen Kommentar zu diesem Ansatz.

Guter Aufruf, den Kontext beim Prüfen gewaltsam zu verlieren, das ist eine großartige Idee, da wir (100 %) wissen, dass uns dieser Kontext gehört.

Wurde das zusammengeführt? Wir haben das gleiche Problem.

ping @englercj ich denke das kann jetzt geschlossen werden

gl.getExtension('WEBGL_lose_context').loseContext() kann die Warnung unterdrücken, aber die Spezifikation sagt, dass es nur den Verlust des Kontexts simuliert, bis WEBGL_lose_context.restoreContext() aufgerufen wird - was den Kontext nur wiederherstellen kann, wenn er nicht wirklich verloren ging.

Dies könnte nützlich sein: lose_context() ist nur eine Simulation (laut Spezifikation), aber in diesem Thread ( Public WebGL: WEBGL_lose_context erfahren wir (vom Mozilla-Entwickler Jeff Gilbert), dass in Firefox loseContext() ein Synonym für " löse diesen Kontext und seineRessourcen" .

Das Problem ist, dass andere Browser dies anders handhaben. Es gibt Kommentare von einem Google-Entwickler (Ken Russell), der empfiehlt: Bitte verwenden Sie die expliziten delete*-APIs im WebGLRenderingContext, um GPU-Ressourcen freizugeben, die Ihre Anwendung nicht mehr verwendet.

Ich muss zugeben, dass ich damit noch nicht experimentiert habe, aber es sieht so aus, als ob jede Lösung browserspezifisch sein wird.

Interessanter Hinweis zur FF-Implementierung. Tatsächlich scheint Kens Ratschlag zum Löschen einzelner Ressourcen der Standardweg zu sein, FF ist nur einer der vielen populären Browser und sogar das Verhalten von FF kann sich ohne Vorankündigung ändern. Ich habe auch nicht pro Browser getestet, weil ich mich nicht auf Implementierungsdetails und gefälschte User-Agent-Strings oder Browser-Sniffing und zusätzliche Codepfade verlassen wollte, wenn es eine Alternative gibt.

Ich habe heute einige Tests für meine App auf iOS durchgeführt und bin auf diesen Fehler gestoßen. Gibt es eine bekannte Problemumgehung? Meine Pixi-Ansicht ist eine Unterkomponente eines Bildschirms in meiner App und wird daher zerstört, wenn der Benutzer diese Seite verlässt. Ich dachte, würde es funktionieren, den gleichen Renderer irgendwie von der Leinwand der Ansicht zu trennen / wieder anzuhängen, um das Problem zu umgehen?

Die Problemumgehung besteht darin, nicht mehrere Kontexte zu erstellen. Verwenden Sie einfach die, die Sie haben. Wenn Sie durch Seiten navigieren, sollte es egal sein, was auf der vorherigen Seite erstellt wurde.

Hmm, wie würde ich einen Kontext wiederverwenden? Ich verwende angleJS, also arbeite ich in einem Template-Framework. Könnte ich beispielsweise das Canvas-Element vom DOM trennen und speichern, und es dann, wenn der Benutzer zur Seite zurückkehrt, wieder an seinen Platz in der Seite anhängen? Es wäre großartig, wenn es innerhalb von PIXI eine Möglichkeit gäbe, zu sagen: "Verwende diesen vorhandenen Renderer + Kontext und füge sie dieser neuen Leinwand hinzu", aber ich verstehe nicht, wie das unter der Haube passiert.

Der Grund, warum ich mich interessiere, ist, dass ich eine große Pixelansicht als Hintergrund meiner App verwende und eine kleinere habe, die ich auf meiner Einstellungsseite verwende. Wenn der Benutzer die Einstellungsseite mehrmals aufruft, wird die Haupthintergrundseite beendet, da sie zuerst erstellt wurde.

Scheint ein Problem bei der Verwendung von PIXI in einer einzelnen Seiten-App zu sein.

Wenn Sie die Instanz Ihrer PIXI.Application/PIXI.WebGLRenderer/PIXI.CanvasRenderer speichern, wird auch die Instanz Ihres Canvas-Elements gespeichert. In PIXI.Application können Sie beispielsweise die Eigenschaft view abrufen, die das HTMLCanvasElement ist. Sie können dieses Canvas-Element einfügen und aus Ihrer AngularJS/React-App entfernen. In PIXI können Sie dem Renderer mitteilen, welches Canvas-Element verwendet werden soll, indem Sie es als Option übergeben, dies sollten Sie jedoch nicht verwenden. Lassen Sie stattdessen PIXI intern ein eigenes Canvas-Element erstellen und hängen SieChild einfach an einen DOM-Container an.

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

Okay, danke, das werde ich mal ausprobieren. Sieht eigentlich gar nicht schlecht aus.

Okay, das Trennen und erneute Anhängen der Ansicht, anstatt sie zu zerstören, hat mein Problem gelöst, danke für die Hilfe Jungs.

Dieser Thread wurde automatisch gesperrt, da nach dem Schließen in letzter Zeit keine Aktivität stattgefunden hat. Bitte öffnen Sie eine neue Ausgabe für verwandte Fehler.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

zcr1 picture zcr1  ·  3Kommentare

courtneyvigo picture courtneyvigo  ·  3Kommentare

lunabunn picture lunabunn  ·  3Kommentare

samueller picture samueller  ·  3Kommentare

SebastienFPRousseau picture SebastienFPRousseau  ·  3Kommentare