为什么我们应该在将一些数据放入此缓冲区之前调用 webgl.bindBuffer?
Why we should call webgl.bindBuffer before putting some data to this buffer?
我试图弄清楚缓冲区在 WebGL 中的确切工作原理,但我有点卡壳了。以下是我的猜测-请确认或否决。
const positions = new Float32Array([
-1, 1,
-0.5, 0,
-0.25, 0.25,
]);
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
- 我们通过 JS 在 RAM 上创建一个浮点数数组。
- WebGL 直接在 GPU 上创建一个空缓冲区,并 returns 在此缓冲区上引用 JS。现在变量
buffer
是一个指针。
- 将缓冲区指针设置为
gl.ARRAY_BUFFER
。
- 现在我们将数据从 RAM 复制 到 GPU 缓冲区。
- 从
gl.ARRAY_BUFFER
解除绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定它更多次)。
那么为什么我们不能直接用positions
调用createBuffer()
而不是使用ARRAY_BUFFER
作为JS和GPU之间的桥梁呢?这些是 OpenGL 的唯一限制 API 还是我们有充分的理由不这样做?如果我错了请纠正我,但是分配已知大小的内存比分配一些内存并在我们调用 bufferData
.
后重新分配 positions
大小更快
因为那是 API 唯一真实的答案。
很多人同意你的看法,换一个 API 会更好。这是出现新 API(DirectX11/12、Vulkan、Metal、WebGPU)
的原因之一
但问题中的描述在技术上不正确
我们通过 JS 在 RAM 上创建一个浮点数数组。
WebGL 创建一个 object 表示 GPU 缓冲区(GPU 上没有分配任何内容)
将缓冲区上的指针设置为 gl.ARRAY_BUFFER。
现在我们分配一个缓冲区并将数据从 RAM 复制到 GPU 缓冲区。
从 gl.ARRAY_BUFFER 解除绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定它更多次)。
不需要第 5 步。没有理由解除绑定缓冲区。
你可以这样想。想象一下,您有一个 javascript 函数将图像绘制到 canvas,但在您的示例中图像是通过与缓冲区相同的方式传递的。这是代码
class Context {
constructor(canvas) {
this.ctx = canvas.getContext('2d');
}
bindImage(img) {
this.img = img;
}
drawImage(x, y) {
this.ctx.drawImage(this.img, x, y);
}
}
假设您想绘制 3 张图片
const ctx = new Context(someCanvas);
ctx.bindImage(image1);
ctx.drawImage(0, 0);
ctx.bindImage(image2);
ctx.drawImage(10, 10);
ctx.bindImage(image3);
ctx.drawImage(20, 20);
会很好用。没有理由做
const ctx = new Context(someCanvas);
ctx.bindImage(image1);
ctx.drawImage(0, 0);
ctx.bindImage(null); // not needed
ctx.bindImage(image2);
ctx.drawImage(10, 10);
ctx.bindImage(null); // not needed
ctx.bindImage(image3);
ctx.drawImage(20, 20);
ctx.bindImage(null); // not needed
在WebGL中也是一样的。有时将 null 绑定到某些东西,例如
gl.bindFramebuffer(gl.FRAMEBUFFER, null); // start drawing to the canvas
但大多数时候解除绑定只是程序员的个人喜好,而不是API本身需要的东西
参考文献:
- https://webglfundamentals.org/webgl/lessons/webgl-attributes.html
- https://webglfundamentals.org/webgl/lessons/resources/webgl-state-diagram.html
- https://whosebug.com/a/28641368/128511
请注意,即使我上面的描述在技术上也不正确。 step4 是否将数据复制到 GPU 是未定义的。它可以只将数据复制到 RAM,并且仅在绘制时复制数据,如果缓冲区已被使用,并且尚未复制到 GPU,则复制它。很多 driver 都是这样做的。有关 driver 不将数据复制到 GPU 的更具体示例,它似乎会看到 this answer and this one
我试图弄清楚缓冲区在 WebGL 中的确切工作原理,但我有点卡壳了。以下是我的猜测-请确认或否决。
const positions = new Float32Array([
-1, 1,
-0.5, 0,
-0.25, 0.25,
]);
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
- 我们通过 JS 在 RAM 上创建一个浮点数数组。
- WebGL 直接在 GPU 上创建一个空缓冲区,并 returns 在此缓冲区上引用 JS。现在变量
buffer
是一个指针。 - 将缓冲区指针设置为
gl.ARRAY_BUFFER
。 - 现在我们将数据从 RAM 复制 到 GPU 缓冲区。
- 从
gl.ARRAY_BUFFER
解除绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定它更多次)。
那么为什么我们不能直接用positions
调用createBuffer()
而不是使用ARRAY_BUFFER
作为JS和GPU之间的桥梁呢?这些是 OpenGL 的唯一限制 API 还是我们有充分的理由不这样做?如果我错了请纠正我,但是分配已知大小的内存比分配一些内存并在我们调用 bufferData
.
positions
大小更快
因为那是 API 唯一真实的答案。
很多人同意你的看法,换一个 API 会更好。这是出现新 API(DirectX11/12、Vulkan、Metal、WebGPU)
的原因之一但问题中的描述在技术上不正确
我们通过 JS 在 RAM 上创建一个浮点数数组。
WebGL 创建一个 object 表示 GPU 缓冲区(GPU 上没有分配任何内容)
将缓冲区上的指针设置为 gl.ARRAY_BUFFER。
现在我们分配一个缓冲区并将数据从 RAM 复制到 GPU 缓冲区。
从 gl.ARRAY_BUFFER 解除绑定缓冲区(但缓冲区在 GPU 上仍然可用,我们可以重新绑定它更多次)。
不需要第 5 步。没有理由解除绑定缓冲区。
你可以这样想。想象一下,您有一个 javascript 函数将图像绘制到 canvas,但在您的示例中图像是通过与缓冲区相同的方式传递的。这是代码
class Context {
constructor(canvas) {
this.ctx = canvas.getContext('2d');
}
bindImage(img) {
this.img = img;
}
drawImage(x, y) {
this.ctx.drawImage(this.img, x, y);
}
}
假设您想绘制 3 张图片
const ctx = new Context(someCanvas);
ctx.bindImage(image1);
ctx.drawImage(0, 0);
ctx.bindImage(image2);
ctx.drawImage(10, 10);
ctx.bindImage(image3);
ctx.drawImage(20, 20);
会很好用。没有理由做
const ctx = new Context(someCanvas);
ctx.bindImage(image1);
ctx.drawImage(0, 0);
ctx.bindImage(null); // not needed
ctx.bindImage(image2);
ctx.drawImage(10, 10);
ctx.bindImage(null); // not needed
ctx.bindImage(image3);
ctx.drawImage(20, 20);
ctx.bindImage(null); // not needed
在WebGL中也是一样的。有时将 null 绑定到某些东西,例如
gl.bindFramebuffer(gl.FRAMEBUFFER, null); // start drawing to the canvas
但大多数时候解除绑定只是程序员的个人喜好,而不是API本身需要的东西
参考文献:
- https://webglfundamentals.org/webgl/lessons/webgl-attributes.html
- https://webglfundamentals.org/webgl/lessons/resources/webgl-state-diagram.html
- https://whosebug.com/a/28641368/128511
请注意,即使我上面的描述在技术上也不正确。 step4 是否将数据复制到 GPU 是未定义的。它可以只将数据复制到 RAM,并且仅在绘制时复制数据,如果缓冲区已被使用,并且尚未复制到 GPU,则复制它。很多 driver 都是这样做的。有关 driver 不将数据复制到 GPU 的更具体示例,它似乎会看到 this answer and this one