xrCreateSession returns XR_ERROR_VALIDATION_FAILED error

I am starting integration of OpenXR with my Vulkan-based engine. When I call xrCreateSession this error is returned.

The Vulkan instance and other resources are created by the engine’s default initialization code, not using the OpenXR Vulkan commands, but they work fine outside of OpenXR. I’m using this with a Vive in SteamVR.

How do I debug this further to determine the exact cause?

If you aren’t already, you should use the validation api layer https://github.com/KhronosGroup/OpenXR-SDK-Source/tree/main/src/api_layers

For windows there are builds included in openxr_loader_windows-1.0.31.zip that can be used with environment variables set for the OpenXR application:

XR_API_LAYER_PATH=C:/path/to/openxr_loader_windows-1.0.31/openxr_loader_windows/x64/bin/api_layers
XR_ENABLE_API_LAYERS=XR_APILAYER_LUNARG_core_validation
XR_CORE_VALIDATION_EXPORT_TYPE=text

The application now prints this:

Core validation output type: text, first time = true

The validation layer does not provide any additional information, and xrCreateSession returns XR_ERROR_VALIDATION_FAILED.

This is my initialization code:

bool HMD::InitializeOpenXR(const VkInstance vkinstance, const VkPhysicalDevice vkphysicaldevice, const VkDevice vkdevice, const uint32_t queueFamilyIndex, const uint32_t queueIndex)
{
    if (vrinitialized) return true;

    XrResult r;

    std::vector<const char*> extensions;
    const std::vector<std::string> platformExtensions = {};// { "XR_EXT_debug_utils" };
    const std::vector<std::string> graphicsExtensions = { "XR_KHR_vulkan_enable" };
    std::vector<const char*> layers;
#ifdef _DEBUG
    std::string corevalidation = "XR_APILAYER_LUNARG_core_validation";
    layers.push_back(corevalidation.data());
#endif
    for (const auto& ext : platformExtensions)
    {
        extensions.push_back(ext.c_str());
    }
    for (const auto& ext : graphicsExtensions)
    {
        extensions.push_back(ext.c_str());
    }

    XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
    createInfo.next = { nullptr };
    createInfo.enabledExtensionCount = (uint32_t)extensions.size();
    createInfo.enabledExtensionNames = extensions.data();
    createInfo.enabledApiLayerCount = (uint32_t)layers.size();
    createInfo.enabledApiLayerNames = layers.data();
    strcpy(createInfo.applicationInfo.applicationName, "Ultra Engine");
    createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
    
    if (m_instance == XR_NULL_HANDLE)
    {
        r = xrCreateInstance(&createInfo, &m_instance);
        if (r != XR_SUCCESS)
        {
            Print("Error: Failed to create OpenXR instance (" + String(r) + ")");
            return false;
        }
    }

    if (m_systemId == XR_NULL_SYSTEM_ID)
    {
        XrSystemGetInfo systemInfo{};
        systemInfo.type = XR_TYPE_SYSTEM_GET_INFO;
        systemInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
        r = xrGetSystem(m_instance, &systemInfo, &m_systemId);
        if (r != XR_SUCCESS)
        {
            Print("Error: Failed to get OpenXR system (" + String(r) + ")");
            return false;
        }
    }

    //----------------------------------------------------------------------------
    // Get the blend mode to use
    //----------------------------------------------------------------------------
    std::set<XrEnvironmentBlendMode> m_acceptableBlendModes{
        XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
        XR_ENVIRONMENT_BLEND_MODE_ADDITIVE,
        XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND
    };

    uint32_t count;
    xrEnumerateEnvironmentBlendModes(m_instance, m_systemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &count, nullptr);
    XrEnvironmentBlendMode bmode = XrEnvironmentBlendMode(0);
    if (count > 0)
    {
        std::vector<XrEnvironmentBlendMode> blendModes(count);
        xrEnumerateEnvironmentBlendModes(m_instance, m_systemId, XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count, blendModes.data());            
        for (const auto& blendMode : blendModes)
        {
            if (m_acceptableBlendModes.count(blendMode))
            {
                bmode = blendMode;
                break;
            }
        }
    }
    if (bmode == 0)
    {
        Print("Error: Could not locate XR environment blend mode.");
        return false;
    }

    //----------------------------------------------------------------------------
    // Initialize Device
    //----------------------------------------------------------------------------

    PFN_xrGetVulkanGraphicsRequirementsKHR pfnGetVulkanGraphicsRequirementsKHR = nullptr;
    r = xrGetInstanceProcAddr(m_instance, "xrGetVulkanGraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsRequirementsKHR));
    if (r != XR_SUCCESS)
    {
        Print("Error: xrGetInstanceProcAddr)");
        return false;
    }

    XrGraphicsRequirementsVulkanKHR graphicsRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR };
    r = pfnGetVulkanGraphicsRequirementsKHR(m_instance, m_systemId, &graphicsRequirements);
    if (r != XR_SUCCESS)
    {
        Print("Error: pfnGetVulkanGraphicsRequirementsKHR failed (" + String(r) + ")");
        return false;
    }

    //------------------------------------------------------------------------
    // Initialize session
    //------------------------------------------------------------------------

    XrGraphicsBindingVulkanKHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR };
    m_graphicsBinding.instance = vkinstance;
    m_graphicsBinding.physicalDevice = vkphysicaldevice;
    m_graphicsBinding.device = vkdevice;
    m_graphicsBinding.queueFamilyIndex = queueFamilyIndex;
    m_graphicsBinding.queueIndex = queueIndex;
    m_graphicsBinding.next = nullptr;

    XrSession m_session = XR_NULL_HANDLE;
    XrSessionCreateInfo screateInfo{ XR_TYPE_SESSION_CREATE_INFO };
    screateInfo.next = &m_graphicsBinding;
    screateInfo.systemId = m_systemId;
    r = xrCreateSession(m_instance, &screateInfo, &m_session);
    if (r != XR_SUCCESS)
    {
        //Print("Error: xrCreateSession failed.");
        return false;
    }
    
    //----------------------------------------------------------------------------
    //
    //----------------------------------------------------------------------------

    vrinitialized = true;
    return true;
}

I think it is necessary to call the XR functions to create the Vulkan instance, get the physical device, and create the logical device…

I got as far as being able to create the OpenXR-compatible Vulkan instance now. Validation layer is not very complete and misses almost every error I had.

That’s disappointing to hear but now that you mention it I think I remember noticing too that it lacks a lot of checks for the vulkan initialization. If you want to help, you can file issues for missing validation at Issues · KhronosGroup/OpenXR-SDK-Source · GitHub