Skip to main content

What Problem Does This Solve?

Different VR headsets have different performance limits:
  • Quest 2 has thermal/power constraints
  • Quest 3/Pro can handle higher quality
  • PCVR depends on GPU capabilities
Device-specific overrides let you automatically adjust recording settings based on detected hardware, ensuring the best balance between video quality and game performance.

When to Use This

Use device overrides when:
  • Supporting multiple Quest devices
  • Targeting both standalone VR and PCVR
  • Users report performance issues
  • Optimizing for specific platforms
  • Want automatic quality adjustment
Skip this if: You’re only targeting one specific device and have manually tuned settings.

Supported Devices

DeviceMax ResolutionMax FPSMax BitrateNotes
Quest 21080p HD3010 MbpsConservative (thermal limits)
Quest 34K UHD6020 MbpsFull quality support
Quest Pro4K UHD6020 MbpsFull quality support
PCVR4K UHD6020 MbpsGPU dependent

Quest 2 Optimization

Quest 2 has thermal and power constraints that limit encoding performance.
SettingDefaultQuest 2 OverrideReason
Max Resolution4K1080p HDThermal limits
Framerate6030CPU overhead
Video Bitrate20 Mbps10 MbpsEncoding load
Audio Bitrate320 Kbps192 KbpsSufficient quality
2K ProfileEnabledDisabledNot performant
4K ProfileEnabledDisabledNot performant

Apply Quest 2 Overrides

void ADeviceManager::ApplyQuest2Overrides()
{
    ULCKDeveloperSettings* Settings = ULCKDeveloperSettings::Get();
    
    // Override HD profile for Quest 2
    FLCKRecordingProfile& HDProfile = Settings->Profile_HD;
    HDProfile.Width = 1920;
    HDProfile.Height = 1080;
    HDProfile.Framerate = 30;
    HDProfile.VideoBitrate = 10 << 20;  // 10 Mbps
    HDProfile.AudioBitrate = 192000;    // 192 Kbps
    
    // Disable higher quality profiles
    Settings->bEnable2KProfile = false;
    Settings->bEnable4KProfile = false;
    
    UE_LOG(LogLCK, Log, TEXT("Applied Quest 2 optimizations"));
}

Why These Limits?

Thermal management:
  • Quest 2 SoC (Snapdragon XR2 Gen 1) generates heat under load
  • Extended recording → thermal throttling → frame drops
  • Conservative settings prevent overheating
File size:
  • 10 Mbps @ 30fps = ~1.2 GB per hour
  • 20 Mbps @ 60fps = ~5.4 GB per hour (too much for casual recording)
Battery life:
  • Higher quality = faster battery drain
  • 30fps encoding uses ~30% less power than 60fps

Quest 3 / Quest Pro Optimization

Quest 3 and Quest Pro have better cooling and more powerful hardware.
SettingValueNotes
Max Resolution4K (3840×2160)Full support
Framerate60Smooth encoding
Video Bitrate20 MbpsHigh quality
Audio Bitrate320 KbpsMax quality
2K ProfileEnabledAvailable option
4K ProfileEnabledAvailable option

Apply Quest 3 Overrides

void ADeviceManager::ApplyQuest3Overrides()
{
    ULCKDeveloperSettings* Settings = ULCKDeveloperSettings::Get();
    
    // Enable all quality profiles
    Settings->bEnable2KProfile = true;
    Settings->bEnable4KProfile = true;
    
    // Configure 4K profile
    FLCKRecordingProfile& UHDProfile = Settings->Profile_UHD;
    UHDProfile.Width = 3840;
    UHDProfile.Height = 2160;
    UHDProfile.Framerate = 60;
    UHDProfile.VideoBitrate = 20 << 20;  // 20 Mbps
    UHDProfile.AudioBitrate = 320000;    // 320 Kbps
    
    UE_LOG(LogLCK, Log, TEXT("Applied Quest 3 optimizations"));
}

Automatic Device Detection

Detect the device at runtime and apply appropriate settings:
UCLASS()
class ADeviceManager : public AActor
{
    GENERATED_BODY()
    
public:
    void BeginPlay() override
    {
        Super::BeginPlay();
        ApplyDeviceOverrides();
    }
    
private:
    void ApplyDeviceOverrides()
    {
        FString DeviceModel = GetDeviceModel();
        
        UE_LOG(LogLCK, Log, TEXT("Detected device: %s"), *DeviceModel);
        
        if (DeviceModel.Contains(TEXT("Quest 2")))
        {
            ApplyQuest2Overrides();
        }
        else if (DeviceModel.Contains(TEXT("Quest 3")))
        {
            ApplyQuest3Overrides();
        }
        else if (DeviceModel.Contains(TEXT("Quest Pro")))
        {
            ApplyQuestProOverrides();
        }
        else
        {
            // PCVR or unknown device - use default settings
            UE_LOG(LogLCK, Log, TEXT("Using default settings for PCVR/unknown device"));
        }
    }
    
