10
\$\begingroup\$

Preface: This question is going to be coming from a Direct3D point-of-view, because that's what I'm familiar with.

Obviously we incur a slight overhead every time we change the vertex or index buffers in Direct3D (i.e. with IASetIndexBuffer). But I suppose if we allocate one giant index/vertex buffer we'll end up having to manage the memory manually, and sort of end up with a fragmentation problem (or pauses while we swap all the data around).

So how do commercial engines load/unload data? I'm sort of leaning towards a 'package' based approach, so a 'package' of models has its own vertex/index buffer that we can load/unload as the package is no longer needed; and then try to draw all instances from the same package together.

But I'd be interested to hear what the accepted/standard way of handling this is?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is a good question and it applies to OpenGL just as well. \$\endgroup\$
    – glampert
    Jul 26, 2014 at 18:29

1 Answer 1

15
\$\begingroup\$

There are a number of different techniques for organizing the data, and many games use a mix of them.

For static geometry, it's best to have fewer individual IBs and VBs.

Traditionally games are 'level' based which means the assets for a section of play are loaded and then the game play begins. To minimize load-times, the information is ideally organized to support this (i.e. load everything for the level as fast as possible without seeking around on the media/disk), possibly replicating information between levels. In this approach, one scheme is to have a large VB and IB for a model or a series of models, and then submit subsets from that to draw.

For the Xbox 360 / PS 3 generation, the amount of RAM was quite small relative to the size of the games, so engines became 'streaming' oriented which means they load content dynamically as the player moves through them. This is also called an 'open-world' approach. In this approach, the challenge is 'chunking' the geometry into bits you can load based on spatial hints. Another challenge particularly for 32-bit platforms is ensuring that memory doesn't become fragmented (or even virtual address space fragmentation) over a long period of time--level-based games often reset memory between levels, something streaming engines can't do as easily. Therefore, packing into a fixed size or set of fixed sizes for the IBs/VBs which were allocated and reused as a pool is helpful.

For the Xbox One / PS 4 generation, there's a lot of RAM to work with relative to earlier consoles, and a lot of extra cores, so many games are aggressively compressing their assets and then using "extra" CPU cores to decompress them in the background. With 64-bit virtual address space, there is less concern about virtual address space fragmentation. This approach keeps load times low, packs assets well for digital download, and supports ‘open-world’ rendering. Here the pool approach to managing IBs/VBs is used.

There is also dynamic geometry submission which is often used for terrains, deformable/destructible models, etc. It is not an efficient in terms of GPU bus bandwidth, so games often have a mix of both static and dynamic geometry. In this case, the Direct3D Map DISCARD update pattern typically means you are only using a single IB/VB (or for multithreaded rendering scenarios, dual IB/VB which you swap filling/rendering each frame).

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for your answer. May I ask some clarifying questions? What is the Direct3D Map DISCARD pattern? \$\endgroup\$ Jul 29, 2014 at 20:31
  • 1
    \$\begingroup\$ The pattern is described here for Direct3D 9 as Lock DISCARD, but that pattern is used with Direct3D 10.x/11.x as well as Map DISCARD for dynamic resources. You can see an example of how this is implemented for Direct3D 11 by looking at DirectX Tool Kit's PrimitiveBatch class. \$\endgroup\$ Jul 29, 2014 at 20:47

You must log in to answer this question.

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