是否可以在不先将整个纹理加载到常规RAM的情况下将纹理加载到OpenGL?

时间:2019-04-14 12:57:30

标签: opengl

通常,当我们将纹理加载到OpenGL中时,我们或多或少会这样做

  • 打开文件
  • 将所有字节读入系统RAM
  • 调用glTexImage2D()将字节发送到VRAM
  • 释放系统RAM中的字节

是否有一种方法可以将字节流式传输/加载到VRAM中,而跳过将其加载到系统RAM中的整个过程。像这样:

char chunk[1000];

f = openfile("some file")

glBindTexture(....)
glTexParameteri(...)
glTexParameteri(...)
glTexParameteri(...)

while (f.eof == false) {
  chunk = f.read_chunk(1000);
  glTexImage2D..... (here's the chunk)
}
closefile(f);

我想知道是否有一些新的OpenGL扩展可以做到这一点。

3 个答案:

答案 0 :(得分:2)

大多数图像文件格式都经过压缩或严格编码。因此,它们的至少某些部分必须首先通过常规系统内存。但是,如果使用OpenGL缓冲区对象,则不必为 system RAM中的整个图像分配缓冲区。但是,最终可能会在VRAM中分配缓冲区对象,所以您至少两次在其中存储了两次数据。

关于如何使用悬崖的笔记

size_t buffer_size = ...;
GLuint buffer_id;
glGenBuffers(1, &buffer_id);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer_id);
glBufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL, GL_STATIC_DRAW);

void *buffer_ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
size_t offset_into_buffer = ...;
read_image_pixels_into_buffer((char*)buffer_ptr + offset_into_buffer);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);

glTexImage2D(..., (void*)((uintptr_t)offset_into_buffer));

glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glDeleteBuffers(1, &buffer_id);

请注意,如果天真地使用缓冲区对象,则性能和效率可能会受到影响,如上面的代码所示。通常,将缓冲区对象用作内存池,从该池中按需要剪切大块,而不经常创建和删除缓冲区。但是,也有通过缓冲区孤立进行流传输,实际上它会重新创建缓冲区:https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming

有关@BDL评论的更新

使用OpenGL,您实际上无法控制 的发生方式,但是您可以将所采用的代码路径向某个方向移动。必须始终牢记的一件事是,OpenGL根本没有内存模型。事情应该以某种方式起作用。

一个副作用是,为了满足要求,基于GPU的OpenGL实现可能随时被迫在其他地方发送GPU内存中的某些数据。 …最可能的系统RAM,甚至磁盘交换空间。由于现代操作系统往往会过度使用内存,因此很有可能OpenGL实现将只保留与所有分配的OpenGL对象所需数量相同的实际页面错误内存。对于非流数据,在所有时间都将数据副本保留在系统内存中(或交换)是很有意义的,因为这将驱逐变为清除某些VRAM内存分配条目。

现在,对于缓冲区对象,我上面概述的代码路径实际上可以通过缓冲区区域重命名来避免一个额外的副本,即,将数据加载到纹理中时,实际上并没有数据被复制,而只是纹理描述符设置为指向缓冲区对象中的特定区域…直到缓冲区中的该区域被覆盖,然后触发创建副本。但是,在将缓冲区对象(名称)加载到纹理中后立即删除它实际上可能会将缓冲区的整个内存释放给纹理对象。 _但这只是潜在的优化,可以实现OpenGL实现,但不必这样做。__

整个OpenGL规范非常含糊,这使得开发高性能OpenGL实现非常困难。

答案 1 :(得分:1)

如果不先将纹理加载到RAM中,就无法将纹理传递给OpenGL。 OpenGL不会以任何方式处理文件访问。

在大多数情况下(未压缩的纹理或具有DDS压缩的纹理除外),无论如何都必须进行一些处理才能将压缩文件的内容转换为OpenGL可以理解的格式。

答案 2 :(得分:1)

可以跳过一次加载 entire 纹理,并按代码所示逐块加载它。您只需要使用glTexSubImage2D上传块,并使用支持有效读取图块的文件格式(例如TIFF):

... open file ...
... create texture ...

for each tile in file {
    tile = f.read_tile();
    glTexSubImage2D(GL_TEXTURE_2D, 0, tilex, tiley, tilew, tileh, format, type, tile);
}

但是,与加载整个内容并将其传输到一个GL调用中相比,这可能会更慢

例如,如果图像通过网络连接逐渐加载,它仍然很有用。