    FString GetDeviceModel()
    {
#if PLATFORM_ANDROID
        // Get Android device model
        return FAndroidMisc::GetDeviceModel();
#else
        // Get VR headset name via XR system
        if (GEngine && GEngine->XRSystem.IsValid())
        {
            FString DeviceName = GEngine->XRSystem->GetSystemName().ToString();
            return DeviceName;
        }
        return TEXT("Unknown");
#endif
    }
    
    void ApplyQuest2Overrides() { /* See above */ }
    void ApplyQuest3Overrides() { /* See above */ }
    void ApplyQuestProOverrides() { /* Same as Quest 3 */ }
};

Custom Override Configuration

Using Config Files

Create platform-specific config files: Config/Android_Quest2/DefaultGame.ini:
[/Script/LCKCore.LCKDeveloperSettings]
+Profile_HD=(Width=1920,Height=1080,Framerate=30,VideoBitrate=10000000,AudioBitrate=192000)
bEnable2KProfile=false
bEnable4KProfile=false
Config/Android_Quest3/DefaultGame.ini:
[/Script/LCKCore.LCKDeveloperSettings]
+Profile_UHD=(Width=3840,Height=2160,Framerate=60,VideoBitrate=20000000,AudioBitrate=320000)
bEnable2KProfile=true
bEnable4KProfile=true

Runtime Configuration System

USTRUCT(BlueprintType)
struct FLCKDeviceOverride
{
    GENERATED_BODY()
    
    UPROPERTY(EditAnywhere, Category = "Device Override")
    FString DeviceModelPattern;
    
    UPROPERTY(EditAnywhere, Category = "Device Override")
    ELCKVideoQuality MaxQuality;
    
    UPROPERTY(EditAnywhere, Category = "Device Override")
    int32 MaxFramerate;
    
    UPROPERTY(EditAnywhere, Category = "Device Override")
    int32 VideoBitrate;
    
    UPROPERTY(EditAnywhere, Category = "Device Override")
    int32 AudioBitrate;
};

UCLASS(Config=Game, DefaultConfig)
class ULCKDeviceSettings : public UDeveloperSettings
{
    GENERATED_BODY()
    
public:
    UPROPERTY(Config, EditAnywhere, Category = "Device Overrides")
    TArray<FLCKDeviceOverride> DeviceOverrides;
    
    UFUNCTION(BlueprintCallable)
    void ApplyOverrideForDevice(const FString& DeviceModel)
    {
        for (const FLCKDeviceOverride& Override : DeviceOverrides)
        {
            if (DeviceModel.Contains(Override.DeviceModelPattern))
            {
                ApplyOverride(Override);
                return;
            }
        }
    }
    
private:
    void ApplyOverride(const FLCKDeviceOverride& Override)
    {
        ULCKDeveloperSettings* Settings = ULCKDeveloperSettings::Get();
        
        // Apply override settings
        FLCKRecordingProfile Profile;
        Profile.Framerate = Override.MaxFramerate;
        Profile.VideoBitrate = Override.VideoBitrate;
        Profile.AudioBitrate = Override.AudioBitrate;
        
        // Set based on quality
        switch (Override.MaxQuality)
        {
            case ELCKVideoQuality::HD:
                Settings->Profile_HD = Profile;
                Settings->bEnable2KProfile = false;
                Settings->bEnable4KProfile = false;
                break;
            case ELCKVideoQuality::TWO_K:
                Settings->Profile_2K = Profile;
                Settings->bEnable4KProfile = false;
                break;
            case ELCKVideoQuality::FOUR_K:
                Settings->Profile_UHD = Profile;
                break;
        }
    }
};

Performance Monitoring

