101010
101010

Reputation: 15726

Is it possible to load a texture into OpenGL without first loading the entire texture into regular RAM?

Usually, when we load a texture into OpenGL, we more or less do it like this

Is there a way to stream/load the bytes into VRAM skipping the whole loading it into system RAM part. Something like this:

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);

I'm wondering if there is some new OpenGL extension that can do this.

Upvotes: 2

Views: 2212

Answers (3)

datenwolf
datenwolf

Reputation: 162164

Most image file formats are either compressed or heavily coded. So at least some parts of them must first pass through regular system memory. However it is not necessary to allocate a buffer for the whole image in system RAM, if you use a OpenGL buffer object. It will however likely end up allocating the buffer object in VRAM, so you have the data there twice, at least for some time.

The cliff notes on how to use it is

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);

Take note that performance and efficiency might suffer if buffer objects are used naively, as in the code above. Usually buffer objects are used as memory pools from which chunks are cut on as-required base and buffers not constantly created and deletes. However there's also streaming via buffer orphaning, which does in fact re-create buffers: https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming

Update regarding @BDL's comment

With OpenGL you really don't have any control of how things will happen, but you can nudge the taken code path into a certain direction. One thing one always has to keep in mind is, that OpenGL doesn't have a memory model at all. Things are just supposed to work… somehow.

A side effect of that is, that in order to fulfill the requirements, a GPU based OpenGL implementation may at any moment be forced to evict some of the data in GPU memory somewhere else. …most likely system RAM, but maybe even disk swap space. Since modern operating systems tend to overcommit memory, it's thus very likely, that a OpenGL implementation will simply keep the same amount of actually page-faulted memory around as is required for all allocated OpenGL objects. And for non-streaming data it makes a lot of sense, to have a copy of the data in system memory (or swap) all the time, because this turns eviction into clearing some VRAM memory allocation entry.

Now in the case of buffer objects, the very code path I outlined above may in fact avoid one extra copy, by buffer region renaming, i.e. when loading the data into the texture, no data is actually copied, but just the texture descriptor set to point at that particular region in the buffer object… until that region in the buffer is overwritten, which then triggers the creation of a copy. However deleting the buffer object (name) immediately after loading it into the texture may in fact relegate the whole memory of the buffer to the texture object. This however is just a potential optimization, a OpenGL implementation could implement, but doesn't have to.

The whole OpenGL specification is extremely vague, which makes developing a high-performance OpenGL implementation ming boggingly hard work.

Upvotes: 3

Yakov Galka
Yakov Galka

Reputation: 72479

It is possible to skip loading the entire texture at once, and load it chunk-by-chunk as you showed in your code. You just need to upload the chunks with glTexSubImage2D and use a file format that supports reading tiles efficiently (e.g. 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);
}

This, however, will probably be slower compared to loading the entire thing and transferring it in one GL call.

It can still be useful if your image loads progressively through a network connection, for example.

Upvotes: 2

BDL
BDL

Reputation: 22167

There is no way how you could pass a texture to OpenGL without loading it first into your RAM. OpenGL doesn't handle file access in any way.

Most of the time (except for uncompressed textures or textures with DDS compression), one has to do some processing anyway to get the compressed file content into a format OpenGL can understand.

Upvotes: 1

Related Questions