Reducing VVIS Compile Times with func_detail in Source Engine
Learn how to slash VVIS compile times and reduce runtime overdraw in the Source Engine by strategically converting decorative world brushes to func_detail.
29 Sept 2026, 13:14 UTC

The Visleaf Explosion Problem
In the classic Source Engine BSP pipeline, every single world brush (the basic building blocks of your map) acts as a potential boundary. When the compiler runs vbsp, it slices the map into a grid of "visleaves"—convex volumes used to determine what the player can see from any given point.
The problem arises when you add decorative geometry. A simple maintenance corridor with twenty small pipe brushes, floor gratings, and wall trim doesn't just add polygons; it shatters the surrounding space into dozens of tiny, unnecessary visleaves. This inflates the portal graph that vvis must solve, leading to exponential increases in compile times and potential runtime overdraw as the engine processes more leaf transitions per frame.
The solution is to move non-sealing geometry into func_detail. This entity tells the engine: "Render this object, but do not use it to calculate visibility or split the world into leaves."
How func_detail Optimizes the BSP
A func_detail is a brush entity that behaves like world geometry in terms of collisions and rendering, but it is invisible to the visibility system. By converting decorative brushes to func_detail, you simplify the map's mathematical structure without changing its visual appearance.
The Critical Distinction: Sealing vs. Detail
Not every brush should be a detail. The engine relies on "sealing" geometry to define the boundaries of the playable area and to block sightlines between rooms.
- World Brushes: Use these for the outer hull of your map, primary walls, and floors. These must remain world brushes to prevent "leaks" (holes in the map that lead to the void) and to ensure
vviscan correctly determine that Room A cannot see Room B. - func_detail: Use these for trim, railings, pipes, small pillars, and any geometry that does not completely block a path or a view from one major area to another.
Worked Example: The Maintenance Corridor
Consider a narrow corridor modeled with forty small world brushes used for ceiling pipes and wall conduits. In a standard compile, these brushes create a fragmented mess of visleaves in the corridor's center.
The Optimization Workflow
- Baseline: Compile the map using your target SDK's default settings. Note the
vviselapsed time and the total visleaf count reported in the compile log. - Conversion: In Hammer, select all the pipe and conduit brushes. Use the
Tie to Entitytool (Ctrl+T) and selectfunc_detailfrom the dropdown. - Recompile: Run the compile process again.
- Verification: Compare the new
vvistime against the baseline. You should see a significant reduction in both the time spent calculating visibility and the total number of leaves generated.
Runtime Diagnostic
To verify the impact in-game, launch the map on a local listen server and enable cheats to access debug commands. Run the following in the console:
sv_cheats 1; r_visocclusion 1
With r_visocclusion enabled, the engine highlights the currently visible leaves. By comparing the "before" and "after" versions from the same camera position, you can observe how the engine now handles a few large leaves rather than dozens of fragmented ones.
Trade-offs and Limitations
The primary risk of overusing func_detail is the loss of occlusion. Because detail brushes are ignored by vvis, they cannot block the engine's visibility calculations. If you convert a thick wall to a func_detail, the engine will assume the player can see through that wall into the next room, forcing the GPU to render geometry that is technically hidden. This increases the rendering load (overdraw) and can lead to performance drops in complex scenes.
For dynamic occlusion—such as a large closing blast door—use func_occluder. For static boundaries between major sectors, use areaportals to explicitly tell the engine when to stop calculating visibility.
Actionable Summary
Before optimizing, always record your vbsp, vvis, and vrad times to ensure you are solving the right bottleneck. If vvis is the slowest stage, follow these steps:
- Keep a backup of your original VMF.
- Identify brushes that do not seal the map or block major sightlines.
- Convert those brushes to
func_detail. - Verify that the map still compiles without leaks.
- Confirm that rooms separated by structural walls remain occluded.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.