Monitor encoding performance to validate your overrides:
UCLASS()
class ULCKPerformanceMonitor : public UActorComponent
{
    GENERATED_BODY()
    
public:
    virtual void TickComponent(float DeltaTime, ELevelTick TickType, 
                              FActorComponentTickFunction* ThisTickFunction) override
    {
        Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
        
        if (!Service || !Service->IsRecording())
            return;
        
        // Track frame times
        float FrameTimeMs = DeltaTime * 1000.0f;
        FrameTimeHistory.Add(FrameTimeMs);
        
        // Keep last 2 seconds
        if (FrameTimeHistory.Num() > 60)
        {
            FrameTimeHistory.RemoveAt(0);
        }
        
        // Calculate average frame time
        float AvgFrameTime = 0.0f;
        for (float Time : FrameTimeHistory)
        {
            AvgFrameTime += Time;
        }
        AvgFrameTime /= FrameTimeHistory.Num();
        
        // Check for performance issues
        float TargetFrameTime = 1000.0f / 30.0f; // 33.3ms for 30fps
        if (AvgFrameTime > TargetFrameTime * 1.2f) // 20% over target
        {
            OnPerformanceWarning();
        }
    }
    
private:
    UPROPERTY()
    ULCKService* Service;
    
    TArray<float> FrameTimeHistory;
    
    void OnPerformanceWarning()
    {
        UE_LOG(LogLCK, Warning, 
            TEXT("Recording impacting performance - consider lower quality"));
        
        // Notify UI
        if (OnPerformanceIssue.IsBound())
        {
            OnPerformanceIssue.Broadcast(
                TEXT("Recording may be impacting game performance. "
                     "Consider using a lower quality profile.")
            );
        }
    }
    
public:
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPerformanceIssue, FString, Message);
    
    UPROPERTY(BlueprintAssignable)
    FOnPerformanceIssue OnPerformanceIssue;
};

Thermal Management (Android)

On Android, monitor thermal state to prevent throttling:
#if PLATFORM_ANDROID

UCLASS()
class ULCKThermalMonitor : public UActorComponent
{
    GENERATED_BODY()
    
public:
    virtual void BeginPlay() override
    {
        Super::BeginPlay();
        
        // Check thermal state every 10 seconds
        GetWorld()->GetTimerManager().SetTimer(
            ThermalCheckTimer,
            this,
            &ULCKThermalMonitor::CheckThermalState,
            10.0f,
            true
        );
    }
    
private:
    FTimerHandle ThermalCheckTimer;
    UPROPERTY()
    ULCKService* Service;
    
    void CheckThermalState()
    {
        // Android thermal API
        int32 ThermalStatus = FAndroidMisc::GetThermalStatus();
        
        switch (ThermalStatus)
        {
            case 0: // THERMAL_STATUS_NONE
            case 1: // THERMAL_STATUS_LIGHT
                // Normal operation
                break;
                
            case 2: // THERMAL_STATUS_MODERATE
                UE_LOG(LogLCK, Warning, TEXT("Device warming up"));
                OnThermalWarning();
                break;
                
            case 3: // THERMAL_STATUS_SEVERE
            case 4: // THERMAL_STATUS_CRITICAL
                UE_LOG(LogLCK, Error, TEXT("Thermal throttling detected"));
                ReduceQualityForThermal();
                break;
        }
    }
    
    void OnThermalWarning()
    {
        // Notify user
        if (OnThermalAlert.IsBound())
        {
            OnThermalAlert.Broadcast(
                TEXT("Device temperature rising. Recording quality may be reduced.")
            );
        }
    }
    
    void ReduceQualityForThermal()
    {
        if (!Service)
        {
            Service = GetLCKService();
        }
        
        if (Service && !Service->IsRecording())
        {
            return;
        }
        
        // Get current quality
        ELCKVideoQuality CurrentQuality = Service->GetCurrentQuality();
        
        // Drop one quality level
        ELCKVideoQuality NewQuality = CurrentQuality;
        
        switch (CurrentQuality)
        {
            case ELCKVideoQuality::FOUR_K:
                NewQuality = ELCKVideoQuality::TWO_K;
                break;
            case ELCKVideoQuality::TWO_K:
                NewQuality = ELCKVideoQuality::HD;
                break;
            case ELCKVideoQuality::HD:
                NewQuality = ELCKVideoQuality::SD;
                break;
            case ELCKVideoQuality::SD:
                // Already at lowest - stop recording
                Service->StopRecording();
                UE_LOG(LogLCK, Error, TEXT("Stopped recording due to thermal limits"));
                return;
        }
        
        // Stop current recording
        Service->StopRecording();
        
        // Apply new quality
        Service->SetQualityProfile(NewQuality);
        
        UE_LOG(LogLCK, Warning, 
            TEXT("Reduced quality due to thermal throttling: %d -> %d"), 
            (int32)CurrentQuality, (int32)NewQuality);
        
        // Notify user
        if (OnThermalAlert.IsBound())
        {
            OnThermalAlert.Broadcast(
                FString::Printf(TEXT("Quality reduced to %s due to device temperature."), 
                    *UEnum::GetValueAsString(NewQuality))
            );
        }
    }
    
public:
    DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnThermalAlert, FString, Message);
    
    UPROPERTY(BlueprintAssignable)
    FOnThermalAlert OnThermalAlert;
};

