This is an overview of a quasi virtual texturing system that I implemented for my game.
I use this system instead of a traditional virtual texturing system for the following reasons.
- Supports all levels of anisotropic/trilinear filtering, even on old hardware
- Doesn’t use tiled resources which require newer GPUs, and aren’t available on Windows 7 with d3d11
- I use triplanar texturing exclusively, and do not have traditional UVs
Algorithm
This algorithm uses multiple texture arrays, and a structured buffer that indicates per material, which array to sample from.
It uses a fixed sized amount of memory for textures on the GPU.
Textures are limited to square powers of 2, the only sizes supported are 256,512,1024, and 2048. This reduces the number of arrays we will need to a reasonable number.
I pre-generated a texture array that contains all of the mips of size 256 and under, for all textures.
I also allocate fixed sized arrays for 512,1024 and 2048; with the larger sizes having progressively fewer slots.
Number of slots : Here are the number of slots I have per array, these numbers are mostly arbitrary.
- 256: # of textures
- 512: 128 slots
- 1024: 32 slots
- 2048: 8 slots
Here is a visualization of the arrays.
Yellow is 256, Green is 512, Blue is 1024, and Red is 2048.
I force everything to use 256 past a certain distance to reduce divergence
A structured buffer is used as an indirection mapping; pass in the material ID, and retrieve which array to sample from, and which slot in the array.
Prioritizing Textures
As there is a limited number of high resolution textures that can be on the GPU at any given time, a system to was needed to determine which textures should be resident.
This is done by storing the N most important textures, along with a score, for each voxel patch.
The score is simple the number of vertices that reference the texture.
Each frame, for visible patches, the score is added to the appropriate textures/MIP based on the distance from the camera for the patch.
Patches which are out of the frustum or occluded do not get added.
The textures/mip with the highest score, that is not resident on the GPU is then considered for uploaded, if it has a higher score than a resident texture at the same mip level.
Only one texture is uploaded per frame, to limit the amount of data that needs to be transferred.
The structured buffer is also updated to reflect where the GPU can find the texture.
Latency is reduced by having the CPU side also store a cache of recently accessed textures, the caches size is adjustable, but is currently set to 300mb.
Results
The system works well and transparently pages in texture data based on visibility.
It never suffers from horribly blurry textures since the 256 mips are always available as a fallback.
Even standing still and turning the camera around can and will cause textures to be paged in/out.
Limitations
- D3d11 has a max texture array size of 2048. This means this system can only support 2048 separate materials. I am nowhere near this limit, so this isn’t an issue for me.