mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 02:00:14 +02:00
Add first-person camera logging and adjust object culling defaults for Quest
This commit is contained in:
1 parent
e2c4eb3c8c
commit
75f075b234
7 files changed
+106
-154
No files matched your search
@@ -93,6 +93,7 @@ struct RuntimeUserConfig {
|
||||
// F10 > Diagnostics: OpenXR pacing and presentation logging in console.log.
|
||||
// Off unless set; it is a bug-report aid, not something to leave running.
|
||||
std::optional<bool> diagnosticsOpenXRLogging;
|
||||
std::optional<bool> diagnosticsFirstPersonLogging;
|
||||
std::optional<float> audioVolume;
|
||||
std::optional<float> audioMusicVolume;
|
||||
std::optional<float> audioSoundEffectsVolume;
|
||||
@@ -238,8 +239,17 @@ inline constexpr bool kVrNativeSteeringWheelDefault = true;
|
||||
inline constexpr bool kVrHandSteeringDefault = true;
|
||||
// The game hides karts and objects its own camera cannot see, which a head
|
||||
// turn in VR reveals. object_culling false draws them anyway (see
|
||||
// vr/mkw_vr_culling.h); it only takes effect while VR is enabled.
|
||||
// vr/mkw_vr_culling.h); it only takes effect while VR is enabled. The PC
|
||||
// draws them by default; the Quest keeps the game's culling, since every
|
||||
// extra model costs its GPU twice. The macro is the same default for the
|
||||
// config file written on first launch.
|
||||
#if defined(__ANDROID__)
|
||||
inline constexpr bool kVrObjectCullingDefault = true;
|
||||
#define MKW_VR_OBJECT_CULLING_DEFAULT_TOML "true"
|
||||
#else
|
||||
inline constexpr bool kVrObjectCullingDefault = false;
|
||||
#define MKW_VR_OBJECT_CULLING_DEFAULT_TOML "false"
|
||||
#endif
|
||||
// The cockpit hands follow the headset's hand tracking (the controllers' touch
|
||||
// sensors while they are held, the cameras once they are put down, when bare
|
||||
// hands also drive). Opt-in, and only offered on the Quest for now; the
|
||||
@@ -585,7 +595,7 @@ inline void EnsureConfigFile() {
|
||||
"# The game hides karts and objects its own camera cannot see.\n"
|
||||
"# object_culling = false draws them anyway, so a head turn or a\n"
|
||||
"# look over the shoulder shows them; it costs GPU time.\n"
|
||||
"object_culling = true\n"
|
||||
"object_culling = " MKW_VR_OBJECT_CULLING_DEFAULT_TOML "\n"
|
||||
"# Hand steering (by heurazy): squeeze a grip near the wheel or\n"
|
||||
"# handlebar to take hold of it with the tracked controllers, and\n"
|
||||
"# turn it to steer. Releasing both grips gives steering back to the\n"
|
||||
@@ -881,6 +891,8 @@ inline RuntimeUserConfig ParseConfigDocument(const toml::value& document) {
|
||||
readRangedFloat("wheel_tracking_grace", kVrWheelTrackingGraceMin, kVrWheelTrackingGraceMax);
|
||||
config.vrWheelHaptics = FindConfigValue<bool>(document, "vr", "wheel_haptics");
|
||||
config.diagnosticsOpenXRLogging = FindConfigValue<bool>(document, "diagnostics", "openxr_logging");
|
||||
config.diagnosticsFirstPersonLogging =
|
||||
FindConfigValue<bool>(document, "diagnostics", "first_person_logging");
|
||||
|
||||
auto readVolume = [&](std::string_view key) -> std::optional<float> {
|
||||
auto value = FindConfigFloat(document, "audio", key);
|
||||
@@ -1705,6 +1717,17 @@ inline bool SetDiagnosticsOpenXRLogging(bool value) {
|
||||
return WriteSetting("diagnostics", "openxr_logging", value ? "true" : "false");
|
||||
}
|
||||
|
||||
// The first-person camera's once-a-second anchor, view, pose and cockpit lines
|
||||
// (vr/mkw_vr_first_person.cpp): off unless someone is debugging the camera.
|
||||
inline bool DiagnosticsFirstPersonLogging(bool fallback = false) {
|
||||
return Get().diagnosticsFirstPersonLogging.value_or(fallback);
|
||||
}
|
||||
|
||||
inline bool SetDiagnosticsFirstPersonLogging(bool value) {
|
||||
Mutable().diagnosticsFirstPersonLogging = value;
|
||||
return WriteSetting("diagnostics", "first_person_logging", value ? "true" : "false");
|
||||
}
|
||||
|
||||
inline std::string VrFirstPersonRotation(std::string fallback = kVrFirstPersonRotationDefault) {
|
||||
const auto& value = Get().vrFirstPersonRotation;
|
||||
return value && IsSupportedVrFirstPersonRotation(*value) ? *value : std::move(fallback);
|
||||
|
||||
@@ -184,6 +184,7 @@ int g_vrFrameInterpolationMode = [] {
|
||||
}();
|
||||
int g_vrFirstPersonHiddenModel = RuntimeConfigFile::VrFirstPersonHiddenModel();
|
||||
bool g_openxrDiagnosticsLogging = RuntimeConfigFile::DiagnosticsOpenXRLogging(false);
|
||||
bool g_firstPersonDiagnosticsLogging = RuntimeConfigFile::DiagnosticsFirstPersonLogging(false);
|
||||
// Config spellings and menu labels for the desktop mirror, index-matched to
|
||||
// AuroraStereoMirrorView so the combo selection converts to either directly.
|
||||
constexpr std::array<const char*, 5> kVrMirrorViewNames{"normal", "both", "left", "right", "none"};
|
||||
@@ -1465,7 +1466,7 @@ void DrawVrSettings() {
|
||||
"fully virtual; the immersive window and the Flat Screen race have the room "
|
||||
"around them too. Applies immediately.");
|
||||
}
|
||||
// Shows the live level, which debug.wiicompiled.foveation can override.
|
||||
// Shows the live level Aurora holds.
|
||||
g_vrFoveation = static_cast<int>(aurora_get_stereo_foveation());
|
||||
if (ImGui::Combo("Foveated rendering", &g_vrFoveation, kVrFoveationLabels.data(),
|
||||
static_cast<int>(kVrFoveationLabels.size()))) {
|
||||
@@ -1736,8 +1737,8 @@ void DrawVrCameraSettings() {
|
||||
ImGui::SetTooltip(
|
||||
"The game's own culling: karts, characters and course objects outside its chase "
|
||||
"camera's view are not drawn, so a head turn or a look over the shoulder finds them "
|
||||
"missing. Off draws them anyway, at some GPU cost. The draw distance is unchanged, "
|
||||
"and the Flat screen race view always keeps the game's culling.");
|
||||
"missing. Off draws them anyway, at some GPU cost, and is the PC's default. The draw "
|
||||
"distance is unchanged, and the Flat screen race view always keeps the game's culling.");
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Reset first-person defaults")) {
|
||||
@@ -1949,6 +1950,17 @@ void DrawDiagnosticsSettings() {
|
||||
ImGui::TextDisabled("OpenXR is not running, so nothing is logged until a VR session starts.");
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
if (ImGui::Checkbox("First-person camera logging", &g_firstPersonDiagnosticsLogging)) {
|
||||
RuntimeConfigFile::SetDiagnosticsFirstPersonLogging(g_firstPersonDiagnosticsLogging);
|
||||
mkw::vr::MkwVRFirstPersonApplyConfiguredSettings();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Writes the first-person camera's anchor, view and kart pose, and the cockpit's\n"
|
||||
"wheel and view checks, to console.log once per second during a race.\n"
|
||||
"For reporting a misplaced first-person view. Off by default; it costs a little\n"
|
||||
"game-thread time while on. Applies immediately and is remembered.");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
const bool exporting = g_logExportInProgress.load(std::memory_order_acquire);
|
||||
|
||||
@@ -336,6 +336,8 @@ struct FirstPersonState {
|
||||
float cockpit_units_per_meter = RuntimeConfigFile::kVrCockpitUnitsPerMeterDefault;
|
||||
bool steering_wheel = RuntimeConfigFile::kVrSteeringWheelDefault;
|
||||
bool native_steering_wheel = RuntimeConfigFile::kVrNativeSteeringWheelDefault;
|
||||
// [diagnostics] first_person_logging: the once-a-second camera lines below.
|
||||
bool diagnostic_logging = false;
|
||||
// Read at the race draw boundary, consumed at the seal.
|
||||
struct CockpitLatch {
|
||||
bool valid = false;
|
||||
@@ -1206,6 +1208,9 @@ void FinishNativeWheelFrameLocked() noexcept {
|
||||
}
|
||||
|
||||
void LogCockpitLocked(uint64_t frame, const Mtx34& view_from_world) noexcept {
|
||||
if (!g_state.diagnostic_logging) {
|
||||
return;
|
||||
}
|
||||
if (g_state.logged_frame != 0 && frame - g_state.logged_frame < 60) {
|
||||
return;
|
||||
}
|
||||
@@ -1237,8 +1242,12 @@ void LogCockpitLocked(uint64_t frame, const Mtx34& view_from_world) noexcept {
|
||||
|
||||
void LogAnchorLocked(uint64_t frame, const Mtx34& anchor, const Mtx34& view_from_world,
|
||||
const KartPoseRead& kart, const Mtx34& kart_from_local) noexcept {
|
||||
// One line per second at 60 Hz: enough to confirm the offsets on-device
|
||||
// without drowning the log during a race.
|
||||
// One set of lines per second at 60 Hz when [diagnostics] first_person_logging
|
||||
// asks for them: enough to confirm the offsets on-device. They are written
|
||||
// under the first-person lock, so they stay off otherwise.
|
||||
if (!g_state.diagnostic_logging) {
|
||||
return;
|
||||
}
|
||||
if (g_state.logged_frame != 0 && frame - g_state.logged_frame < 60) {
|
||||
return;
|
||||
}
|
||||
@@ -1356,6 +1365,7 @@ void MkwVRFirstPersonApplyConfiguredSettings() noexcept {
|
||||
g_state.cockpit_units_per_meter = cockpit_units;
|
||||
g_state.steering_wheel = RuntimeConfigFile::VrSteeringWheel();
|
||||
g_state.native_steering_wheel = RuntimeConfigFile::VrNativeSteeringWheel();
|
||||
g_state.diagnostic_logging = RuntimeConfigFile::DiagnosticsFirstPersonLogging();
|
||||
g_state.native_wheel_fallback = false;
|
||||
g_state.native_wheel_unmatched = 0;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#elif defined(__ANDROID__)
|
||||
#include "vr/openxr_android.h"
|
||||
#include "vr/openxr_vulkan.h"
|
||||
#include <sys/system_properties.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#define XR_USE_TIMESPEC
|
||||
@@ -503,7 +502,7 @@ public:
|
||||
// How every eye is replayed, fixed: it ends at the frame's final GXCopyDisp, so it holds the
|
||||
// image the game presented; it keeps the EFB reset after a display copy, which can only be
|
||||
// an earlier one's and erases what that last copy did not show; and it is drawn in one
|
||||
// render pass (the Quest's debug.wiicompiled.eye_passes splits it again, for A/B timing).
|
||||
// render pass.
|
||||
aurora_set_stereo_stop_at_display_copy(true);
|
||||
aurora_set_stereo_skip_copy_clears(false);
|
||||
aurora_set_stereo_single_pass_eyes(true);
|
||||
@@ -938,11 +937,8 @@ private:
|
||||
// The settings panel gets a compositor layer of its own while it is
|
||||
// open, and Aurora leaves it out of the eyes. A backend that could
|
||||
// not make that layer has the panel drawn into the eyes instead.
|
||||
const bool panel_layer = backend_->PanelLayerAvailable() && !PanelLayerForcedOff();
|
||||
const bool panel_layer = backend_->PanelLayerAvailable();
|
||||
aurora_set_stereo_panel_layer(panel_layer);
|
||||
PollEyePassesOverride();
|
||||
PollFoveationOverride();
|
||||
PollWindowEyesOverride();
|
||||
presentation.panel.requested = panel_layer && OpenXRSettingsPanelOpen();
|
||||
|
||||
// Pipeline caches are stored where their stall is least visible: once when a race
|
||||
@@ -1508,11 +1504,7 @@ private:
|
||||
}
|
||||
|
||||
bool WindowShapedEyes() const noexcept {
|
||||
#if defined(__ANDROID__)
|
||||
return kWindowShapedEyesSupported && !window_eyes_forced_off_;
|
||||
#else
|
||||
return kWindowShapedEyesSupported;
|
||||
#endif
|
||||
}
|
||||
|
||||
// The seated frame the controllers are located in for hand steering: the
|
||||
@@ -1624,115 +1616,6 @@ private:
|
||||
return screen;
|
||||
}
|
||||
|
||||
// Android: `adb shell setprop debug.wiicompiled.panel_layer 0` draws the settings panel into
|
||||
// the eyes again, to compare the two ways or to rule out a runtime's quad layers. Read about
|
||||
// once a second.
|
||||
bool PanelLayerForcedOff() noexcept {
|
||||
#if defined(__ANDROID__)
|
||||
if (panel_layer_poll_ == 0) {
|
||||
panel_layer_poll_ = 72;
|
||||
char value[PROP_VALUE_MAX] = {};
|
||||
const bool off =
|
||||
__system_property_get("debug.wiicompiled.panel_layer", value) > 0 && value[0] == '0';
|
||||
if (off != panel_layer_forced_off_) {
|
||||
panel_layer_forced_off_ = off;
|
||||
RT_LOG(RT_TAG_RUNTIME) << "OpenXR: settings panel "
|
||||
<< (off ? "drawn into the eyes" : "on its own layer")
|
||||
<< " (debug.wiicompiled.panel_layer)" << std::endl;
|
||||
}
|
||||
}
|
||||
--panel_layer_poll_;
|
||||
return panel_layer_forced_off_;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Android: `adb shell setprop debug.wiicompiled.eye_passes 0` replays each eye in one render
|
||||
// pass per recorded pass again, and 1 forces the single pass, to compare the two within one
|
||||
// session. An empty value restores the single pass every eye has otherwise. Read about once a
|
||||
// second.
|
||||
void PollEyePassesOverride() noexcept {
|
||||
#if defined(__ANDROID__)
|
||||
if (eye_passes_poll_ != 0) {
|
||||
--eye_passes_poll_;
|
||||
return;
|
||||
}
|
||||
eye_passes_poll_ = 72;
|
||||
char value[PROP_VALUE_MAX] = {};
|
||||
int override_value = -1;
|
||||
if (__system_property_get("debug.wiicompiled.eye_passes", value) > 0 &&
|
||||
(value[0] == '0' || value[0] == '1')) {
|
||||
override_value = value[0] - '0';
|
||||
}
|
||||
if (override_value == eye_passes_override_) {
|
||||
return;
|
||||
}
|
||||
eye_passes_override_ = override_value;
|
||||
const bool single = override_value != 0;
|
||||
aurora_set_stereo_single_pass_eyes(single);
|
||||
RT_LOG(RT_TAG_RUNTIME) << "OpenXR: eyes replayed in "
|
||||
<< (single ? "one render pass" : "one render pass per recorded pass")
|
||||
<< (override_value >= 0 ? " (debug.wiicompiled.eye_passes)"
|
||||
: " (debug.wiicompiled.eye_passes cleared)")
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Android: `adb shell setprop debug.wiicompiled.foveation <0-3>` overrides the foveation level
|
||||
// (off, low, medium, high) within one session, for A/B timing; an empty value hands it back to
|
||||
// the settings. Levels need a session launched with foveation on (see VrFoveation). Read about
|
||||
// once a second.
|
||||
void PollFoveationOverride() noexcept {
|
||||
#if defined(__ANDROID__)
|
||||
if (foveation_poll_ != 0) {
|
||||
--foveation_poll_;
|
||||
return;
|
||||
}
|
||||
foveation_poll_ = 72;
|
||||
char value[PROP_VALUE_MAX] = {};
|
||||
int override_value = -1;
|
||||
if (__system_property_get("debug.wiicompiled.foveation", value) > 0 && value[0] >= '0' &&
|
||||
value[0] <= '3' && value[1] == '\0') {
|
||||
override_value = value[0] - '0';
|
||||
}
|
||||
if (override_value == foveation_override_) {
|
||||
return;
|
||||
}
|
||||
foveation_override_ = override_value;
|
||||
const uint32_t level = override_value >= 0
|
||||
? static_cast<uint32_t>(override_value)
|
||||
: RuntimeConfigFile::VrFoveationLevelIndex(RuntimeConfigFile::VrFoveation());
|
||||
aurora_set_stereo_foveation(level);
|
||||
RT_LOG(RT_TAG_RUNTIME) << "OpenXR: foveation " << RuntimeConfigFile::kVrFoveationLevels[level]
|
||||
<< (override_value >= 0 ? " (debug.wiicompiled.foveation)"
|
||||
: " (debug.wiicompiled.foveation cleared)")
|
||||
<< (aurora_stereo_foveation_available() ? "" : "; unavailable in this session")
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Android: `adb shell setprop debug.wiicompiled.window_eyes 0` renders the immersive window's eyes
|
||||
// whole and only masks them, as the PC does, to compare the cost within one session; an empty
|
||||
// value aims them through the window again. Read about once a second.
|
||||
void PollWindowEyesOverride() noexcept {
|
||||
#if defined(__ANDROID__)
|
||||
if (window_eyes_poll_ != 0) {
|
||||
--window_eyes_poll_;
|
||||
return;
|
||||
}
|
||||
window_eyes_poll_ = 72;
|
||||
char value[PROP_VALUE_MAX] = {};
|
||||
const bool off = __system_property_get("debug.wiicompiled.window_eyes", value) > 0 && value[0] == '0';
|
||||
if (off != window_eyes_forced_off_) {
|
||||
window_eyes_forced_off_ = off;
|
||||
RT_LOG(RT_TAG_RUNTIME) << "OpenXR: immersive window eyes "
|
||||
<< (off ? "rendered whole and masked" : "aimed through the window")
|
||||
<< " (debug.wiicompiled.window_eyes)" << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// The settings panel's layer hangs exactly where its pointer hits are
|
||||
// tested, the rectangle it used to cover in the eyes.
|
||||
static void PlacePanelLayer(OpenXRBackendFrame& frame, const OpenXRPointerScreen& screen) noexcept {
|
||||
@@ -1957,14 +1840,6 @@ private:
|
||||
std::unique_ptr<OpenXRRuntime> runtime_;
|
||||
std::unique_ptr<GraphicsBackend> backend_;
|
||||
#if defined(__ANDROID__)
|
||||
uint32_t panel_layer_poll_ = 0;
|
||||
bool panel_layer_forced_off_ = false;
|
||||
uint32_t eye_passes_poll_ = 0;
|
||||
int eye_passes_override_ = -1;
|
||||
uint32_t foveation_poll_ = 0;
|
||||
int foveation_override_ = -1;
|
||||
uint32_t window_eyes_poll_ = 0;
|
||||
bool window_eyes_forced_off_ = false;
|
||||
#endif
|
||||
std::unique_ptr<OpenXRInput> input_;
|
||||
std::thread pacing_thread_;
|
||||
|
||||
@@ -41,6 +41,20 @@ int main() {
|
||||
Require(!Parse("[vr]\nhand_tracking = 1\n").vrHandTracking.has_value());
|
||||
Require(!RuntimeConfigFile::kVrHandTrackingDefault);
|
||||
|
||||
// [vr] object_culling: false draws what the game camera culls. The PC
|
||||
// defaults to that; the Quest keeps the game's culling.
|
||||
Require(Parse("[vr]\nobject_culling = true\n").vrObjectCulling == true);
|
||||
Require(Parse("[vr]\nobject_culling = false\n").vrObjectCulling == false);
|
||||
Require(!Parse("[vr]\n").vrObjectCulling.has_value());
|
||||
Require(!Parse("[vr]\nobject_culling = 0\n").vrObjectCulling.has_value());
|
||||
#if defined(__ANDROID__)
|
||||
Require(RuntimeConfigFile::kVrObjectCullingDefault);
|
||||
Require(std::string_view(MKW_VR_OBJECT_CULLING_DEFAULT_TOML) == "true");
|
||||
#else
|
||||
Require(!RuntimeConfigFile::kVrObjectCullingDefault);
|
||||
Require(std::string_view(MKW_VR_OBJECT_CULLING_DEFAULT_TOML) == "false");
|
||||
#endif
|
||||
|
||||
// [vr] immersive_window and flat_screen: one race view in two keys, Flat
|
||||
// Screen mode winning, so a file that predates the window reads as before.
|
||||
using RuntimeConfigFile::VrRaceView;
|
||||
|
||||
Reference in new issue
Block a user