英文:
Canvases content is lost when the context is lost
问题
我想在多于100个画布上绘制,并保留其内容。我的设置是,在循环中创建画布,然后针对每个画布获取其webgl2
上下文并进行一些绘制操作。问题是,当我从第17个画布获取webgl2
上下文时,第一个画布显示了一个缺失图像的标志。
起初,我认为我应该优雅地释放和释放上下文,但经过一段时间的搜索后,我唯一的运气是找到一些调用扩展上的loseContext()
的方法,但在调用后画布的外观就像在图像中的画布一样。
我还尝试删除上下文中使用的所有资源(如缓冲区、程序等),但也没有成功。
那么,我应该如何实现这一目标?这是否可能?
英文:
I want to draw on canvases (more than 100) and have their content preserved. My setup is that I create canvases in a loop, and for each one I get the webgl2
context of it and do some drawing. The problem is, that after 16 canvases, when i get the webgl2
context from the 17th, the first one shows a missing image logo
I first thought that I should gracefully free up and release the context, but after googling a while, my only luck was to find some hack to call loseContext()
on an extension, which ended in making the canvas look like the one in the image right after the call.
I also tried deleting all the resources used in the context (like buffers, programs and etc.) but no luck there either.
So, how do I achieve that? Is it possible at all?
答案1
得分: 2
基于WebGL基础知识,大多数浏览器同时支持8个WebGL上下文。
Chromium曾提出一个已注册请求来使上下文数量可调整。他们还表示,这个值很难进行配置,因为实际上下文与硬件紧密相连,如果超出限制,可能会导致视频驱动程序停止工作。
然而,新的配置值 --max-active-webgl-contexts=32
已经引入,Chromium 的官方默认值是16。
还有一个项目可以允许虚拟化WebGL上下文,但不清楚它的使用可能会产生什么影响。
总结:
- 上下文数量取决于浏览器和硬件。
- 不能通过JavaScript更改。
- 在大多数情况下,假定只使用1个上下文。
请告诉我这是否有帮助。
英文:
Based on WebGL Fundamentals most browsers support only 8 webgl contexts simultaneously.
Chromium had a registered request to make a number of contexts adjustable. They also stated that it is not easy to make this value configurable because the actual contexts are tightly connected with hardware and, if exceeded, can make the video driver stop.
However, the new config value --max-active-webgl-contexts=32
was introduced, and the official default value for Chromium is 16.
There is also a project that allows virtualizing WebGL contexts, but it is not clear what impact its usage may have.
To summarize:
- The number of contexts is browser and hardware dependent
- It cannot be changed via JS
- In most scenarios, it is assumed that only 1 context is used
Please let me know if this helps.
答案2
得分: 1
简单的答案是,每个浏览器都限制了活动(webgl)上下文的数量。正如您自己发现的那样,基于Chromium的浏览器限制为16,而Firefox则为300,例如。
如果超过了此限制,浏览器将开始丢弃最旧的上下文,这意味着它会释放与该特定上下文相关的所有资源,包括用于存储其像素数据的后备缓冲区。在Chrome上,您将收到如下警告:
警告:活动的 WebGL 上下文太多。最旧的上下文将被丢弃。
不幸的是,上下文的数量不能更改。您可以根据“保留内容”的定义来采取的措施不同。如果这意味着您想备份其后备缓冲区的内容,可以采取以下解决方法:
- 在创建画布和其上下文时,保持活动上下文的计数,并将对其的引用存储在数组中,例如
canvases=[]
。 - 如果活动上下文的数量超过了一个特定的数字 - 比如16 - 从
canvases
数组中获取当前位置之前的第16个上下文。 - 创建一个具有2D上下文的新画布,并使用
drawImage()
从webgl
画布中复制像素数据。 - 使用
WEBGL_lose_context
扩展,摆脱旧的webgl上下文。 - 等待它发出
webglcontextlost
事件,这样您就知道可以安全地创建新的webgl上下文了。 - 通过刚刚复制的克隆替换旧的画布与webgl上下文。
以下是以上述方式生成64个webgl上下文的示例:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const MAX_CONTEXTS = 16;
(async() => {
let canvas, context;
let width = 40;
let height = 40;
let canvases = [];
let oldCanvas;
for (let a = 0; a < 64; a++) {
if (canvases.length >= MAX_CONTEXTS) {
oldCanvas = canvases[canvases.length - MAX_CONTEXTS];
canvas = document.createElement("canvas");
context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
context.drawImage(oldCanvas.canvas, 0, 0);
oldCanvas.context.getExtension("WEBGL_lose_context").loseContext();
await new Promise(resolve => {
oldCanvas.canvas.addEventListener('webglcontextlost', resolve);
});
document.body.insertBefore(canvas, oldCanvas.canvas);
canvas.id = oldCanvas.canvas.id;
document.body.removeChild(oldCanvas.canvas);
}
canvas = document.createElement("canvas");
canvas.id = a;
canvas.width = width;
canvas.height = height;
context = canvas.getContext("webgl", {
preserveDrawingBuffer: true
});
context.viewport(0, 0, context.drawingBufferWidth, context.drawingBufferHeight);
context.clearColor(Math.random(), Math.random(), Math.random(), 1.0);
context.clear(context.COLOR_BUFFER_BIT);
document.body.appendChild(canvas);
canvases.push({
canvas: canvas,
context: context
});
}
})();
<!-- end snippet -->
希望这有助于您理解和解决问题。
英文:
The simple answer is that each browser limits the amount of active (webgl) contexts. As you found out yourself, with Chromium-based browsers the limit is 16, while in Firefox it's 300 for example.
If you exceed this limit, the browser will start throwing away the oldest contexts, what means that it frees up all resources related to that particular context, including the backbuffer used to store it's pixeldata. On Chrome you'll get a warning like:
> WARNING: Too many active WebGL contexts. Oldest context will be lost.
Unfortunately the amount of contexts can't be changed. What you can do depends on your definition of preserving content
. If it means you want to backup the contents of it's backbuffer, you can workaround like this:
- While creating the canvases and it's contexts keep a counter of active contexts and store a reference to those inside an array e.g.
canvases=[]
- If the number of active contexts exceeds a magic number - let's say 16 - get the context 16 elements before the current from the
canvases
array - Create a new canvas with a 2d context and copy the pixeldata from the
webgl
canvas usingdrawImage()
- Using the
WEBGL_lose_context
extension, get rid of the old webgl context - Wait until it emits the
webglcontextlost
event, so you know it's save to create a new webgl context - Replace the old canvas with the webgl context by the just copied clone
Here's an example of generating 64 webgl contexts in the aforementioned way:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const MAX_CONTEXTS = 16;
(async() => {
let canvas, context;
let width = 40;
let height = 40;
let canvases = [];
let oldCanvas;
for (let a = 0; a < 64; a++) {
if (canvases.length >= MAX_CONTEXTS) {
oldCanvas = canvases[canvases.length - MAX_CONTEXTS];
canvas = document.createElement("canvas");
context = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
context.drawImage(oldCanvas.canvas, 0, 0);
oldCanvas.context.getExtension("WEBGL_lose_context").loseContext();
await new Promise(resolve => {
oldCanvas.canvas.addEventListener('webglcontextlost', resolve);
});
document.body.insertBefore(canvas, oldCanvas.canvas);
canvas.id = oldCanvas.canvas.id;
document.body.removeChild(oldCanvas.canvas);
}
canvas = document.createElement("canvas");
canvas.id = a;
canvas.width = width;
canvas.height = height;
context = canvas.getContext("webgl", {
preserveDrawingBuffer: true
});
context.viewport(0, 0, context.drawingBufferWidth, context.drawingBufferHeight);
context.clearColor(Math.random(), Math.random(), Math.random(), 1.0);
context.clear(context.COLOR_BUFFER_BIT);
document.body.appendChild(canvas);
canvases.push({
canvas: canvas,
context: context
});
}
})();
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论