Pixi.js: Слишком много активных контекстов WebGL

Созданный на 11 дек. 2015  ·  29Комментарии  ·  Источник: pixijs/pixi.js

Я создаю приложение, интегрированное с React, которое загружает несколько отдельных экспериментов в pixi.js, three.js и т. Д. Поскольку я не могу использовать iframe для загрузки экспериментов, мне нужно убирать за собой.

Каждый раз, когда появляется переход к новому эксперименту, я проверяю, существует ли рендерер, и если есть, я его уничтожаю).

фиктивный код

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

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

Кажется, я правильно очищаю свой рендерер webgl, однако после загрузки 16 страниц я получаю предупреждение о том, что WARNING: Too many active WebGL contexts. Oldest context will be lost. Это не должно быть проблемой на рабочем столе, так как у нас есть только предупреждение, но на ipad оно вылетает. в браузере и появится сообщение: X A problem occurred with this web page, so it was reloaded. .

Я смотрел на threejs и на то, как они реализуют решение для этого, и у них есть следующий код

Я делаю что-нибудь глупое?

Самый полезный комментарий

Думаю, я нашел хорошее решение. Это позволяет мне иметь компоненты React наложения карты, которым не нужно знать друг о друге, и позволяет React владеть DOM.

Он использует эту технику для принудительной потери контекста:

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

Вот полный пример:

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

Может быть, PIXI стоит добавить это в свой собственный метод «уничтожения»?

Все 29 Комментарий

@andrevenancio
Я не эксперт в Pixi, поскольку использую его только с понедельника или вторника. Если у вас есть несколько рендереров и холстов из-за множества экспериментов ( например, этого ), имея в виду, что единовременно нужно получить доступ только к одному, у меня будет глобальный рендерер или другой, и я буду хранить эксперименты в массиве. Затем, когда вам нужно отобразить его, просто передайте этап из каждого эксперимента в средство визуализации.

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

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

Таким образом, вы делаете рендерер только один раз и просто передаете ему этап из каждого эксперимента. Хотя это работает только в том случае, если вы пытаетесь отобразить только одну страницу и проводить на ней все эксперименты, а не иметь много разных экспериментальных страниц.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я новичок в pixi и не пробовал это сделать, ваш пробег может отличаться. Кроме того, это все еще звучит так, как если бы вы обнаружили какую-то ошибку, которую, возможно, разработчики захотят изучить или дать какие-то рекомендации.

Одного средства визуализации и нескольких сцен должно хватить. 1 рендерер === 1 элемент холста.

Привет, ребята, спасибо за отзыв. @samuraiseoul То, что вы предлагаете, вовсе не глупо, и именно так я обычно продолжаю проводить несколько экспериментов в одном фреймворке ... однако мне нужно будет сделать другое решение, поскольку эксперименты можно проводить с использованием pixi.js threejs или простой webgl. Кроме того, из-за типа переходов, уже определенных в структуре реакции, в какой-то момент вы увидите старый эксперимент и новый эксперимент одновременно, рендеринг которых выполняется одновременно, так что идея одного рендерера выиграла. не работает ....

Плюс .... это обход проблемы, а не само решение ... Если метод .destroy () на самом деле не уничтожает ссылку на контекст webgl ... в чем смысл? :(

Эй, приятель! Полностью согласен с тем, что функция уничтожения не совсем выполняет то, что должна, если контексты не уничтожаются :)

Я брожу, вы используете autodetectRenderer ? Возможно, тестовый контекст, который мы создаем, чтобы определить, поддерживает ли браузер поддержку WebGL, не уничтожается.

Дополнительная информация: вы не можете уничтожить контекст webgl, они собираются мусором (если что-то не изменилось?)

Я думаю, что мы удаляем ссылки, которые у нас есть, и, возможно, удаляем элемент холста (который требуется для очистки контекста), но затем браузер должен выполнить очистку. Возможно, что контексты утекают или создаются быстрее, чем этого хочет браузер. Честно говоря, я не уверен.

Обнаруживают ли инструменты разработки утечки в подобных контекстах? Может это поможет? Похоже, что-то держит контекст ... Может быть, ссылка js на контекст или ссылка js на нижележащий холст?

Вот почему я до сих пор люблю фреймы, несмотря ни на что.

@GoodBoyDigital Я использую autodetectRenderer вот так

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

Я припарковал это сейчас, и скажу вам, ребята, какое решение, когда я его найду ... Я просто подумал, что принудительная потеря этого контекста, возможно, заставит сборщик мусора очистить его ... но я тоже не уверен в этом :)

У меня такая же проблема. То, что я создаю, требует частого разрушения и восстановления Pixi, и меня беспокоят последствия для памяти. Каждый экземпляр WebGL на данный момент довольно мал, но по мере того, как мы продолжаем его развивать, он будет намного больше.

Какой-либо прогресс?

Для ясности, я использую Phaser, но, похоже, он вызывает метод Pixi renderer.destroy.

пока не повезло. Если вы можете использовать один глобальный рендерер и просто отображать в нем разные сцены, либо просто загружать контент через iframe, и он правильно удаляет все, что висит.

Привет, да, это действительно непросто. Насколько я понимаю, мы делаем все возможное, чтобы разрушить контекст. Таким образом, все текстуры / программные массивы буферов и т. Д. Уничтожаются. Что мы могли бы сделать, это, возможно, объединить контексты после их уничтожения, чтобы мы использовали их повторно, а не создавали каждый раз новый?

