Develop
Develop
Select your platform

Creating and Using Swapchains

Swapchains, which are queues of images to be displayed to the user, are required for graphics pipelining. This topic covers the key considerations for using swapchains in OpenXR Native development.

Enumerating Swapchain Formats

To enumerate the available swapchain texture formats, you must use the xrEnumerateSwapchainFormats function. On Quest devices, these texture formats include OpenGL internal formats like GL_RGB10_A2 (10 bits for Red, 10 bits for Green, 10 bits for Blue, 2 bits for Alpha) for OpenGL-based graphics, and Vulkan formats like VK_FORMAT_B8G8R8A8_SRGB (8 bits for Red, 8 bits for Green, 8 bits for Blue, 8 bits for Alpha).
The Khronos Group’s hello_xr sample app uses xrEnumerateSwapchainFormats to count and store swapchain formats as follows:
// Select a swapchain format.
uint32_t swapchainFormatCount;
CHECK_XRCMD(xrEnumerateSwapchainFormats(m_session, 0, &swapchainFormatCount, nullptr));
std::vector<int64_t> swapchainFormats(swapchainFormatCount);
CHECK_XRCMD(xrEnumerateSwapchainFormats(m_session, (uint32_t)swapchainFormats.size(), &swapchainFormatCount, swapchainFormats.data()));
The m_session is an XrSession handle that is initially defined as:
XrSession m_session{XR_NULL_HANDLE};
Swapchain formats are stored in a swapchainFormats vector (implementation-specific). Successful calls to xrEnumerateSwapchainFormats return:
  • XR_SUCCESS
  • XR_SESSION_LOSS_PENDING
The XR_SESSION_LOSS_PENDING result indicates that the function temporarily simulates success because the session will soon be lost. This is returned only for an unspecified period, after which the runtime may return XR_ERROR_SESSION_LOST, when the function actually fails and the session is lost, making the XrSession handle and all its child handles unusable. To free resources, destroy the XrSession handle immediately.
Error codes include:
  • XR_ERROR_VALIDATION_FAILURE
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_SESSION_LOST
  • XR_ERROR_SIZE_INSUFFICIENT
For more information on these error codes, refer to the Error Codes section in the OpenXR specification.

Creating Swapchains

A valid session is required to create swapchains. Create a swapchain using the xrCreateSwapchain function. The hello_xr app creates swapchains as follows:
The app invokes xrGetSystemProperties to receive information about the system the app runs on, such as graphics and tracking properties.
XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES};
CHECK_XRCMD(xrGetSystemProperties(m_instance, m_systemId, &systemProperties));
In the following code snippet, m_configViews is a vector of XrViewConfigurationView. The xrEnumerateViewConfigurationViews function enumerates system view configurations.
uint32_t viewCount;
CHECK_XRCMD(xrEnumerateViewConfigurationViews(m_instance, m_systemId, m_viewConfigType, 0, &viewCount, nullptr));
m_configViews.resize(viewCount, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
CHECK_XRCMD(xrEnumerateViewConfigurationViews(m_instance, m_systemId, m_viewConfigType, viewCount, &viewCount, m_configViews.data()));
The app creates a swapchain for each view by looping through all views:
// Create a swapchain for each view.
for (uint32_t i = 0; i < viewCount; i++) {
    const XrViewConfigurationView& vp = m_configViews[i];
    ...
}
Inside the loop, the app creates each swapchain by using an XrSwapchainCreateInfo struct for info such as width, height, face count (that is, the number of faces, meaning either six textures for a swapchain per view for cubemaps, or one), and by calling xrCreateSwapchain:
    // Create the swapchain.
    XrSwapchainCreateInfo swapchainCreateInfo{XR_TYPE_SWAPCHAIN_CREATE_INFO};
    swapchainCreateInfo.arraySize = 1;
    swapchainCreateInfo.format = m_colorSwapchainFormat;
    swapchainCreateInfo.width = vp.recommendedImageRectWidth;
    swapchainCreateInfo.height = vp.recommendedImageRectHeight;
    swapchainCreateInfo.mipCount = 1;
    swapchainCreateInfo.faceCount = 1;
    swapchainCreateInfo.sampleCount = m_graphicsPlugin->GetSupportedSwapchainSampleCount(vp);
    swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
    Swapchain swapchain;
    swapchain.width = swapchainCreateInfo.width;
    swapchain.height = swapchainCreateInfo.height;
    CHECK_XRCMD(xrCreateSwapchain(m_session, &swapchainCreateInfo, &swapchain.handle));

    m_swapchains.push_back(swapchain);
The m_swapchains is a vector containing swapchain handles, widths, and heights. XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT is a flag meaning the image might be a color rendering target and m_colorSwapchainFormat contains the selected swapchain format, initially set to -1 (implementation-specific detail):
int64_t m_colorSwapchainFormat{-1};
Calling xrEnumerateSwapchainImages returns the number of allocated images, and calling it again fills an array of graphics API-specific XrSwapchainImage structs.
    uint32_t imageCount;
    CHECK_XRCMD(xrEnumerateSwapchainImages(swapchain.handle, 0, &imageCount, nullptr));
    std::vector<XrSwapchainImageBaseHeader*> swapchainImages =
        m_graphicsPlugin->AllocateSwapchainImageStructs(imageCount, swapchainCreateInfo);
    CHECK_XRCMD(xrEnumerateSwapchainImages(swapchain.handle, imageCount, &imageCount, swapchainImages[0]));

    m_swapchainImages.insert(std::make_pair(swapchain.handle, std::move(swapchainImages)));
The XrSwapchainImage struct overrides XrSwapchainImageBaseHeader. The function AllocateSwapchainImageStructs is implementation-specific. This function allocates the buffer, initializes it, and returns an array of pointers to each swapchain image struct.

Render View to Parts of Swapchain Images

For each view, there is a separate swapchain (implementation-specific).
const Swapchain viewSwapchain = m_swapchains[i];
Each swapchain is acquired using an XrSwapchainImageAcquireInfo struct, which is set to the enum value XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO.
XrSwapchainImageAcquireInfo acquireInfo{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
Calling xrAcquireSwapchainImage acquires the image in the array position returned by xrEnumerateSwapchainImages.
uint32_t swapchainImageIndex;
CHECK_XRCMD(xrAcquireSwapchainImage(viewSwapchain.handle, &acquireInfo, &swapchainImageIndex));
An XrSwapchainImageWaitInfo struct describes the “waiting” process for a swapchain image to be read by the compositor. Apps must wait for the compositor to finish reading from any image. By calling the xrWaitSwapchainImage function, apps wait for the oldest acquired swapchain image, and then they must release it before waiting on the next acquired one.
XrSwapchainImageWaitInfo waitInfo{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
waitInfo.timeout = XR_INFINITE_DURATION;
CHECK_XRCMD(xrWaitSwapchainImage(viewSwapchain.handle, &waitInfo));
The XrCompositionLayerProjection struct represents projected images rendered per eye and the XrCompositionLayerProjectionView struct contains info such as Field of View, location, or orientation of a projection element. This information populates a projectionLayerViews vector (implementation-specific detail).
projectionLayerViews[i] = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW};
projectionLayerViews[i].pose = m_views[i].pose;
projectionLayerViews[i].fov = m_views[i].fov;
projectionLayerViews[i].subImage.swapchain = viewSwapchain.handle;
projectionLayerViews[i].subImage.imageRect.offset = {0, 0};
projectionLayerViews[i].subImage.imageRect.extent = {viewSwapchain.width, viewSwapchain.height};
Based on the graphics plugin, cube primitive images render (implementation-specific detail) at a certain position and with a given width and height.
const XrSwapchainImageBaseHeader* const swapchainImage = m_swapchainImages[viewSwapchain.handle][swapchainImageIndex];
m_graphicsPlugin->RenderView(projectionLayerViews[i], swapchainImage, m_colorSwapchainFormat, cubes);
The app then releases the swapchain image through xrReleaseSwapchainImage.
XrSwapchainImageReleaseInfo releaseInfo{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
CHECK_XRCMD(xrReleaseSwapchainImage(viewSwapchain.handle, &releaseInfo));
All code snippets in this document belong to hello_xr sample app, which is developed by The Khronos Group Inc. and licensed under the Apache License, Version 2.0.
Did you find this page helpful?