Files
Faset_Engine/docs/manual/editor/visibility-lod.md
T

4.6 KiB
Raw Blame History

GPU visibility and prepared mesh LOD

Faset can draw opaque static triangle meshes through GPU frustum culling and, optionally, two-pass HZB occlusion. The original direct renderer remains available for comparison. In the Editor, build with FASET_DEBUG_IMGUI=ON, press F12, and choose Direct, GPU frustum, or GPU occlusion in Diagnostics. Path: active confirms that the selected GPU path actually ran. See developer diagnostics for the HZB preview and counters.

The current GPU path groups instances with the same mesh and texture into fixed indirect draw bins. A previous-frame HZB can defer an object, but the current-frame post pass checks it again before the final image. Sprites, editor UI, transparent meshes, and shadow casters keep their existing ordered or shadow paths. This option does not require ray tracing or mesh shaders.

Supply prepared LODs from C++

DrawItem::mesh is LOD 0. Set lod_meshes[0] to a prepared LOD 1 mesh, lod_meshes[1] to LOD 2, and so on. These are actual Mesh objects with a complete triangle list; Faset does not simplify a source mesh automatically. Keep a stable instance_key across frames. Give different scene objects different keys, and retain the same shared mesh objects instead of rebuilding them every frame. For a manually constructed Snapshot, supply both projection and view_projection; set view_id for each camera and camera_cut when switching shots. The Player's scene extraction fills the camera fields and instance keys.

faset::render::DrawItem draw;
draw.mesh = prepared_high_detail;
draw.lod_meshes = {prepared_medium_detail, prepared_low_detail};
draw.instance_key = "scene/object-id/primitive-id";
draw.model = faset::render::transform({0, 0, 0});
snapshot.draws.push_back(std::move(draw));

LOD selection uses projected screen size. The first transition is 192 pixels; each additional level halves that threshold. A 12% hysteresis band avoids switching back and forth near a boundary. Missing levels fall back to an available mesh. Changing level invalidates that instance's temporal occlusion state while preserving its logical key. FrameStats::lod_counts reports selected levels 03 (higher levels are included in the final bucket).

For a runnable C++ example, build faset_p2_visibility_example and capture the same scene in any mode:

cmake --build --preset linux-debug --target faset_p2_visibility_example
build/linux-debug/faset_p2_visibility_example occlusion /tmp/p2-visibility.ppm
build/linux-debug/faset_p2_visibility_example direct /tmp/p2-direct.ppm

The example creates a fine cube and a prepared coarse tetrahedron in examples/renderer/p2_visibility.cpp. Its near object selects LOD 0 and the eight smaller objects select LOD 1; the program prints those counts and reports Vulkan validation errors. Imported GLB geometry can be supplied as prepared levels by C++ integration, but automatic LOD generation and assigning a GLB's alternate meshes as LODs in the Inspector are not implemented yet.

Measure before choosing a mode

The GPU route saves per-instance CPU vertex transformation and direct draw submission, but its compute passes and indirect rendering have a cost. Keep the same scene, resolution and camera path when comparing modes. Hide Diagnostics for an ordinary Player measurement: opening it enables GPU counter readback, while Show HZB adds a separate image copy. The renderer currently captures and waits for every frame even without this overlay. Read profiling and measurements and the P2 acceptance study for the measured scope and limitations.

Select a mode in the Player

The exported Player uses Direct by default. Choose a GPU mode for one run with --visibility; the same option works for a development Player and a relocated standalone game:

./faset_player --visibility gpu-frustum
./faset_player --visibility gpu-occlusion --headless --frames 120 \
  --profile visibility-profile.json

Accepted values are direct, gpu-frustum, and gpu-occlusion. The profile records the requested visibility_mode and each frame's gpu_visibility_active status. If the device lacks the GPU path, the Player reports a Direct fallback; selecting a mode alone does not prove that it ran. The Editor's Diagnostics selector changes only its viewport and does not save a Player setting. An exported game contains the P2 shader bundle and does not need the Slang compiler at runtime.