RenderDynamicLOD - ApertureViewer/Aperture-Viewer GitHub Wiki

Technical Deep Dive: RenderDynamicLOD

1. Function and Purpose

The RenderDynamicLOD setting serves as a master switch to enable or disable the viewer's dynamic Level of Detail (LOD) system specifically for mesh-based objects (including sculpties, which are treated as meshes internally, and potentially terrain surface patches).

  • When Enabled (true): The viewer actively adjusts the geometric complexity (number of triangles/vertices) used to render an object based primarily on its distance from the camera and its apparent size on screen. Objects farther away or appearing smaller are rendered with simpler geometry, while closer or larger-appearing objects use more detailed geometry. This is the standard LOD behavior designed to optimize performance.
  • When Disabled (false): The viewer bypasses the distance-based calculation for determining the LOD level for these objects. Instead, the LOD level seems to be determined by a simpler calculation based on the object's intrinsic properties (like its radius/size) and the global LOD factor (RenderVolumeLODFactor, controlled by the "Objects & Sculpts LOD" slider in Preferences), effectively ignoring the camera's distance. The code specifically sets the calculated distance metric (mVisInfo.mDistance) to 0.f in llsurfacepatch.cpp when this setting is false, strongly suggesting objects are treated as if they are very close for LOD calculation purposes within that system.

In essence, RenderDynamicLOD = true prioritizes performance by simplifying distant objects, while RenderDynamicLOD = false prioritizes visual fidelity by attempting to render objects at a higher, more consistent detail level regardless of how far away they are (up to the draw distance limit).

2. Code Utilization

The RenderDynamicLOD setting directly controls the boolean static variable LLPipeline::sDynamicLOD.

  • Initialization:

    • llviewercontrol.cpp: A listener (handleRenderDynamicLODChanged) is set up for the RenderDynamicLOD setting. When the setting changes, this handler updates LLPipeline::sDynamicLOD.
    • pipeline.cpp: LLPipeline::sDynamicLOD is initialized by reading the value of RenderDynamicLOD from gSavedSettings.
  • Core Logic Implementation: The LLPipeline::sDynamicLOD flag is checked in critical functions responsible for determining object detail:

    • llsurfacepatch.cpp (LLSurfacePatch::updateCameraDistanceRegion):
      • This function updates information about a terrain patch's visibility, including its distance relevant for LOD.
      • if (LLPipeline::sDynamicLOD): Calculates mVisInfo.mDistance based on the actual camera position relative to the patch center and radius, scaled by the global LOD factor (LLVOSurfacePatch::sLODFactor).
      • else: Sets mVisInfo.mDistance = 0.f;, effectively overriding the distance calculation and likely forcing a high LOD state for the patch.
      // In LLSurfacePatch::updateCameraDistanceRegion
      if (LLPipeline::sDynamicLOD)
      {
          // ... calculates mVisInfo.mDistance based on actual distance ...
      }
      else
      {
          mVisInfo.mDistance = 0.f; // Treats patch as if it's very close
      }
      
    • llvovolume.cpp (LLVOVolume::computeLODDetail):
      • This function calculates the specific LOD level (e.g., 0, 1, 2, 3) for mesh/sculpty objects.
      • if (LLPipeline::sDynamicLOD): Calculates the detail level (cur_detail) based on the object's apparent size on screen (using distance, radius, lod_factor, and LLVolumeLODGroup::getDetailFromTan). This considers how much screen space the object occupies.
      • else: Calculates cur_detail using a simpler formula (sqrtf(radius)*lod_factor*4.f) that primarily depends on the object's size (radius) and the global lod_factor, crucially ignoring the actual distance.
      // In LLVOVolume::computeLODDetail
      S32 cur_detail;
      if (LLPipeline::sDynamicLOD)
      {
          // ... calculates cur_detail based on apparent size (tan_angle) ...
      }
      else
      {
          // ... calculates cur_detail ignoring distance, using radius/lod_factor ...
          cur_detail = llclamp((S32) (sqrtf(radius)*lod_factor*4.f), 0, 3);
      }
      return cur_detail;
      

3. Performance Impact

  • RenderDynamicLOD = true (Enabled - Default):
    • Generally Higher Performance: Significantly reduces the number of triangles/vertices the GPU needs to process for objects far from the camera.
    • Impact: Leads to higher FPS, lower GPU load, and potentially lower CPU load (less data preparation) and VRAM usage (simpler models might require less memory). The performance gain is most noticeable in scenes with many complex objects at varying distances.
  • RenderDynamicLOD = false (Disabled):
    • Generally Lower Performance: Forces the rendering of objects at a higher detail level irrespective of their distance.
    • Impact: Leads to lower FPS, higher GPU load (more geometry processing), potentially higher CPU load, and VRAM usage, especially in geometrically dense scenes. The performance hit increases as more distant objects are brought into view.