эй @GoodBoyDigital, в моем случае у меня была анимация, поэтому мне всегда нужно было 2 контекста ... это проблема с интеграцией с React, а не с полным pixi ... в любом случае, не уверен, что это ошибка, которую вы можете исправить за говорят .. Я думаю, что это больше похоже на то, что браузер управляет контекстами ... даже не знаю, работает ли этот хак от three.js ... может быть, попробуйте ur V4 и попробуйте открыть более 16 вкладок с помощью pixi https://github.com/mrdoob/three.js/blob/master/src/renderers/WebGLRenderer.js#L289

В остальном не беспокойся, дружище, я уже изменил архитектуру своего приложения ... iframe FTW :)

Хороший человек! Я обязательно рассмотрю возможность добавления трюка с three.js.

Я бы хотел использовать одну глобальную визуализацию. Собственно, это моя конечная цель. Однако мне нужен способ потоковой передачи кода в существующий контекст.

Другими словами, пользователь пишет код. Это не написано заранее.

Думаю, я нашел хорошее решение. Это позволяет мне иметь компоненты React наложения карты, которым не нужно знать друг о друге, и позволяет React владеть DOM.

Он использует эту технику для принудительной потери контекста:

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

Вот полный пример:

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

Может быть, PIXI стоит добавить это в свой собственный метод «уничтожения»?

Это имеет смысл, если мы владеем контекстом, что, я думаю, сейчас мы предполагаем (v3).

В функции isWebGLSupported () (source / core / utils / index.js) заменив
return !!(gl && gl.getContextAttributes().stencil);

с участием
var success = !!(gl && gl.getContextAttributes().stencil); gl.getExtension('WEBGL_lose_context').loseContext(); gl = undefined; return success;

исправил проблему для меня. Это было наиболее заметно в Google Chrome - Firefox работал в любом случае. Поскольку я немного новичок в WebGL, я был бы очень благодарен за комментарий по поводу этого подхода.

Хороший вызов принудительно теряет контекст при проверке, это отличная идея, поскольку мы знаем (на 100%), что этот контекст принадлежит нам.

Это было объединено? У нас та же проблема.

ping @englercj, думаю, сейчас можно закрыть

gl.getExtension('WEBGL_lose_context').loseContext() может подавить предупреждение, но в спецификации говорится, что он просто имитирует потерю контекста, пока не будет вызван WEBGL_lose_context.restoreContext() , который может восстановить контекст, только если он изначально не был действительно потерян.

Это может быть полезно: lost_context () - это всего лишь симуляция (согласно спецификации), но в этом потоке ( Public WebGL: WEBGL_lose_context мы узнаем (от разработчика Mozilla Джеффа Гилберта), что в Firefox loseContext() является синонимом для " освободить этот контекст и егоресурсы » .

Проблема в том, что другие браузеры обрабатывают это иначе. Комментарии от разработчика Google (Кен Рассел) рекомендуют: Используйте явные API удаления * в WebGLRenderingContext, чтобы освободить ресурсы графического процессора, которые ваше приложение больше не использует.

Должен признать, что я ни с чем из этого не экспериментировал, но похоже, что любое решение будет зависеть от браузера.

Интересное замечание по реализации FF. Действительно, совет Кена об удалении отдельных ресурсов выглядит стандартным способом, FF - лишь один из многих популярных браузеров, и даже поведение FF может измениться без предварительного уведомления. Я также не тестировал для каждого браузера, потому что не хотел полагаться на детали реализации и поддельную строку пользовательского агента или анализ браузера и дополнительные пути кода, когда есть альтернатива.

Сегодня я тестировал свое приложение на iOS и столкнулся с этой ошибкой. Есть ли известный обходной путь? Мое представление pixi - это подкомпонент экрана в моем приложении, поэтому он уничтожается, когда пользователь уходит с этой страницы. Я думал, получится ли каким-то образом отсоединить / повторно подключить один и тот же рендерер к холсту представления, чтобы обойти проблему?

Обходной путь - не создавать несколько контекстов. Просто используйте те, которые у вас есть. Если вы перемещаетесь по страницам, то не имеет значения, что было создано на предыдущей странице.

Хм, а как мне повторно использовать контекст? Я использую angularJS, поэтому работаю в рамках шаблонов. Например, могу ли я отсоединить элемент холста от DOM и сохранить его, а затем, когда пользователь вернется на страницу, снова прикрепить его на свое место на странице? Было бы здорово, если бы в PIXI был способ сказать «используйте этот существующий рендерер + контекст и прикрепите их к этому новому холсту», но я не понимаю, как это происходит под капотом.

Причина, по которой меня волнует, заключается в том, что я использую одно большое изображение pixi в качестве фона своего приложения, а у меня есть меньшее, которое я использую на своей странице настроек. Если пользователь переходит на страницу настроек несколько раз, он убивает основной фоновый, потому что он был создан первым.

Похоже, это будет проблемой при использовании PIXI в любом одностраничном приложении.

Сохранение экземпляра вашего PIXI.Application / PIXI.WebGLRenderer / PIXI.CanvasRenderer также сохранит экземпляр вашего элемента холста. Например, в PIXI.Application вы можете получить свойство view которое является HTMLCanvasElement. Вы можете вставить этот элемент холста и удалить его из приложения AngularJS / React. В PIXI вы можете указать Renderer, какой элемент холста использовать, передав его в качестве опции, однако вам не следует использовать это. Вместо этого разрешите PIXI создать собственный элемент холста внутри и просто добавитьChild в некоторый контейнер 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);

Хорошо, спасибо, я попробую. На самом деле совсем неплохо.

Хорошо, отсоединение и повторное добавление представления вместо его уничтожения решило мою проблему, спасибо за помощь, ребята.

Этот поток был автоматически заблокирован, поскольку после его закрытия в последнее время не было никаких действий. Пожалуйста, откройте новую проблему для связанных ошибок.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги