Graphics

Human eye resolution

To take advantage of Varjo’s human eye resolution displays, use the XR_VARJO_quad_views extension described in the official OpenXR documentation. This allows you to have two viewports per eye like in the Analytics window in Varjo Base.

Like all extensions in OpenXR, XR_VARJO_quad_views must be enabled first. If the extension is not enabled, the runtime will not return XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO as a view configuration and will return an error when the application tries to pass it to functions.

When an application enumerates view configurations, Varjo OpenXR runtime will return XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO first:

// xrEnumerateViewConfigurations
uint32_t viewConfigTypeCount;
XRCHECK(xrEnumerateViewConfigurations(instance, systemId, 0, &viewConfigTypeCount, nullptr));
std::vector<XrViewConfigurationType> viewConfigTypes(viewConfigTypeCount);
XRCHECK(xrEnumerateViewConfigurations(instance, systemId, viewConfigTypeCount, &viewConfigTypeCount, viewConfigTypes.data()));

XrViewConfigurationType viewConfigType = viewConfigTypes[0];  // will contain XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO

If the application does not hard code 2 as a view count, it should work without any further modifications.

For example, to find out how many views there are in a given view configuration, do the following:

uint32_t viewCount;
XRCHECK(xrEnumerateViewConfigurationViews(instance, systemId, viewConfigType, 0, &viewCount, nullptr));
std::vector<XrViewConfigurationView> configViews(viewCount, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
XRCHECK(xrEnumerateViewConfigurationViews(instance, systemId, viewConfigType, viewCount, &viewCount, configViews.data()));

viewCount will contain 4 and configViews will contain descriptions for all views. The first two views are context, which has a lower dpi, while the third and fourth views are focus, which has a high dpi.

Foveated rendering

With the high-resolution displays in the Varjo headsets, an application would need to render frames with full resolution 90 frames per second to achieve the best possible image quality. This kind of performance is very difficult to achieve.

To compensate for this, an application could lower the texture resolution at the cost of image quality.

A better solution is to use foveated rendering to track the user’s gaze and render content in the highest resolution only where the user is looking. Varjo has designed an OpenXR extension that makes it easy to implement foveated rendering. See the official specification for an example and a detailed explanation of the main concepts related to foveated rendering.

OpenXR is a new and evolving standard. Please check the official OpenXR specifications for any recent changes.

OpenXR wait frame

When profiling your application you will may see xrWaitFrame taking a long time perform. This is often indication application being too heavy to render and results in visible stuttering in the headset.

The call is part of the OpenXR frame pacing model. Its purpose is to:

  • Synchronize the application with the runtime and compositor timing.
  • Ensure frames are produced at the correct cadence.
  • Throttle the application, so it does not run ahead of the runtime.

In practice, this means:

  • If the application or GPU work in the previous frame takes too long, the runtime may delay returning from xrWaitFrame.
  • This can appear as the main thread waiting in xrWaitFrame, but typically it reflects that the system is trying to maintain frame timing rather than being blocked by the call itself.

So when you see long xrWaitFrame, it is often an indication that the GPU or another part of the pipeline is missing the frame budget or the runtime is compensating to maintain stable presentation timing