4. Visual Impact

  • RenderDynamicLOD = true (Enabled - Default):
    • Visible LOD Switching: Objects will noticeably change shape and decrease in complexity as the camera moves away from them. Conversely, they gain detail as the camera approaches. This can sometimes result in a "popping" effect as discrete LOD levels switch.
    • Appearance: Distant objects appear simpler, potentially blockier or less detailed compared to their appearance up close.
  • RenderDynamicLOD = false (Disabled):
    • Consistent Detail: Objects maintain a more uniform level of detail regardless of camera distance (up to the draw distance). Distant objects will appear significantly more complex and detailed than they would with dynamic LOD enabled.
    • Appearance: The scene can look sharper and more detailed overall, particularly at mid-to-long distances. However, the maximum detail is still capped by the object's highest LOD model and influenced by the global "Objects & Sculpts LOD" setting (RenderVolumeLODFactor). The key visual difference is the absence of detail reduction based on distance.

5. Default Value Analysis

  • The default value is true (1).
  • This default is appropriate for the vast majority of users as it prioritizes performance, which is crucial for a smooth experience in complex virtual environments like Second Life. Standard LOD behavior is expected in real-time 3D applications.

6. Recommended Value (High-End Hardware)

  • For Maximum Visual Fidelity: Set RenderDynamicLOD to false (0). This forces the viewer to render objects with more detail at greater distances, reducing visual simplification/popping.
    • Requirement: This should be combined with setting the "Objects & Sculpts LOD" slider (RenderVolumeLODFactor) in Preferences > Graphics > Advanced Settings to its maximum value (typically 4.0 or higher, depending on the viewer) to ensure the base detail level used in the non-dynamic calculation is also high.
    • Trade-off: Expect a noticeable decrease in performance (FPS).
  • For Maximum Performance: Keep RenderDynamicLOD at its default value of true (1). Adjust the "Objects & Sculpts LOD" slider (RenderVolumeLODFactor) to balance performance and the minimum detail level you find acceptable.

Testing Procedures

User Testing

  1. Objective: Visually observe LOD switching behavior and perceived performance differences.
  2. Location: Choose a region with a high density of complex mesh objects (e.g., stores with detailed merchandise, elaborate builds) visible at a range of distances (near, mid, far).
  3. Prerequisites: Enable the FPS counter (Ctrl+Shift+1 or via viewer menus). Note your typical FPS in the chosen location.
  4. Steps: a. Ensure RenderDynamicLOD is set to true (Default). Access via Debug Settings. b. Set "Objects & Sculpts LOD" (Preferences > Graphics > Advanced) to a moderate value (e.g., 2.0). c. Position your camera far from a group of complex objects. Slowly move towards them, observing their shapes. Note if/when they visibly change complexity ("pop"). Note the FPS. d. Move away again, observing the simplification. e. Set RenderDynamicLOD to false in Debug Settings. f. Repeat steps c and d. Compare the visual behavior. Do distant objects look more detailed now? Is the "popping" reduced or eliminated? Note the FPS – has it decreased? g. (Optional) Repeat steps c-f with "Objects & Sculpts LOD" set to maximum (e.g., 4.0 or higher) to see the combined effect for maximum fidelity vs maximum performance LOD.
  5. Expected Observations:
    • true: Visible simplification of distant objects, potential "popping" on approach/retreat, generally higher FPS.
    • false: Distant objects remain more detailed, less/no distance-based popping, generally lower FPS.

Developer Testing

  1. Objective: Quantify performance impact and verify code execution paths.
  2. Tools: Viewer debug build, Graphics Debugger (e.g., RenderDoc, NVIDIA Nsight, AMD RGP), CPU Profiler (if needed), viewer's Statistics Bar (Ctrl+Shift+1).
  3. Location: Same as User Testing location.
  4. Steps: a. Set RenderDynamicLOD = true. Set a fixed camera position viewing objects at various distances. b. Capture a frame using the graphics debugger or record performance metrics using built-in/external tools. c. Measure/Observe: * Total scene triangle/vertex count submitted to the GPU. * GPU frame time. * Draw calls related to LLVOVolume / LLSurfacePatch. * (Optional) VRAM usage for geometry buffers. * (Optional) Set breakpoints/log points in LLSurfacePatch::updateCameraDistanceRegion and LLVOVolume::computeLODDetail to confirm the if (LLPipeline::sDynamicLOD) block is executed and observe the calculated mVisInfo.mDistance and cur_detail. d. Set RenderDynamicLOD = false. Maintain the exact same camera position and view. e. Capture/record again. f. Measure/Observe: Same metrics as step c. Compare values between the true and false states. g. (Optional) Verify via breakpoints/logging that the else blocks are now executed and observe mVisInfo.mDistance (should be 0) and the distance-ignoring cur_detail calculation.
  5. Expected Results:
    • The false state should exhibit significantly higher triangle/vertex counts compared to the true state for the same view.
    • GPU frame time is likely to be higher in the false state.
    • Code execution paths should match the setting's state as verified by debugging/logging.