5
\$\begingroup\$

I'm building a game in OpenGL, relying on shaders as much as possible because python. However, I think I've been working under a misunderstanding of OpenGL since I started.

I've abstracted stuff into a homemade shader class/object that takes vertices and images, and turns them into VBOs and textures, and a draw() function. Not realizing there was a small limit on the GL_TEXTURE_* stuff, I was giving each one its own texture slot (as I'm thinking of them). I also hadn't yet bothered making sure the same models were using the same shader. You can see how I was running into problems.

So, just how do you deal with using more than 16/32/GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS textures in a frame? If I have floor, a player, three NPCs, four types of tree and a house, (each with unique textures) how am I going to add a fridge?

\$\endgroup\$

4 Answers 4

13
\$\begingroup\$

You only need the textures bound when you'd need to refer to them during the rendering of an object. You do not need every texture you will ever use bound to the pipeline at once.

Thus, to render the floor, you bind only the textures you need for the floor; then you render the floor. Then you bind only the textures you need for the NPCs, and render all the NPCs. Et cetera. This generalizes to something like

foreach (object in the scene) {
  texture_slot = 0;
  foreach (texture used by that object) {
    bind the texture to texture_slot
    ++texture_slot
  }

  draw the object
}

Note that texture state changes like this are relatively expensive, so when possible you want to minimize them (perhaps by sorting your object list by which textures they use, so you don't switch back and forth).

However, this is the general idea that you want to employ.

\$\endgroup\$
2
  • \$\begingroup\$ So, how does this work with defining objects and their textures at the start, and loading them into graphics memory? Do I have to reupload the texture whenever I want to bind it? \$\endgroup\$
    – bonzairob
    Commented Jul 23, 2013 at 18:44
  • \$\begingroup\$ No, not necessarily. You can have more textures in-memory on the GPU that you have bound to shader input slots. \$\endgroup\$
    – user1430
    Commented Jul 23, 2013 at 18:59
1
\$\begingroup\$

You are misunderstanding GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS.

This value dictates the maximum number of simultaneously bound textures at any given time, and that you can access in a shader via texture(tex_id,coord.xy).

You can have as many textures as you have GPU memory, and more, if you have a smart asset manager than loads/unloads assets as required. But I digress.

In all practical circumstances, unless the hardware specification of GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is something low, like a value of two, it should have zero impact on your game. Even with a relatively fancy rendering system, you should need no more than 5-6 textures bound at once for each rendered object.

\$\endgroup\$
0
\$\begingroup\$

You could merge multiple images into one (like a sprite sheet) if they are small enough then crop the UV to that selected image when you need to render it. It helps avoid rebinding texture slots if you've got a lot of small images.

\$\endgroup\$
0
\$\begingroup\$

When one runs against this limit, then one is almost certainly doing something wrong.

Using lots of textures has many obvious and not-so-obvious disadvantages, the most obvious one being that you have no way of knowing how many you can use (at the time of writing the program). Sure, in theory you would query the limit, but let's stay realistic. It's not a very workable (or efficient) approach. In particular, what do you do if you have 17 textures, and the limit is 16? Leave one object black?

Binding one of several small sets of textures at a time as needed is a common approach, but is not the optimum since changing texture descriptor sets is possibly a massively expensive operation. GPUs can typically switch between a very small number of descriptor sets for almost free, but whenever you exceed that unknown small number (which might be something like 2 or 4), you have a complete pipeline stall. That's going to kill you.
So, rebinding textures once or twice per frame is probably no issue, but doing it 200 times is a bad idea. Keyword: batching. When one says "batching" that doesn't necessarily mean draw calls (those are actually pretty cheap with GL). Batching refers to leaving all state (such as textures) as it is. If one changes textures (or state in general) mid-frame, then at the very least care must be taken to sort and thus batch all objects by state.

Ideally, one uses a single small set of textures (never change bindings) where the many different textures are either packed in what's called a texture atlas, or if the hardware supports it (virtually all hardware nowaday does, core since 3.0) different textures are actually layers within the same array texture.
A texture atlas works with even older hardware, but has a few little gotchas (bleeding, derivatives, etc) that you need to take care of. Packing textures into an array texture "just works". All you need is one uniform to pick the right layer.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .