Files
PodeCaradoxandClaude Opus 5.5 9bd10a2d81 Steam Frame: native Linux arm64 build
- Cross-build against the Steam Runtime 4 arm64 SDK (build_linux.sh,
  cmake/steamrt4-arm64.cmake): the executable sits at 0x98000000 next to
  the game's 32-bit window, with the OpenXR loader and jsoncpp beside it
  (RPATH $ORIGIN); build_linux.sh also packs a release archive.
- OpenXR on SteamVR: EGL surfaceless through XR_MNDX_egl_enable, the
  Frame controller profile (both names), a flick or a wrist turn as the
  Wii Remote's shake, the D-pad as look; the refresh rate goes to 120 Hz
  through vrcmd where the runtime offers only its current rate.
- Timewarp in the app: SteamVR shows frames that arrive in time as they
  were rendered, so at 120 Hz every game frame stood still for two
  refreshes while the head turned.  The game frame's eyes now go into a
  scene set, and every refresh composites the last finished set turned
  to its own head pose (timewarp.h, checked by tools/timewarp_test and
  by the simulator's PETARI_XRSIM_WARP); sharpening runs once per game
  frame; the pacing changes only after a new display period has held.
- Fixes found on the way: libstdc++'s <cstdio>/<cwchar> against the MSL
  renames (compat.h), language archives a disc carries damaged (cook.py
  takes another language's), host allocations on the DVD and DSP
  threads, quitting without waiting on the game's threads, case of two
  include names.
- ANLEITUNG-STEAM-FRAME.md: the player's guide in German.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 17:26:28 +02:00

78 lines
3.4 KiB
C++

// Host-side check for the composite's timewarp (platform/src/xr/timewarp.h):
// c++ -O1 -o timewarp_test tools/timewarp_test/timewarp_test.cpp && ./timewarp_test
// For eyes turned against each other, every pixel of the eye image shown is
// sent to the place in the rendered picture where the same direction in the
// room was drawn; the same eye gives the identity.
#include "../../platform/src/xr/timewarp.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
namespace {
xm::Quat axisAngle(xm::Vec3 axis, float deg) {
float h = deg * 0.5f * 3.14159265f / 180.0f;
return {axis.x * sinf(h), axis.y * sinf(h), axis.z * sinf(h), cosf(h)};
}
xm::Quat mul(xm::Quat a, xm::Quat b) {
return {a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z};
}
// Where the room direction `world` is drawn in the picture of an eye looking
// `q` with field of view `f` (uv, y up); false when it is behind the eye.
bool project(const xm::Quat& q, const vr::FovTan& f, xm::Vec3 world, float* u, float* v) {
xm::Vec3 e = xm::rotate(xm::conj(q), world);
if (e.z >= 0.0f) return false;
*u = (e.x / -e.z - f.left) / (f.right - f.left);
*v = (e.y / -e.z - f.down) / (f.up - f.down);
return true;
}
int failures = 0;
void check(const char* name, const xm::Quat& from, const vr::FovTan& ff, const xm::Quat& to, const vr::FovTan& tf) {
float m[9];
vr::warpMatrix(from, ff, to, tf, m);
float worst = 0.0f;
for (int iy = 0; iy <= 8; iy++) {
for (int ix = 0; ix <= 8; ix++) {
float u = ix / 8.0f, v = iy / 8.0f;
// The room direction of the shown pixel...
xm::Vec3 dir = xm::rotate(to, {tf.left + u * (tf.right - tf.left), tf.down + v * (tf.up - tf.down), -1.0f});
float eu, ev;
if (!project(from, ff, dir, &eu, &ev)) continue;
// ...against the matrix's answer.
float h0 = m[0] * u + m[3] * v + m[6], h1 = m[1] * u + m[4] * v + m[7], h2 = m[2] * u + m[5] * v + m[8];
float err = fmaxf(fabsf(h0 / h2 - eu), fabsf(h1 / h2 - ev));
worst = fmaxf(worst, err);
}
}
bool ok = worst < 1e-4f;
printf("%-34s worst uv error %.2e %s\n", name, worst, ok ? "ok" : "FAILED");
failures += !ok;
}
} // namespace
int main() {
// A Quest 3 / Steam Frame like eye: wider outwards and downwards.
const vr::FovTan fov{tanf(-0.94f), tanf(0.80f), tanf(0.84f), tanf(-0.98f)};
const xm::Quat head = mul(axisAngle({0, 1, 0}, 30.0f), axisAngle({1, 0, 0}, -20.0f));
float m[9];
if (vr::warpMatrix(head, fov, head, fov, m) || m[0] != 1.0f || m[4] != 1.0f || m[8] != 1.0f || m[1] != 0.0f) {
printf("same eye: not the identity FAILED\n");
failures++;
} else {
printf("%-34s identity ok\n", "same eye");
}
check("yaw 2 deg (a refresh at 240 deg/s)", head, fov, mul(axisAngle({0, 1, 0}, 2.0f), head), fov);
check("yaw -4, pitch 3", head, fov, mul(axisAngle({1, 0, 0}, 3.0f), mul(axisAngle({0, 1, 0}, -4.0f), head)), fov);
check("roll 5", head, fov, mul(head, axisAngle({0, 0, 1}, 5.0f)), fov);
vr::FovTan wider{tanf(-1.0f), tanf(0.9f), tanf(0.9f), tanf(-1.0f)};
check("yaw 3, another field of view", head, fov, mul(axisAngle({0, 1, 0}, 3.0f), head), wider);
return failures ? 1 : 0;
}