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
| Device | Max Resolution | Max FPS | Max Bitrate | Notes |
|---|
| Quest 2 | 1080p HD | 30 | 10 Mbps | Conservative (thermal limits) |
| Quest 3 | 4K UHD | 60 | 20 Mbps | Full quality support |
| Quest Pro | 4K UHD | 60 | 20 Mbps | Full quality support |
| PCVR | 4K UHD | 60 | 20 Mbps | GPU dependent |
Quest 2 Optimization
Quest 2 has thermal and power constraints that limit encoding performance.
Recommended Quest 2 Settings
| Setting | Default | Quest 2 Override | Reason |
|---|
| Max Resolution | 4K | 1080p HD | Thermal limits |
| Framerate | 60 | 30 | CPU overhead |
| Video Bitrate | 20 Mbps | 10 Mbps | Encoding load |
| Audio Bitrate | 320 Kbps | 192 Kbps | Sufficient quality |
| 2K Profile | Enabled | Disabled | Not performant |
| 4K Profile | Enabled | Disabled | Not 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.
Recommended Quest 3/Pro Settings
| Setting | Value | Notes |
|---|
| Max Resolution | 4K (3840×2160) | Full support |
| Framerate | 60 | Smooth encoding |
| Video Bitrate | 20 Mbps | High quality |
| Audio Bitrate | 320 Kbps | Max quality |
| 2K Profile | Enabled | Available option |
| 4K Profile | Enabled | Available 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;
}
}
};
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
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