Added support for VR Frame Interpolation

This commit is contained in:
iChris4 committed 2026-09-10 21:33:34 +02:00
1 parent c8eaa52727
commit 6467c6390c
24 files changed
+1030 -132

No files matched your search

+16 -9
View File
@@ -20,6 +20,7 @@
#include <vector>
#include <toml.hpp>
#include "platform/host_platform.h"
#include "vr/frame_interpolation_pacing.h"
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
@@ -57,7 +58,7 @@ struct RuntimeUserConfig {
std::optional<bool> vrStopAtDisplayCopy;
std::optional<bool> vrSkipCopyClears;
std::optional<std::string> vrMirrorView;
std::optional<bool> vrEagerFrameHeartbeat;
std::optional<uint32_t> vrFrameInterpolationFps;
std::optional<bool> vrFirstPerson;
std::optional<float> vrFirstPersonUnitsPerMeter;
std::optional<float> vrFirstPersonHeadUpMeters;
@@ -380,8 +381,8 @@ inline void EnsureConfigFile() {
"# \"right\" mirror the headset's eyes, and \"none\" blacks the window\n"
"# out. Changeable live from the F10 menu.\n"
"mirror_view = \"normal\"\n"
"# Repeat at headset cadence (true), or only during stalls (false). Live.\n"
"eager_frame_heartbeat = false\n"
"# VR interpolation: 0 = Off, 1 = Auto, or 72/90/120 FPS. Live.\n"
"frame_interpolation_fps = 0\n"
"render_scale = 1.0\n"
"world_units_per_meter = 500.0\n"
"hud_distance_meters = 2.0\n"
@@ -642,7 +643,12 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
value && IsSupportedVrMirrorView(*value)) {
config.vrMirrorView = *value;
}
config.vrEagerFrameHeartbeat = FindConfigValue<bool>(document, "vr", "eager_frame_heartbeat");
config.vrFrameInterpolationFps = FindConfigValue<uint32_t>(document, "vr", "frame_interpolation_fps");
if (!config.vrFrameInterpolationFps) {
// Migrate the initial experimental checkbox to Auto.
if (const auto legacy = FindConfigValue<bool>(document, "vr", "frame_interpolation"))
config.vrFrameInterpolationFps = *legacy ? 1u : 0u;
}
if (auto value = FindConfigInt(document, "vr", "first_person_hidden_model");
value && *value >= -1 && *value <= 31) {
config.vrFirstPersonHiddenModel = static_cast<int32_t>(*value);
@@ -944,9 +950,10 @@ inline bool SetVrMirrorView(std::string value) {
return WriteSetting("vr", "mirror_view", FormatString(value));
}
inline bool SetVrEagerFrameHeartbeat(bool value) {
Mutable().vrEagerFrameHeartbeat = value;
return WriteSetting("vr", "eager_frame_heartbeat", value ? "true" : "false");
inline bool SetVrFrameInterpolationFps(uint32_t value) {
value = mkw::vr::NormalizeFrameInterpolationFps(value);
Mutable().vrFrameInterpolationFps = value;
return WriteSetting("vr", "frame_interpolation_fps", std::to_string(value));
}
inline bool SetVrFirstPersonRotation(std::string value) {
@@ -1281,8 +1288,8 @@ inline std::string VrMirrorView(std::string fallback = kVrMirrorViewDefault) {
return value && IsSupportedVrMirrorView(*value) ? *value : std::move(fallback);
}
inline bool VrEagerFrameHeartbeat() {
return Get().vrEagerFrameHeartbeat.value_or(false);
inline uint32_t VrFrameInterpolationFps() {
return mkw::vr::NormalizeFrameInterpolationFps(Get().vrFrameInterpolationFps.value_or(0));
}
inline std::string VrFirstPersonRotation(std::string fallback = kVrFirstPersonRotationDefault) {
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <algorithm>
#include <cstdint>
namespace mkw::vr {
inline uint32_t NormalizeFrameInterpolationFps(uint32_t value) noexcept {
return value == 1 || value == 72 || value == 90 || value == 120 ? value : 0;
}
// A render-rate ceiling on the compositor's own display-time grid. Auto (1)
// renders every tick. Fixed targets cannot increase the physical refresh rate.
class FrameInterpolationPacing {
public:
bool ShouldRender(int64_t display_time, uint32_t target) noexcept {
if (target != target_ || display_time <= last_time_) {
next_time_ = 0;
target_ = target;
}
last_time_ = display_time;
if (target == 0 || target == 1) {
next_time_ = 0;
return true;
}
if (next_time_ != 0 && display_time + 1'000 < next_time_) return false;
const int64_t interval = 1'000'000'000 / target;
if (next_time_ == 0 || display_time - next_time_ > interval * 2) {
next_time_ = display_time + interval;
} else {
next_time_ += interval;
}
return true;
}
void Reset() noexcept { *this = {}; }
private:
int64_t next_time_ = 0;
int64_t last_time_ = 0;
uint32_t target_ = 0;
};
} // namespace mkw::vr
+9 -3
View File
@@ -53,8 +53,14 @@ void OpenXRRequestRecenter() noexcept;
// once per published frame.
void OpenXRSetLeanBackDegrees(float degrees) noexcept;
// Live pacing choice. Disabled: submit fresh frames promptly and repeat during
// stalls. Enabled: repeat at each headset display deadline while waiting.
void OpenXRSetEagerFrameHeartbeat(bool enabled) noexcept;
// Live scene interpolation at the headset's own display deadlines.
// 0 = Off, 1 = Auto, otherwise 72/90/120 as a rendering-rate ceiling.
void OpenXRSetFrameInterpolationFps(uint32_t target) noexcept;
bool OpenXRFrameInterpolationAvailable() noexcept;
struct OpenXRFrameTiming {
float headset_hz = 0;
float rendered_fps = 0; // Newly rendered pairs; excludes retained-layer repeats.
};
OpenXRFrameTiming OpenXRGetFrameTiming() noexcept;
} // namespace mkw::vr