使用 Glium 进行高效的 2D 渲染
Efficient 2D rendering with Glium
我正在使用 Glium 为我正在编写的模拟器进行渲染。我拼凑了一些可行的东西(基于 this example),但我怀疑它效率很低。这是相关功能:
fn update_screen(display: &Display, screen: &Rc<RefCell<NesScreen>>) {
let target = display.draw();
// Write screen buffer
let borrowed_scr = screen.borrow();
let mut buf = vec![0_u8; 256 * 240 * 3];
buf.clone_from_slice(&borrowed_scr.screen_buffer[..]);
let screen = RawImage2d::from_raw_rgb_reversed(buf, SCREEN_DIMENSIONS);
glium::Texture2d::new(display, screen)
.unwrap()
.as_surface()
.fill(&target, MagnifySamplerFilter::Nearest);
target.finish().unwrap();
}
在高层次上,这就是我正在做的事情:
- 借用
NesScreen
,其中包含屏幕缓冲区,是一个数组。
- 将屏幕缓冲区克隆到向量中
- 从矢量数据创建纹理并渲染它
我怀疑通过 clone_from_slice
克隆整个屏幕缓冲区的效率确实很低。 RawImage2d::from_raw_rgb_reversed
函数拥有传递给它的向量的所有权,所以我不确定如何以避免克隆的方式执行此操作。
那么,两个问题:
这实际上效率低下吗?我没有足够的渲染经验来直观地了解。
如果是这样,有没有更有效的方法来做到这一点?我对 Glium 进行了相当多的搜索,但没有太多关于 2D 渲染的内容。
这不是一个很好的答案,但也许这里的一些事情可以帮助你。
首先:这真的很低效吗?这真的很难说,尤其是 OpenGL 部分,因为 OpenGL 性能在很大程度上取决于何时同步 required/requested。
关于屏幕缓冲区的克隆:你只是复制了180kb,这并不算多。我很快在我的机器上对它进行了基准测试,克隆一个 180kb 的向量大约需要 5µs,这真的不是很多。
请注意,您可以在不使用方法的情况下创建 RawImage2d
,因为所有字段都是 public。这意味着如果您自己创建一个反向向量,您可以避免简单的 5µs 克隆。 但是,用the method glium uses反转向量比仅仅克隆向量要慢很多;在我的机器上,相同长度的向量需要 170µs。如果你只想达到每帧 60fps = 17ms,这可能仍然 可以容忍 ,但仍然不是很好。
您可以考虑在原始数组中使用正确的行顺序来避免此问题。或者你可以,而不是直接将纹理复制到帧缓冲区,只需绘制一个带有纹理的全屏四边形(每个屏幕角一个顶点)。当然,然后您需要一个网格、一个着色器和所有这些东西,但是您可以通过调整纹理坐标 "reverse" 图像。
最后,不幸的是,我不太了解 GPU 执行 OpenGL 命令所花费的时间。我 猜测 这不是最佳选择,因为 OpenGL 没有太多空间来安排您的命令,但必须立即执行它们(强制同步)。但也许这对你来说是不可避免的。
我正在使用 Glium 为我正在编写的模拟器进行渲染。我拼凑了一些可行的东西(基于 this example),但我怀疑它效率很低。这是相关功能:
fn update_screen(display: &Display, screen: &Rc<RefCell<NesScreen>>) {
let target = display.draw();
// Write screen buffer
let borrowed_scr = screen.borrow();
let mut buf = vec![0_u8; 256 * 240 * 3];
buf.clone_from_slice(&borrowed_scr.screen_buffer[..]);
let screen = RawImage2d::from_raw_rgb_reversed(buf, SCREEN_DIMENSIONS);
glium::Texture2d::new(display, screen)
.unwrap()
.as_surface()
.fill(&target, MagnifySamplerFilter::Nearest);
target.finish().unwrap();
}
在高层次上,这就是我正在做的事情:
- 借用
NesScreen
,其中包含屏幕缓冲区,是一个数组。 - 将屏幕缓冲区克隆到向量中
- 从矢量数据创建纹理并渲染它
我怀疑通过 clone_from_slice
克隆整个屏幕缓冲区的效率确实很低。 RawImage2d::from_raw_rgb_reversed
函数拥有传递给它的向量的所有权,所以我不确定如何以避免克隆的方式执行此操作。
那么,两个问题:
这实际上效率低下吗?我没有足够的渲染经验来直观地了解。
如果是这样,有没有更有效的方法来做到这一点?我对 Glium 进行了相当多的搜索,但没有太多关于 2D 渲染的内容。
这不是一个很好的答案,但也许这里的一些事情可以帮助你。
首先:这真的很低效吗?这真的很难说,尤其是 OpenGL 部分,因为 OpenGL 性能在很大程度上取决于何时同步 required/requested。
关于屏幕缓冲区的克隆:你只是复制了180kb,这并不算多。我很快在我的机器上对它进行了基准测试,克隆一个 180kb 的向量大约需要 5µs,这真的不是很多。
请注意,您可以在不使用方法的情况下创建 RawImage2d
,因为所有字段都是 public。这意味着如果您自己创建一个反向向量,您可以避免简单的 5µs 克隆。 但是,用the method glium uses反转向量比仅仅克隆向量要慢很多;在我的机器上,相同长度的向量需要 170µs。如果你只想达到每帧 60fps = 17ms,这可能仍然 可以容忍 ,但仍然不是很好。
您可以考虑在原始数组中使用正确的行顺序来避免此问题。或者你可以,而不是直接将纹理复制到帧缓冲区,只需绘制一个带有纹理的全屏四边形(每个屏幕角一个顶点)。当然,然后您需要一个网格、一个着色器和所有这些东西,但是您可以通过调整纹理坐标 "reverse" 图像。
最后,不幸的是,我不太了解 GPU 执行 OpenGL 命令所花费的时间。我 猜测 这不是最佳选择,因为 OpenGL 没有太多空间来安排您的命令,但必须立即执行它们(强制同步)。但也许这对你来说是不可避免的。