我正在构建一个与 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 以及他们如何为此实现解决方案,他们有以下代码
我在做什么傻事吗?
@andrevencio
我不是 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。 此外,最重要的是,由于在 react 结构中已经定义了过渡类型,因此您将在某一时刻看到旧实验和新实验同时渲染,因此一个渲染器的想法获胜不行....
另外……这是解决问题的方法,而不是解决方案本身……如果 .destroy() 方法实际上没有破坏对 webgl 上下文的引用……有什么意义? :(
哟伙计! 完全同意,如果上下文没有被销毁,destroy 函数并没有完全按照预期执行:)
我徘徊,你在使用autodetectRenderer
吗? 可能是因为我们创建的用于检测浏览器是否支持 webGL 的测试上下文没有被破坏。
一些额外的信息:你不能破坏 webgl 上下文,它们是垃圾收集的(除非发生了一些变化?)
我认为我们删除了我们持有的引用,并有选择地删除了 canvas 元素(这是上下文清理所必需的),但是接下来由浏览器来进行清理。 上下文可能正在泄漏,或者它们的创建速度比浏览器想要的要快。 老实说,我不确定。
开发工具是否指示这样的上下文泄漏? 也许这有帮助? 听起来好像有什么东西在保持上下文……也许是对上下文的 js 引用或对底层画布的 js 引用?
这就是为什么我仍然喜欢 iframes 无论如何。
@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的hack是否有效......也许可以试试你的V4并尝试用pixi打开16个以上的标签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;
为我解决了问题。 它在谷歌浏览器中最为突出——Firefox 两种方式都可以。 由于我有点 WebGL 菜鸟,我非常感谢您对这种方法发表评论。
检查时强行丢失上下文的好方法,这是一个好主意,因为我们知道(100%)我们拥有该上下文。
这是合并了吗? 我们有同样的问题。
ping @englercj我想现在可以关闭了
gl.getExtension('WEBGL_lose_context').loseContext()
可能会抑制警告,但规范说它只是模拟丢失上下文,直到WEBGL_lose_context.restoreContext()
被调用 - 只有当上下文没有真正丢失时才能恢复上下文。
这可能很有用:lose_context() 只是一个模拟(根据规范),但在这个线程(公共 WebGL:WEBGL_lose_context我们了解到(从 mozilla 开发人员杰夫吉尔伯特)在 Firefox 中loseContext()
是“的代名词”释放此上下文及其资源” 。
问题是其他浏览器对此的处理方式不同。 来自 Google 开发人员 (Ken Russell) 的评论建议:请使用 WebGLRenderingContext 上的显式删除* API 来释放您的应用程序不再使用的 GPU 资源。
我不得不承认我没有尝试过任何这些,但看起来任何解决方案都将是特定于浏览器的。
有趣的 FF 实施说明。 事实上,Ken 关于删除单个资源的建议看起来是标准的做法,FF 只是众多流行浏览器中的一种,甚至 FF 的行为也可能会在没有通知的情况下发生变化。 我也没有对每个浏览器进行测试,因为在有替代方案时不想依赖实现细节和可伪造的用户代理字符串或浏览器嗅探和其他代码路径。
我今天在 iOS 上对我的应用程序进行了一些测试,但遇到了这个错误。 有已知的解决方法吗? 我的 pixi 视图是我的应用程序屏幕的子组件,因此当用户离开该页面时它会被销毁。 我在想,以某种方式将相同的渲染器分离/重新附加到视图的画布以解决这个问题是否可行?
解决方法是不创建多个上下文。 只需重复使用您拥有的。 如果您浏览页面,那么在上一页上创建的内容应该无关紧要。
嗯,那么我将如何重用上下文? 我正在使用 angularJS,所以我在模板框架内工作。 就像,我可以从 DOM 中分离画布元素并存储它,然后当用户返回页面时,将其重新附加到页面中的位置吗? 如果 PIXI 中有一种方法可以说“使用这个现有的渲染器 + 上下文并将它们附加到这个新的画布上”,那就太好了,但我不明白它是如何发生在引擎盖下的。
我关心的原因是我使用一个大的 pixi 视图作为我的应用程序的背景,而我在我的设置页面上使用了一个较小的视图。 如果用户多次进入设置页面,它会杀死主背景,因为它是第一个创建的。
似乎在任何单页应用程序中使用 PIXI 都是一个问题。
保存 PIXI.Application/PIXI.WebGLRenderer/PIXI.CanvasRenderer 的实例也将保存画布元素的实例。 例如,在 PIXI.Application 中,您可以获得view
属性,即 HTMLCanvasElement。 您可以插入此画布元素并将其从 AngularJS/React 应用程序中删除。 在 PIXI 中,您可以通过将其作为选项传递来告诉渲染器使用哪个画布元素,但是,您不应该使用它。 相反,允许 PIXI 在内部创建它自己的画布元素,并将 appendChild 附加到某个 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);
好的,谢谢,我试试看其实看起来一点都不差。
好的,分离并重新附加视图而不是破坏它解决了我的问题,感谢您的帮助。
此线程已被自动锁定,因为它在关闭后没有任何近期活动。 请为相关错误打开一个新问题。
最有用的评论
我想我找到了一个很好的解决方案。 这允许我拥有地图覆盖 React 组件,它们不必相互了解并允许 React 拥有 DOM。
它使用这种技术来强制上下文丢失:
这是一个完整的例子:
也许 PIXI 应该将其添加到自己的“销毁”方法中?