Skip to main content

VR Performance

This guide covers optimization strategies for running VROcean on VR headsets, with specific focus on Meta Quest 3 standalone and PC VR platforms.

Performance Targets

PlatformTarget Frame RateFrame Budget
Quest 3 (72 Hz)72 fps13.9 ms
Quest 3 (90 Hz)90 fps11.1 ms
Quest 3 (120 Hz)120 fps8.3 ms
PC VR (90 Hz)90 fps11.1 ms

VROcean should consume no more than 30-40% of your frame budget to leave room for game logic, physics, and other rendering.

PlatformVROcean Budget
Quest 3 (72 Hz)4-5 ms
Quest 3 (90 Hz)3-4 ms
PC VR3-4 ms

Quick Settings Reference

OceanSimulator:
Resolution: 64x64

SurfaceLodRenderer:
Quality: Low
Ocean Size: 512 - 1024

Ocean Material:
VR Mobile Mode: On
Use Screen Effects: Off
LOD Distance: 30
Detail Fade Distance: 15

WaveConfig:
Small Wave Suppression: 0.3 - 0.4

SceneSystem:
Reflection Update Interval: 15 - 30 seconds

ReflectionProbe:
Resolution: 64 - 128
OceanSimulator:
Resolution: 128x128

SurfaceLodRenderer:
Quality: Medium or High
Ocean Size: 1024 - 2048

Ocean Material:
VR Mobile Mode: Off
Use Screen Effects: Optional
LOD Distance: 50
Detail Fade Distance: 30

WaveConfig:
Small Wave Suppression: 0.1 - 0.2

SceneSystem:
Reflection Update Interval: 3 - 5 seconds

ReflectionProbe:
Resolution: 128 - 256

Simulation Optimization

Resolution Selection

The FFT resolution has the largest impact on CPU performance.

ResolutionCPU Time (Quest 3)Visual QualityRecommendation
64x64~0.3 msGoodQuest standalone
128x128~0.8 msHighQuest with headroom, PC VR
256x256~2.5 msVery highPC VR only
512x512~8 msMaximumNot recommended for VR
// Platform-specific resolution
#if UNITY_ANDROID
simulator.Resolution = OceanResolution._64x64;
#else
simulator.Resolution = OceanResolution._128x128;
#endif

Wave Pattern Size

Larger pattern sizes can hide low resolution:

// Use larger pattern to mask low resolution detail
waveConfig.WavePatternSize = 96f; // Instead of 64

This increases the world-space size of waves, making individual texels less noticeable.

Rendering Optimization

Quality Presets

PresetVertices/FrameGPU ImpactUse Case
Low~8,000MinimalQuest standalone
Medium~30,000LowQuest with headroom
High~120,000ModeratePC VR mid-range
Ultra~200,000HighPC VR high-end
// Dynamic quality based on performance
void AdjustQuality()
{
float fps = 1f / Time.smoothDeltaTime;

if (fps < 68f) // Below Quest 72Hz target
{
renderer.Quality = QualityLevel.Low;
}
else if (fps > 85f)
{
renderer.Quality = QualityLevel.Medium;
}
}

Ocean Size

Smaller ocean areas require fewer LOD levels:

Ocean SizeLOD LevelsTraversal Cost
2562Very low
5123Low
10244Moderate
20485Higher
4096+6+High

For confined VR experiences (boat deck, small island), use smaller ocean sizes:

// Small play area
renderer.OceanSize = 512f;

// Open ocean exploration
renderer.OceanSize = 1024f;

LOD Distance

Reduce detail fade distance for faster LOD falloff:

// Material property
material.SetFloat("_LODDistance", 30f); // Default 50
material.SetFloat("_DetailFadeDistance", 15f); // Default 30

Shader Optimization

VR Mobile Mode

Enable VR_MOBILE_MODE in the ocean material for significant GPU savings:

Features disabled by VR Mobile Mode:

  • Detail normal mapping
  • Subsurface scattering
  • Advanced reflections
  • Foam texture sampling

Features retained:

  • FFT displacement
  • Basic normals and lighting
  • Fresnel reflections
  • Foam (simplified)
  • Fog
// Enable via code (if material has the property)
material.EnableKeyword("VR_MOBILE_MODE");

Screen Effects

Disable Use Screen Effects unless essential:

material.DisableKeyword("USE_SCREEN_EFFECTS");

Screen effects require:

  • Depth texture sampling
  • Opaque texture sampling
  • Additional shader complexity

The visual benefit (depth-based color, refraction) is often not worth the cost in VR.

Shader LOD

The ocean shader includes a fallback SubShader (LOD 50) for very low-end devices:

// Main SubShader - LOD 100
SubShader
{
LOD 100
// Full featured shader
}

// Fallback SubShader - LOD 50
SubShader
{
LOD 50
// Minimal shader: basic lighting + fresnel only
}

Force the fallback via Quality Settings or Shader LOD API if needed.

Reflection Probe Optimization

Update Frequency

Reflection probe updates are expensive. Reduce frequency for VR:

// Quest standalone
SceneSystem.Instance.SetReflectionUpdateInterval(20f);

// PC VR
SceneSystem.Instance.SetReflectionUpdateInterval(5f);

Probe Resolution

ResolutionCostQuality
64LowAcceptable for mobile VR
128ModerateGood balance
256HighPC VR only

Disable During Intensity

Pause probe updates during performance-critical moments:

public class PerformanceManager : MonoBehaviour
{
public void OnIntenseGameplayStart()
{
SceneSystem.Instance.EnableProbeUpdates = false;
}

public void OnIntenseGameplayEnd()
{
SceneSystem.Instance.EnableProbeUpdates = true;
SceneSystem.Instance.ForceReflectionUpdateNextFrame();
}
}

Buoyancy Optimization

Component Selection

ComponentCPU CostUse Case
NonPhysicBasedObjectFloatationLowestVisual-only objects
PhysicBasedObjectFloatationLowFew physics objects
ThreadedPhysicBasedObjectFloatationLowest per-objectMany physics objects

For Quest with multiple floating objects, always use the threaded variant:

// Prefer threaded for VR
gameObject.AddComponent<ThreadedPhysicBasedObjectFloatation>();

Sample Count

Reduce buoyancy sample resolution:

// ThreadedPhysicBasedObjectFloatation
floatation.gridResolution = 0; // Minimum: (2*0+1)² = 1 sample
floatation.gridResolution = 1; // Default: (2*1+1)² = 9 samples

For small objects, a single center sample is often sufficient.

Object Pooling

Avoid instantiating/destroying floating objects at runtime:

public class FloatingObjectPool : MonoBehaviour
{
private Queue<GameObject> pool = new Queue<GameObject>();

public GameObject GetFloatingObject()
{
if (pool.Count > 0)
{
var obj = pool.Dequeue();
obj.SetActive(true);
return obj;
}
return CreateNewFloatingObject();
}

public void ReturnToPool(GameObject obj)
{
obj.SetActive(false);
pool.Enqueue(obj);
}
}

VR Comfort Optimization

Performance and comfort are related. Dropped frames cause discomfort.

Small Wave Suppression

Higher values reduce high-frequency detail that can cause eye strain:

waveConfig.SmallWaveSuppression = 0.35f; // Comfortable for most users

Time Scale

Slower waves are more comfortable:

waveConfig.TimeScale = 0.8f; // 80% speed

Choppiness

Reduce choppiness for calmer motion:

waveConfig.Choppiness = 0.6f; // Less sharp peaks

Profiling

Unity Profiler Markers

VROcean includes profiler markers:

MarkerLocationMeasures
LOD TraversalSurfaceLodRendererQuadtree traversal
Patch SubmissionSurfaceLodRendererMatrix building
(Job markers)OceanSimulatorFFT job execution

Key Metrics to Watch

MetricTarget (Quest 3)Location
Ocean jobs< 1 msProfiler > Jobs
LOD traversal< 0.3 msProfiler > Scripts
Draw calls< 30Frame Debugger
Triangle count< 50kFrame Debugger

Performance Testing Checklist

  1. Test on target hardware, not editor
  2. Build with IL2CPP (not Mono)
  3. Enable Burst compilation
  4. Test at target frame rate (72/90/120 Hz)
  5. Test with representative scene content
  6. Monitor thermal throttling on Quest

Platform-Specific Tips

Quest 3

void ConfigureForQuest()
{
// Simulation
simulator.Resolution = OceanResolution._64x64;

// Rendering
renderer.Quality = QualityLevel.Low;
renderer.OceanSize = 512f;

// Material
oceanMaterial.EnableKeyword("VR_MOBILE_MODE");
oceanMaterial.DisableKeyword("USE_SCREEN_EFFECTS");

// Reflections
SceneSystem.Instance.SetReflectionUpdateInterval(20f);

// Comfort
profile.WaveConfig.SmallWaveSuppression = 0.35f;
profile.WaveConfig.TimeScale = 0.9f;
}

Quest 2

Quest 2 has less headroom than Quest 3:

void ConfigureForQuest2()
{
simulator.Resolution = OceanResolution._64x64;
renderer.Quality = QualityLevel.Low;
renderer.OceanSize = 256f; // Smaller than Quest 3

// Consider disabling reflection updates entirely
SceneSystem.Instance.EnableProbeUpdates = false;
}

PC VR (Mid-Range)

void ConfigureForPCVR()
{
simulator.Resolution = OceanResolution._128x128;
renderer.Quality = QualityLevel.Medium;
renderer.OceanSize = 1024f;

// Can afford more features
oceanMaterial.DisableKeyword("VR_MOBILE_MODE");

SceneSystem.Instance.SetReflectionUpdateInterval(5f);
}

PC VR (High-End)

void ConfigureForHighEndPCVR()
{
simulator.Resolution = OceanResolution._256x256;
renderer.Quality = QualityLevel.High;
renderer.OceanSize = 2048f;

// Full features
oceanMaterial.DisableKeyword("VR_MOBILE_MODE");
oceanMaterial.EnableKeyword("USE_SCREEN_EFFECTS");

SceneSystem.Instance.SetReflectionUpdateInterval(2f);
}

Common Performance Issues

High CPU Usage

Symptom: CPU frame time exceeds budget

Solutions:

  • Reduce simulation resolution
  • Reduce buoyancy sample counts
  • Use threaded buoyancy component
  • Reduce ocean size (fewer LOD levels)

High GPU Usage

Symptom: GPU frame time exceeds budget

Solutions:

  • Enable VR Mobile Mode
  • Disable screen effects
  • Reduce quality preset
  • Lower reflection probe resolution

Frame Spikes

Symptom: Periodic hitches

Solutions:

  • Avoid rapid profile/wind changes (spectrum regeneration)
  • Increase reflection update interval
  • Use time-sliced probe rendering
  • Pool floating objects

Thermal Throttling

Symptom: Performance degrades over time on Quest

Solutions:

  • Reduce all quality settings
  • Add rest periods for the GPU
  • Lower target frame rate to 72 Hz

Next Steps