RenderReflectionProbeLevel - ApertureViewer/Aperture-Viewer GitHub Wiki
The RenderReflectionProbeLevel
setting is a crucial control within the Second Life viewer's rendering pipeline that dictates the scope and type of reflection probes actively utilized to generate and display environmental reflections. Reflection probes capture cubemaps of their surroundings, which are then sampled by shaders applied to reflective surfaces (like water, shiny objects, PBR materials) to simulate realistic reflections.
This setting acts as a quality and performance tier selector, allowing users to balance visual fidelity against computational cost. The levels, as defined in the setting's comment and corroborated by code logic, correspond to:
-
Level 0: Disabled (Global Fallback Only): Only a single, very basic "default probe" (
mDefaultProbe
inLLReflectionMapManager
) is potentially updated and used. This probe typically captures a low-detail, distant view (sky, basic terrain/water) and acts as a global fallback when no other relevant probe is available or when higher levels are disabled. Reflections will lack local detail and accuracy. -
Level 1: Manual Probes Only: Enables the use of reflection probes explicitly placed by content creators (often represented by
LLVOVolume
objects configured as probes, potentially registered viaLLReflectionMapManager::registerSpatialGroup
or similar). The default global probe remains active as a fallback. Reflections become more accurate in areas specifically designated by creators (e.g., interiors). - Level 2: Manual + Terrain/Water Probes: Builds upon Level 1 by adding automatically generated probes associated with terrain and water surfaces. This significantly improves the realism of outdoor environments, allowing landscapes and water bodies to reflect the sky and nearby scenery more accurately.
-
Level 3: Manual + Terrain/Water + Object Probes: The highest level, incorporating probes dynamically attached to specific scene objects (registered via
LLReflectionMapManager::registerViewerObject
). This allows reflective objects (prims, avatar attachments with appropriate materials) to potentially receive highly localized reflections from their immediate vicinity, providing the most detailed and dynamic reflection effects.
In essence, increasing the RenderReflectionProbeLevel
enables progressively more sophisticated and numerous reflection sources, aiming for greater visual realism at the cost of increased resource utilization.
The RenderReflectionProbeLevel
setting influences several key areas of the codebase:
-
Reading the Setting: The value is primarily accessed using the
LLCachedControl
mechanism within various classes. Examples:-
LLReflectionMapManager::update()
:static LLCachedControl<S32> sLevel(gSavedSettings, "RenderReflectionProbeLevel", 3);
(Note: Fallback default3
used here). -
LLViewerShaderMgr::loadBasicShaders()
:S32 probe_level = llclamp(gSavedSettings.getS32("RenderReflectionProbeLevel"), 0, 3);
-
LLPipeline::renderGeomPostDeferred()
andLLPipeline::renderDeferredLighting()
:static LLCachedControl<S32> probe_level(gSavedSettings, "RenderReflectionProbeLevel", 0);
(Note: Fallback default0
used here, but the runtime value from settings takes precedence).
-
-
Reflection Map Manager (
llreflectionmapmanager.cpp
):-
LLReflectionMapManager::update()
: This core function's behavior is heavily gated bysLevel
.- If
sLevel == 0
, the main probe processing loop (for (unsigned int i = 0; i < mProbes.size(); ++i)
) has specific logic (if (sLevel == 0) { ... break; }
) to essentially only consider themDefaultProbe
for updates, significantly reducing processing. - If
sLevel > 0
, the loop processes other probe types (Manual, Terrain/Water, Object) based on their relevance, distance, priority, and the manager's scheduling logic (check_priority
,allocateCubeIndex
).
- If
-
LLReflectionMapManager::updateProbeFace()
: Includes an assertionllassert(gSavedSettings.getS32("RenderReflectionProbeLevel") > 0);
before proceeding to update probes other than themDefaultProbe
, reinforcing that non-default probes are ignored at Level 0.
-
-
Shader Compilation and Behavior (
LLViewerShaderMgr::loadBasicShaders()
):- The clamped
probe_level
value is injected into shader source code as a preprocessor define:attribs["REFMAP_LEVEL"] = std::to_string(probe_level);
. - Fragment shaders, particularly those involved in lighting and material rendering (e.g.,
deferred/reflectionProbeF.glsl
), use this define (#if REFMAP_LEVEL > X ... #endif
) to conditionally compile and execute different code paths. Higher levels enable more complex sampling logic involving multiple probe lookups, blending, and parallax correction. Level 0 might result in shaders that skip complex reflection code entirely or only sample the default probe.
- The clamped
-
Rendering Pipeline Optimizations (
pipeline.cpp
):-
renderDeferredLighting()
: When generating reflection probe cubemaps (gCubeSnapshot
is true), the processing of local point/spot lights is skipped ifprobe_level == 0
. This optimization assumes the default probe (active at Level 0) primarily needs to capture distant ambiance, not local light sources. -
renderGeomPostDeferred()
: Similarly, during probe snapshots (gCubeSnapshot
), atmospheric effects and water haze rendering are skipped (low_detail_probe = probe_level == 0 && gCubeSnapshot;
) ifprobe_level == 0
, further optimizing default probe generation.
-
-
Setting Change Handling:
- The
setting_setup_signal_listener
connects changes inRenderReflectionProbeLevel
to thehandleReflectionProbeDetailChanged
function. -
handleReflectionProbeDetailChanged()
: This function triggers a significant reset of the graphics state: it resets the reflection map and hero probe managers, releases and recreates OpenGL buffers, and forces shaders to be reloaded. This is necessary because changing theREFMAP_LEVEL
requires different shader code and potentially different buffer structures/sizes.
- The
Changing RenderReflectionProbeLevel
has a direct and significant impact on viewer performance:
-
CPU:
-
Level 0: Minimal CPU overhead within
LLReflectionMapManager
. Theupdate()
loop does very little work. -
Levels 1-3: CPU usage increases progressively. The manager needs to iterate through more potential probes, calculate distances/relevance, sort probes for prioritization, manage cube map slot allocation (
mCubeFree
), update probe neighbor lists (updateNeighbors
), and potentially track associated viewer objects. Level 3 incurs the highest CPU cost due to the potentially large number of object probes.
-
Level 0: Minimal CPU overhead within
-
GPU (VRAM):
-
Level 0: Lowest VRAM usage. Only the
mDefaultProbe
requires space in the reflection (mTexture
) and irradiance (mIrradianceMaps
) cube map arrays. -
Levels 1-3: VRAM usage increases significantly. Each active probe (
mCubeIndex != -1
) consumes a slot in themTexture
(radiance) andmIrradianceMaps
(irradiance)GL_TEXTURE_CUBE_MAP_ARRAY
objects. The total VRAM consumed depends on the number of active probes (up toLL_MAX_REFLECTION_PROBE_COUNT
), the probe resolution (RenderReflectionProbeResolution
), and whether HDR is enabled (RenderHDREnabled
). Level 3 potentially activates the most probes, leading to the highest VRAM footprint.
-
Level 0: Lowest VRAM usage. Only the
-
GPU (Processing/FPS):
- Level 0: Highest potential FPS (related to reflections). Fragment shaders execute simpler code paths, potentially skipping complex reflection sampling entirely. Fewer state changes and draw calls related to probe updates.
-
Levels 1-3: FPS generally decreases as the level increases.
- Probe Updates: Generating/updating cubemaps involves rendering the scene 6 times per probe update cycle (once per face), which consumes significant GPU time, even though it's amortized over time. More active probes mean more frequent update cycles running in the background.
- Shader Complexity: Fragment shaders execute more complex instructions at higher
REFMAP_LEVEL
s, sampling the cube map arrays, potentially blending between multiple probes, and performing parallax corrections. This increases the per-pixel cost on the GPU.
-
Responsiveness during Setting Change: Changing this setting triggers
handleReflectionProbeDetailChanged
, causing a noticeable pause or freeze as graphics resources are reinitialized and shaders are recompiled/reloaded.
The visual difference between the levels is substantial, particularly on surfaces with high reflectivity or PBR materials:
- Level 0: Reflections appear generic, uniform, and often inaccurate. Surfaces reflect a low-detail distant environment (often just the skybox) regardless of their immediate surroundings. Shiny objects in an interior might incorrectly reflect the sky. Visuals can look flat or unrealistic.
- Level 1: Reflections improve noticeably in areas designated by creators using manual probes. An indoor space with a manual probe will show reflections of that interior on shiny surfaces within the probe's influence. Areas without manual probes still exhibit Level 0 behavior.
- Level 2: Outdoor scenes gain significant realism. Water surfaces and terrain (if using reflective properties) will reflect the sky, clouds, and nearby scenery captured by their dedicated probes, reacting appropriately to changes in the environment (like time of day).
- Level 3: Provides the most accurate and dynamic reflections. Shiny objects can pick up detailed reflections from their immediate surroundings, including other nearby objects (if those objects have probes attached or are captured by nearby probes). This leads to a much more cohesive and immersive visual experience, especially in complex scenes with many reflective elements.
Based on the XML definition (<integer>3</integer>
) and the default fallback value used in the core LLReflectionMapManager::update()
function (LLCachedControl<S32> sLevel(..., 3);
), the intended default value for RenderReflectionProbeLevel
is 3. This suggests a design choice favoring visual fidelity out-the-box for users with capable hardware, enabling the full reflection system by default.
For users seeking maximum visual fidelity and possessing high-end hardware (sufficient CPU, GPU, and VRAM), the recommended value is 3. This level enables all types of probes (Manual, Terrain/Water, Object), offering the most detailed, localized, and accurate reflections possible within the viewer's capabilities. The visual benefits are most apparent on complex PBR materials, shiny surfaces, and water. Achieving the best results at this level also depends on having RenderReflectionProbeResolution
set to a high value (e.g., 256 or higher) and RenderHDREnabled
turned on.
Objective: To visually observe the differences in reflection quality and perceive changes in performance between RenderReflectionProbeLevel
settings.
Prerequisites:
- Second Life Viewer installed.
- Access to a region with a variety of reflective surfaces: water, shiny prims (metallic PBR materials preferred), potentially an interior space known to use manual reflection probes (if available).
- Enable the FPS display (Advanced menu > Performance Tools > Statistics Bar or similar).
Steps:
-
Baseline (Level 0):
- Go to Graphics preferences (Me > Preferences > Graphics).
- Set
Reflection Detail
slider toNone (sky only)
. This corresponds toRenderReflectionProbeLevel = 0
. Apply and OK. - Navigate to the test location. Observe reflections on water, shiny objects, etc. Note the generic, often sky-only nature of the reflections.
- Record the approximate FPS.
-
Manual Probes (Level 1):
- Go to Graphics preferences.
- Set
Reflection Detail
slider toTerrain and Trees
(This often corresponds to Level 1 or 2, check specific viewer mapping. If precise control via Debug Settings is needed, use that: SetRenderReflectionProbeLevel
to 1). Apply and OK. The viewer might pause briefly. - Observe the same surfaces. Look for changes, especially if inside a structure potentially using manual probes. Reflections might become slightly more relevant if manual probes are nearby.
- Record the approximate FPS. Note if it decreased.
-
Terrain/Water (Level 2):
- Go to Graphics preferences or Debug Settings.
- Set
RenderReflectionProbeLevel
to 2. Apply and OK. Wait for the pause. - Observe the same surfaces. Pay close attention to water and terrain reflections. They should appear more detailed and accurate, reflecting nearby objects/sky appropriately.
- Record the approximate FPS. Note any further decrease.
-
Full Detail (Level 3):
- Go to Graphics preferences.
- Set
Reflection Detail
slider to the highest setting (e.g.,Everything
). This corresponds toRenderReflectionProbeLevel = 3
. Apply and OK. Wait for the pause. - Observe the same surfaces. Shiny objects should now show the most detailed and localized reflections from their immediate surroundings.
- Record the approximate FPS. Note the likely lowest FPS value here.
Expected Outcomes:
- Visuals: A clear progression from generic sky reflections (Level 0) to increasingly localized and accurate reflections on water, terrain, and shiny objects (Levels 1-3). Level 3 should provide the most realistic results.
- Performance: FPS should generally decrease as the level increases from 0 to 3. The pauses when changing the setting should be noticeable. Users with VRAM monitoring tools might observe increasing VRAM usage.
Objective: To quantitatively measure the performance impact and verify the functional correctness of different RenderReflectionProbeLevel
settings.
Prerequisites:
- Compiled Debug build of the viewer.
- Profiling tools:
- GPU profiler (e.g., NVIDIA Nsight, AMD Radeon GPU Profiler, RenderDoc) for frame time analysis and shader inspection.
- System monitor for CPU usage per core/process.
- VRAM monitoring tool or GPU profiler capable of tracking texture memory allocation.
- Specific test scenarios/locations:
- A region known to be heavy on objects and reflective surfaces.
- An empty region (for baseline measurement).
- A region with manually placed reflection probes.
- Enable viewer debug HUDs/Stats (Statistics Bar, Debug GL, Debug Pipeline if needed). Consider enabling
RenderDebugProbeUpdates
(Debug Setting) to visualize probe activity.
Procedures:
-
VRAM Consumption:
- For each level (0, 1, 2, 3):
- Set
RenderReflectionProbeLevel
via Debug Settings. Let the viewer settle after the graphics reset. - Teleport into the complex test region.
- Using a VRAM monitor/profiler, measure the VRAM allocated specifically to the reflection probe cube map arrays (
mTexture
,mIrradianceMaps
inLLReflectionMapManager
). Note the difference between levels.
- Set
- For each level (0, 1, 2, 3):
-
CPU Usage:
- For each level (0, 1, 2, 3):
- Set the level and teleport to the complex region.
- Keep the camera static for a period (e.g., 30 seconds).
- Using a system monitor or profiler, measure the average CPU utilization of the viewer process, paying attention to threads potentially related to rendering or the reflection manager's update loop.
- Optionally, use a code profiler attached to the debug build to measure time spent specifically within
LLReflectionMapManager::update()
.
- For each level (0, 1, 2, 3):
-
GPU Frame Time / FPS:
- For each level (0, 1, 2, 3):
- Set the level and teleport to the complex region.
- Keep the camera static. Record average FPS using the in-viewer stats bar.
- Use a GPU profiler to capture frame data. Analyze the average GPU frame time and identify bottlenecks (e.g., fragment shader cost related to reflection sampling, probe update costs). Compare frame times between levels.
- For each level (0, 1, 2, 3):
-
Functional Verification:
- Set
RenderReflectionProbeLevel
to 0. Verify (visually or using RenderDoc) that only the default probe is sampled in shaders and thatLLReflectionMapManager::update()
logic heavily restricts probe processing. - Set Level to 1. In a region with manual probes, verify these probes become active (
mCubeIndex != -1
) and are sampled correctly. - Set Level to 2. Verify
- Set