Vulkan doesn't detect all compatible present modes

I recently started learning Vulkan, and have been using this (vulkan-tutorial[.]com) tutorial to start. I’ve gone all the way through the Vertex Buffer section, and have the validation layers active. I have some flickering issues, and I’ve started troubleshooting which has lead to the question alluded to in the title.

Question:
Why doesn’t vkGetPhysicalDeviceSurfacePresentModesKHR() detect all supported presentation modes as defined by VkPresentModeKHR?

Explanation:
My investigation lead to evaluating what VkPresentModeKHR was being chosen. The code that chooses the present mode is as follows:

VkPresentModeKHR GraphicsManager::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes) {
  //Check for availability of triple buffer
  for (const auto& availablePresentMode : availablePresentModes) {
    if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
      return availablePresentMode;
    }
  }

  //Fallback mode with guaranteed support
  return VK_PRESENT_MODE_FIFO_KHR;
}

To my surprise, I discovered that the fallback VK_PRESENT_MODE_FIFO_KHR was being chosen. Out of curiosity, I inserted return VK_PRESENT_MODE_MAILBOX_KHR; as the first line of the function, to force the VK_PRESENT_MODE_MAILBOX_KHR present mode, and the flickering stopped. I then investigated how availablePresentModes was determined.

My class has the struct:

//Swap chain support details
struct SwapChainSupportDetails {
    VkSurfaceCapabilitiesKHR capabilities;
    std::vector<VkSurfaceFormatKHR> formats;
    std::vector<VkPresentModeKHR> presentModes;
};

I get the SwapChainSupportDetails with a function call:

SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
GraphicsManager::SwapChainSupportDetails GraphicsManager::querySwapChainSupport(VkPhysicalDevice device) {
    SwapChainSupportDetails details;

    //Basic surface capabilities
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);

    //Surface formats
    uint32_t formatCount;
    vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
    if (formatCount != 0) {
        details.formats.resize(formatCount);
        vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
    }

    //Presentation modes
    //!Does not return all compatible present modes
    uint32_t presentModeCount;
    vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
    if (presentModeCount != 0) {
      details.presentModes.resize(presentModeCount);
      vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
    }

    return details;
}

The function call vkGetPhysicalDeviceSurfacePresentModesKHR() fills in details.presentModes with VK_PRESENT_MODE_IMMEDIATE_KHR and VK_PRESENT_MODE_FIFO_KHR.

This explains why my call to chooseSwapPresentMode() chooses the fallback.

VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);

As I stated at the beginning, I can force VK_PRESENT_MODE_MAILBOX_KHR, and I don’t get any errors from the validation layers, the program doesn’t crash, and the flickering stops. Clearly this means that VK_PRESENT_MODE_MAILBOX_KHR is compatible, but Vulkan doesn’t return it as a compatible mode in the function call vkGetPhysicalDeviceSurfacePresentModesKHR(), why?

Specs:
OS: Arch Linux
GPU: NVIDIA GTX 1660 Ti Mobile
Driver: nvidia 535.98-2
Language: C++

My CPU has integrated graphics, but my program only sees the 1660 as compatible, and I have verified that my program is running on the 1660 with the command prime-smi as well as verifying the vendor code of the gpu in my program matches NVIDIA.

I’m not sure what you are hoping for, the spec is quite clear: VkSwapchainCreateInfoKHR.presentMode must be set to a value returned by vkGetPhysicalDeviceSurfacePresentModesKHR (ref).

Your Vulkan implementation (aka your graphics driver) reports that it can only support immediate and fifo modes on the surface. Only the driver devs can know why other modes are not supported (including possible bug in the driver), but the fact that it works in your testing means unfortunately nothing: what you are doing is still invoking undefined behavior and can stop working at any time.
That is a general thing in Vulkan: just because something is working does not imply that it is correct and validation layers are not exhaustive, meaning there are incorrect uses of the API that they do not detect.

You could try asking on the nvidia forums what influences the supported present modes. Also, possibly file an enhancement request bug report against the validation layers to catch the incorrect present mode.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.