Virtual texturing

When implementing virtual texturing how do you determine which portion of the texture/s are currently visible? To be clear I mean how do I decide what to update the texture atlas with?

Thanks in advance for any help.
 
You render to a small framebuffer and jitter a little to catch things you might miss otherwise, then you read back from it to find out what you need.
I'm pretty sure there are a number of presentations available out there detailing the process, AFAIR one even provides source code.
 
Our virtual texturing system renders the scene to a 4x4 smaller 32 bit per pixel frame buffer. For this pass you do not need to calculate anything else than texture coordinates for each pixel and the mip level, so the pixel shader will be pretty simple (and very fast). Vertex shader input can be also really simple: position and (a rough precision) texture coordinate. Pass though the texture coordinate to pixel shader and transform the position.

In the pixel shader, you can calculate the mip level either with gradient instructions (based on texture coordinate screen space derivatives) and a few math instructions, or you can use DX10/DX11 texture objects' CalculateLevelOfDetail-method (but that method needs DX10.1 hardware or better). Both methods are really fast.

We directly transform the texture coordinates to page IDs in the pixel shader, as it saves a lot of CPU cycles (later in the pipeline), and makes it easier to quickly remove duplicate pages when the data is processed by the CPU. Our page ID is basically: 4 bit mip, 11 bit page Y, 11 bit page X (starting from most significant to least significant). The pixel shader outputs the packed page ID.

Now on the CPU side, you will lock the texture data buffer (use staging resources on DX10/11). Each 32 bit value in the buffer represents one visible page. There's usually a lot of same page IDs in the locked buffer, so I recommend doing some quick and dirty duplicate removal before feeding that data to your cache (*). We are using LRU policy in our cache, and it has been working really well. In your cache design focus on query speed, since every visible page needs to be queried in every update (you do not want to load data that's already on cache). Adding and deleting pages can be slower operations, since the frequency of those operations isn't as high.

(*) You can iterate the pixel buffer, and if "pixel = last added pixel" do not add it to visible array. This removes huge amount of duplicates already. On consoles it's very efficient to do duplicate check for whole cache tiles (it's very efficient since scanlines are not linear, but in tiled format, so a region of the image is in linear memory addresses). This kind of local duplicate check removes almost all duplicates with a very low cost.
 
Just to be clear here, nothing about virtual texturing explicitly requires you to render to a buffer and read it back to determine visible pages. You could certainly use other means to determine page visibility, in fact it's even possible to precompute (I believe the iPhone version of RAGE does this, someone correct me if I'm wrong).
 
What exactly did you mean by jitter a little?

Every frame you can translate your projection a tiny bit (in XY space relative to the camera). This lets you pick up small details that might otherwise be missed due to rendering at a lower resolution.
 
Our page ID is basically: 4 bit mip, 11 bit page Y, 11 bit page X (starting from most significant to least significant). The pixel shader outputs the packed page ID.

That's 26bits worth of bits: how efficient would it be to implement it as a logical OR into a 2^26 bitmap (8MByte) on PS3/360/PC? How well do different consoles and PC GPU generations support integer boolean operations?

Reducing mip resolution down to 2 bits looks like a reasonable optimization for that case , to get down to 2MByte. Is there a point in managing mips with such a fine granularity? I can only think of the very reduced memory on consoles. Looks to me that paging 16 mip levels (some of them tiny) is over managing the problem.

22 bits for page number are 4 million unique pages, possibly postprocessing the final content to linear 1D page numbers could lead to further address bits reductions, reducing pagehit bitmap memory footprint below 1Mbyte.
 
Such as...?

The viewer's distance from an object, or even a fully precomputed solution based on viewer position. Something that doesn't depend on feedback wouldn't suffer from any blurry mess when the viewer suddenly turns 180. It also would enable you to prefetch textures for an object that will suddenly fill the user's view, even if it isn't there yet. (wouldn't want your Imp dropping out of a monster closet to appear covered in all of 2x2 pixels).
 
Visibility in real-time graphics has a long history of research. Most of it was for the purpose of hidden surface removal, but it's almost the exact same problem (instead of trying to figure out which polygons or objects are visible, you want to figure out which texture pages are visible). So you can rasterize on the GPU and feed back to the CPU, or you can rasterize on the GPU and have the GPU figure out which pages need to be loaded in, or you can rasterize completely on the CPU, you can use PVS, you can use portals/antiportals, you can use bounding volume hierarchies, you can ray-trace, you can use occlusion queries, etc. Or you can even do something really simple like what they did in Quake Wars, which is they just loaded in terrain pages in a concentric ring around the camera.
 
Sorry to bump an old topic but I have a stupid question... In software based virtual texture mapping triangle (UV's) can't cross tile boundaries right? At least if you're using texture filtering. The texturing unit would potentially fetch the wrong data correct?
 
Back
Top