#endif // PLATFORM_ANDROID

User-Selectable Performance Presets

Let users choose their own balance:
UENUM(BlueprintType)
enum class ELCKPerformancePreset : uint8
{
    Quality      UMETA(DisplayName = "Quality"),      // Highest quality, may impact performance
    Balanced     UMETA(DisplayName = "Balanced"),     // Default, good quality with minimal impact
    Performance  UMETA(DisplayName = "Performance")   // Lower quality, prioritize game FPS
};

UCLASS()
class ULCKPerformancePresetManager : public UObject
{
    GENERATED_BODY()
    
public:
    UFUNCTION(BlueprintCallable)
    void ApplyPreset(ELCKPerformancePreset Preset)
    {
        ULCKDeveloperSettings* Settings = ULCKDeveloperSettings::Get();
        FString DeviceModel = GetDeviceModel();
        
        if (DeviceModel.Contains(TEXT("Quest 2")))
        {
            ApplyQuest2Preset(Preset);
        }
        else if (DeviceModel.Contains(TEXT("Quest 3")))
        {
            ApplyQuest3Preset(Preset);
        }
    }
    
private:
    void ApplyQuest2Preset(ELCKPerformancePreset Preset)
    {
        ULCKDeveloperSettings* Settings = ULCKDeveloperSettings::Get();
        FLCKRecordingProfile& HDProfile = Settings->Profile_HD;
        
        switch (Preset)
        {
            case ELCKPerformancePreset::Quality:
                // 1080p @ 30fps, 12 Mbps
                HDProfile.Framerate = 30;
                HDProfile.VideoBitrate = 12 << 20;
                break;
                
            case ELCKPerformancePreset::Balanced:
                // 1080p @ 30fps, 8 Mbps (default)
                HDProfile.Framerate = 30;
                HDProfile.VideoBitrate = 8 << 20;
                break;
                
            case ELCKPerformancePreset::Performance:
                // 720p @ 30fps, 4 Mbps
                HDProfile.Width = 1280;
                HDProfile.Height = 720;
                HDProfile.Framerate = 30;
                HDProfile.VideoBitrate = 4 << 20;
                break;
        }
    }
};

Best Practices

1. Test on Target Devices

Always test recording performance on actual target devices, not in editor or emulator.
  • Quest 2 has different thermal characteristics than Quest 3
  • PCVR performance depends heavily on GPU
  • Test with your game’s most demanding scenes
  • Monitor frame times during extended recordings

2. Provide User Options

// Settings menu
void ASettingsMenu::BuildQualityOptions()
{
    FString DeviceModel = GetDeviceModel();
    
    if (DeviceModel.Contains(TEXT("Quest 2")))
    {
        // Quest 2: Offer SD and HD only
        AddQualityOption(ELCKVideoQuality::SD, TEXT("SD (720p)"), 
            TEXT("Best performance"));
        AddQualityOption(ELCKVideoQuality::HD, TEXT("HD (1080p)"), 
            TEXT("Recommended"));
    }
    else if (DeviceModel.Contains(TEXT("Quest 3")))
    {
        // Quest 3: Offer all options
        AddQualityOption(ELCKVideoQuality::HD, TEXT("HD (1080p)"), 
            TEXT("Recommended"));
        AddQualityOption(ELCKVideoQuality::TWO_K, TEXT("2K (1440p)"), 
            TEXT("High quality"));
        AddQualityOption(ELCKVideoQuality::FOUR_K, TEXT("4K (2160p)"), 
            TEXT("Maximum quality"));
    }
}

3. Document Device Support

Clearly communicate what works best: In-game tooltips:
FString GetQualityTooltip(ELCKVideoQuality Quality)
{
    if (IsQuest2())
    {
        switch (Quality)
        {
            case ELCKVideoQuality::HD:
                return TEXT("Recommended for Quest 2. Best balance of quality and performance.");
            case ELCKVideoQuality::SD:
                return TEXT("Lower quality, but ensures smooth gameplay.");
        }
    }
    return TEXT("");
}

Key Takeaways

Quest 2: HD @ 30fps max — thermal/power limits
Quest 3/Pro: Up to 4K @ 60fps — better cooling and hardware
Auto-detect device — apply appropriate overrides at runtime
Monitor performance — track frame times during recording
Watch thermal state (Android) — reduce quality if overheating
Offer user presets — Quality, Balanced, Performance
Test on target devices — editor performance ≠ device performance