mirror of
https://github.com/astromaddie/lambda1-vr.git
synced 2026-10-18 01:00:12 +02:00
Frame: size the eye image from vr_resolution_scale, ask for a refresh rate
The Frame build rendered the runtime's 1728 square recommendation, but the panel is 2160 square. The eye image is now the recommended size times vr_resolution_scale (archived, 1.25 by default, which is 2160), held to what the view (maxImageRect) and the swapchain allow; the engine's screen size is set to match before the swapchains are made. The log has the recommended size, the largest the view and swapchain take, and what was used. The HUD shift and the rest work from the real width, so they follow. vr_refresh_rate (archived, default 90, standard section 9) is asked for with xrRequestDisplayRefreshRateFB: the highest the runtime offers that isn't over it. The offered list, the request and the result are logged. The earlier "now: 90 Hz" at a 13.89 ms frame period was a mismatch in what was read, not asked for; vr_refresh follows the real frame period. Frame build only. Builds, haven't run it on the headset yet.
This commit is contained in:
1 parent
a4f12f84fd
commit
afbc400cfe
5 files changed
+121
-31
No files matched your search
@@ -500,6 +500,8 @@ convar_t *vr_use_gesture_boundary;
|
||||
#ifdef L1VR_STEAM_FRAME
|
||||
convar_t *vr_hud_distance;
|
||||
convar_t *vr_hud_level;
|
||||
convar_t *vr_refresh_rate;
|
||||
convar_t *vr_resolution_scale;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -580,6 +582,8 @@ void VR_Init()
|
||||
}
|
||||
}
|
||||
vr_hud_distance = Cvar_Get( "vr_hud_distance", "1.0", CVAR_ARCHIVE, "How far ahead the HUD sits, in metres" );
|
||||
vr_refresh_rate = Cvar_Get( "vr_refresh_rate", "90", CVAR_ARCHIVE, "The display refresh rate to ask for, the highest the runtime offers that isn't over this" );
|
||||
vr_resolution_scale = Cvar_Get( "vr_resolution_scale", "1.25", CVAR_ARCHIVE, "Eye image size as a multiple of the runtime's recommended one (1.25 is the panel's 2160 on the Steam Frame), 0.5 to 2" );
|
||||
vr_hud_level = Cvar_Get( "vr_hud_level", "1", CVAR_ARCHIVE, "1 keeps the HUD, text, and camera-facing sprites level when the head rolls, 0 lets them roll with the head" );
|
||||
#endif
|
||||
|
||||
@@ -664,6 +668,14 @@ void * AppThreadFunction( void * parm )
|
||||
VR_Init();
|
||||
|
||||
TBXR_EnterVR();
|
||||
#ifdef L1VR_STEAM_FRAME
|
||||
{
|
||||
// the eye image size, from vr_resolution_scale (the engine set up its screen before it was known)
|
||||
extern void R_ChangeDisplaySettingsFast( int width, int height );
|
||||
TBXR_SetEyeScale( vr_resolution_scale->value );
|
||||
R_ChangeDisplaySettingsFast( (int)gAppState.Width, (int)gAppState.Height );
|
||||
}
|
||||
#endif
|
||||
TBXR_InitRenderer();
|
||||
TBXR_InitActions();
|
||||
TBXR_WaitForSessionActive();
|
||||
@@ -687,15 +699,16 @@ void * AppThreadFunction( void * parm )
|
||||
{
|
||||
//The positional movement scaling goes by vr_refresh. SteamVR picks the rate for
|
||||
//the app, so take it from the frame period rather than from what's on offer.
|
||||
static bool refreshSet = false;
|
||||
static int lastRate = 0;
|
||||
const XrDuration period = gAppState.FrameState.predictedDisplayPeriod;
|
||||
if (!refreshSet && period > 0)
|
||||
const int rate = period > 0 ? (int)(1e9 / (double)period + 0.5) : 0;
|
||||
if (rate > 0 && rate != lastRate)
|
||||
{
|
||||
char rate[16];
|
||||
Q_snprintf(rate, sizeof(rate), "%d", (int)(1e9 / (double)period + 0.5));
|
||||
Cvar_Set2("vr_refresh", rate, true);
|
||||
ALOGI("[openxr] frame period %.2f ms, vr_refresh %s", period / 1e6, rate);
|
||||
refreshSet = true;
|
||||
char text[16];
|
||||
Q_snprintf(text, sizeof(text), "%d", rate);
|
||||
Cvar_Set2("vr_refresh", text, true);
|
||||
ALOGI("[openxr] frame period %.2f ms, vr_refresh %s", period / 1e6, text);
|
||||
lastRate = rate;
|
||||
}
|
||||
}
|
||||
if (!TBXR_ShouldRender())
|
||||
|
||||
@@ -1486,6 +1486,91 @@ void ovrTrackedController_Clear(ovrTrackedController* controller) {
|
||||
controller->Pose = XrPosef_Identity();
|
||||
}
|
||||
|
||||
#ifdef L1VR_STEAM_FRAME
|
||||
static uint32_t tbxrMaxSwapchain[2];
|
||||
|
||||
/*
|
||||
The eye image size: what the runtime recommends times a scale, held to what the view and the
|
||||
swapchain can take. The scale is vr_resolution_scale (standard.md section 9). The panel is 2160x2160
|
||||
a eye and the runtime recommends 1728, so 1.25 is native. Returns the scale that was used.
|
||||
*/
|
||||
float TBXR_SetEyeScale(float scale)
|
||||
{
|
||||
const uint32_t recW = gAppState.ViewConfigurationView[0].recommendedImageRectWidth;
|
||||
const uint32_t recH = gAppState.ViewConfigurationView[0].recommendedImageRectHeight;
|
||||
uint32_t maxW = gAppState.ViewConfigurationView[0].maxImageRectWidth;
|
||||
uint32_t maxH = gAppState.ViewConfigurationView[0].maxImageRectHeight;
|
||||
if (tbxrMaxSwapchain[0] && (maxW == 0 || tbxrMaxSwapchain[0] < maxW)) maxW = tbxrMaxSwapchain[0];
|
||||
if (tbxrMaxSwapchain[1] && (maxH == 0 || tbxrMaxSwapchain[1] < maxH)) maxH = tbxrMaxSwapchain[1];
|
||||
|
||||
if (scale < 0.5f) scale = 0.5f;
|
||||
if (scale > 2.0f) scale = 2.0f;
|
||||
scale *= SS_MULTIPLIER; // 1.0 unless -s was given
|
||||
|
||||
uint32_t w = (uint32_t)(recW * scale + 0.5f);
|
||||
uint32_t h = (uint32_t)(recH * scale + 0.5f);
|
||||
if (maxW && w > maxW) w = maxW;
|
||||
if (maxH && h > maxH) h = maxH;
|
||||
w &= ~1u; // even
|
||||
h &= ~1u;
|
||||
|
||||
gAppState.Width = (float)w;
|
||||
gAppState.Height = (float)h;
|
||||
ALOGI("[openxr] eye image: recommended %ux%u, largest the view takes %ux%u (swapchain %ux%u), scale %.2f, using %ux%u",
|
||||
recW, recH,
|
||||
gAppState.ViewConfigurationView[0].maxImageRectWidth, gAppState.ViewConfigurationView[0].maxImageRectHeight,
|
||||
tbxrMaxSwapchain[0], tbxrMaxSwapchain[1], scale, w, h);
|
||||
return scale;
|
||||
}
|
||||
|
||||
/*
|
||||
Ask for the highest refresh rate the runtime offers that isn't over the setting (vr_refresh_rate). SteamVR
|
||||
offers an app only the rate set for it in its own per-app settings, so this is often the only one there is.
|
||||
*/
|
||||
static void TBXR_ChooseRefreshRate(float wanted)
|
||||
{
|
||||
PFN_xrEnumerateDisplayRefreshRatesFB pfnEnumerateRates = NULL;
|
||||
PFN_xrGetDisplayRefreshRateFB pfnGetRate = NULL;
|
||||
PFN_xrRequestDisplayRefreshRateFB pfnRequestRate = NULL;
|
||||
xrGetInstanceProcAddr(gAppState.Instance, "xrEnumerateDisplayRefreshRatesFB", (PFN_xrVoidFunction*)(&pfnEnumerateRates));
|
||||
xrGetInstanceProcAddr(gAppState.Instance, "xrGetDisplayRefreshRateFB", (PFN_xrVoidFunction*)(&pfnGetRate));
|
||||
xrGetInstanceProcAddr(gAppState.Instance, "xrRequestDisplayRefreshRateFB", (PFN_xrVoidFunction*)(&pfnRequestRate));
|
||||
if (pfnEnumerateRates == NULL || pfnGetRate == NULL || pfnRequestRate == NULL)
|
||||
return;
|
||||
|
||||
float rates[16];
|
||||
uint32_t numRates = 0;
|
||||
pfnEnumerateRates(gAppState.Session, 0, &numRates, NULL);
|
||||
if (numRates > 16)
|
||||
numRates = 16;
|
||||
pfnEnumerateRates(gAppState.Session, numRates, &numRates, rates);
|
||||
|
||||
float best = 0.0f, lowest = 0.0f;
|
||||
char list[160] = "";
|
||||
for (uint32_t i = 0; i < numRates; i++)
|
||||
{
|
||||
char one[24];
|
||||
snprintf(one, sizeof(one), "%s%.0f", i ? ", " : "", rates[i]);
|
||||
strncat(list, one, sizeof(list) - strlen(list) - 1);
|
||||
if (rates[i] <= wanted + 0.5f && rates[i] > best) best = rates[i];
|
||||
if (lowest == 0.0f || rates[i] < lowest) lowest = rates[i];
|
||||
}
|
||||
if (best == 0.0f)
|
||||
best = lowest;
|
||||
|
||||
float before = 0.0f;
|
||||
pfnGetRate(gAppState.Session, &before);
|
||||
XrResult result = XR_SUCCESS;
|
||||
if (best > 0.0f && fabsf(best - before) > 0.5f)
|
||||
result = pfnRequestRate(gAppState.Session, best);
|
||||
float after = 0.0f;
|
||||
pfnGetRate(gAppState.Session, &after);
|
||||
ALOGI("[openxr] refresh rate: offered %s Hz, wanted %.0f, asked for %.0f (now %.1f, was %.1f, result %d)",
|
||||
list, wanted, best, after, before, (int)result);
|
||||
gAppState.currentDisplayRefreshRate = after;
|
||||
}
|
||||
#endif
|
||||
|
||||
void TBXR_InitialiseResolution()
|
||||
{
|
||||
// Enumerate the viewport configurations.
|
||||
@@ -1723,30 +1808,10 @@ void TBXR_InitRenderer( ) {
|
||||
}
|
||||
|
||||
#ifdef L1VR_STEAM_FRAME
|
||||
//Just look at the refresh rate, SteamVR only offers the one set for the app
|
||||
//Pick a refresh rate, SteamVR only offers the one set for the app
|
||||
if (TBXR_ExtensionEnabled(XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME))
|
||||
{
|
||||
PFN_xrEnumerateDisplayRefreshRatesFB pfnEnumerateRates = NULL;
|
||||
PFN_xrGetDisplayRefreshRateFB pfnGetRate = NULL;
|
||||
xrGetInstanceProcAddr(gAppState.Instance, "xrEnumerateDisplayRefreshRatesFB", (PFN_xrVoidFunction*)(&pfnEnumerateRates));
|
||||
xrGetInstanceProcAddr(gAppState.Instance, "xrGetDisplayRefreshRateFB", (PFN_xrVoidFunction*)(&pfnGetRate));
|
||||
if (pfnEnumerateRates != NULL && pfnGetRate != NULL)
|
||||
{
|
||||
float rates[16];
|
||||
uint32_t numRates = 0;
|
||||
pfnEnumerateRates(gAppState.Session, 0, &numRates, NULL);
|
||||
if (numRates > 16)
|
||||
{
|
||||
numRates = 16;
|
||||
}
|
||||
pfnEnumerateRates(gAppState.Session, numRates, &numRates, rates);
|
||||
for (uint32_t i = 0; i < numRates; i++)
|
||||
{
|
||||
ALOGI("[openxr] display refresh rate offered: %.1f Hz", rates[i]);
|
||||
}
|
||||
pfnGetRate(gAppState.Session, &gAppState.currentDisplayRefreshRate);
|
||||
ALOGI("[openxr] display refresh rate now: %.1f Hz", gAppState.currentDisplayRefreshRate);
|
||||
}
|
||||
TBXR_ChooseRefreshRate(vr_refresh_rate != NULL ? vr_refresh_rate->value : 90.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1934,6 +1999,8 @@ void TBXR_InitialiseOpenXR()
|
||||
XrSystemProperties systemProperties = {};
|
||||
systemProperties.type = XR_TYPE_SYSTEM_PROPERTIES;
|
||||
OXR(xrGetSystemProperties(gAppState.Instance, gAppState.SystemId, &systemProperties));
|
||||
tbxrMaxSwapchain[0] = systemProperties.graphicsProperties.maxSwapchainImageWidth;
|
||||
tbxrMaxSwapchain[1] = systemProperties.graphicsProperties.maxSwapchainImageHeight;
|
||||
ALOGI("[openxr] system: %s, OpenGL ES %d.%d to %d.%d wanted by the runtime",
|
||||
systemProperties.systemName,
|
||||
XR_VERSION_MAJOR(graphicsRequirements.minApiVersionSupported),
|
||||
|
||||
@@ -372,6 +372,9 @@ void TBXR_RecenterToHead(bool resetHeight);
|
||||
// The menu laser: where the weapon hand's aim ray meets the flat screen, 0..1 across and down. False
|
||||
// when the screen isn't showing or the hand isn't tracked (the ray can also miss the panel's edge).
|
||||
bool TBXR_ScreenPointer(float* u, float* v);
|
||||
|
||||
// The eye image size from the recommended one times a scale, held to the limits. Before the swapchains are made.
|
||||
float TBXR_SetEyeScale(float scale);
|
||||
#endif
|
||||
|
||||
#endif //vrcommon_h
|
||||
@@ -38,4 +38,6 @@ extern convar_t *vr_stereo_side;
|
||||
#ifdef L1VR_STEAM_FRAME
|
||||
extern convar_t *vr_hud_distance;
|
||||
extern convar_t *vr_hud_level;
|
||||
extern convar_t *vr_refresh_rate;
|
||||
extern convar_t *vr_resolution_scale;
|
||||
#endif
|
||||
@@ -140,8 +140,13 @@ says the Frame profile is active.
|
||||
Java loads it as `openxr_loader`.
|
||||
- Only OpenGL ES is required from the runtime. Everything else is enabled if
|
||||
the runtime lists it. There's no Pico or Meta code on that build.
|
||||
- It renders at the runtime's recommended eye size. The Quest build uses 1.3x of
|
||||
it, which is a lot on the Frame.
|
||||
- The eye image is the runtime's recommended size (1728 square) times `vr_resolution_scale` (archived, default 1.25,
|
||||
which is the panel's own 2160 square), held to what the view and the swapchain allow. The Quest build uses 1.3x of the
|
||||
recommended size. The log has the recommended, the largest allowed and the size used.
|
||||
- `vr_refresh_rate` (archived, default 90) is the rate to ask for: the highest the runtime offers that isn't
|
||||
over it, with the offered list, the request and the result in the log. SteamVR only offers an app the rate in
|
||||
its per-app settings in the headset, so that's often the only one on the list. `vr_refresh`, which the game
|
||||
uses to scale walking, follows the real frame period.
|
||||
- When the headset is in standby (`shouldRender` is false) the frame goes in with
|
||||
no layers and the game doesn't simulate. Without that the server kept running.
|
||||
- If SteamVR quits the app the game shuts down the normal way.
|
||||
|
||||
Reference in new issue
Block a user