Vulkan rendering APIs

Updated: April 19, 2023

Screen supports Khronos's Vulkan rendering APIs. Vulkan is a low-overhead, cross-platform, open industry standard API for 3D graphics and computing. It enables developers to target a wide range of devices using the same graphics APIs.

Vulkan standards are published by Khronos Group.

Typically, hardware vendors have their own implementation of the Khronos standards that takes advantage of hardware acceleration and, in particular, the GPU hardware. In order to provide a common interface to Screen applications, the Khronos rendering API calls are redirected onto appropriate vendor-specific rendering API functionality.

Khronos standards are implemented specifically by the hardware vendor, and so, performance varies between different implementations.

Below are instructions to help you get started with Khronos's Vulkan on Screen.

Note: This document covers steps that are specific to Screen. To complete your application, you will need to refer to Khronos Vulkan tutorials and documentation online. These instructions assume that you are familiar with basic Vulkan APIs and terminology.
  1. Configure your Vulkan display
  2. Create a rendering surface
  3. Create a swap chain
  4. Choosing the correct pixel format for your swap chain
  5. Choosing a presentation mode for your swap chain
  6. Debugging

Configure your Vulkan display

To get started, you will need to configure your Vulkan display in your graphics.conf file. Your Vulkan display needs to be declared as an egl display. Below is an example:

begin egl display 1
  egl-dlls = libglapi-mesa.so libEGL-mesa.so
  glesv1-dlls = libglapi-mesa.so libGLESv1_CM-mesa.so
  glesv2-dlls = libglapi-mesa.so libGLESv2-mesa.so
  gpu-dlls = gpu_drm-gvtg.so
  cl-dlls = libigdrcl.so
  vk-icds = intel_icd.json
  vk-imps = /usr/lib/graphics/vulkan/VkLayer_MESA_overlay.json
end egl display

The parameters vk-exps, vk-dlls, and vk-imps are specific to Vulkan displays. They determine how layers are loaded by the ICD driver. You can set one or more of the parameters to a list of Vulkan JSON file names or paths. The egl display subsection in the Configuring Screen chapter of this guide contains more information on the behaviour of each parameter. Below are examples of the Vulkan JSON files.

intel_icd.json

{
    "ICD": {
        "api_version": "1.2.177",
        "library_path": "/usr/lib/graphics/intel-drm/libvulkan_intel.so"
    },
    "file_format_version": "1.0.0"
}

VkLayer_MESA_overlay.json

{
    "file_format_version" : "1.0.0",
    "layer" : {
        "name": "VK_LAYER_MESA_overlay",
        "type": "GLOBAL",
        "library_path": "/usr/lib/graphics/vulkan/libVkLayer_MESA_overlay.so",
        "api_version": "1.1.73",
        "implementation_version": "1",
        "description": "Mesa Overlay layer",
        "disable_environment": {
          "DISABLE_VK_LAYER_MESA_OVERLAY": ""
        }
    }
}

Create a rendering surface

A surface object is a logical representation of an application's window. The Vulkan surface (VkSurfaceKHR) is designed to allow Vulkan APIs to use it for all Window System Integration (WSI) operations.

How Vulkan APIs communicate with Screen is defined by the VK_QNX_screen_surface extension. This extension contains the following structure used for creating the rendering surface:

typedef struct VkScreenSurfaceCreateInfoQNX {
    VkStructureType                  sType;
    const void*                      pNext;
    VkScreenSurfaceCreateFlagsQNX    flags;
    struct _screen_context*          context;
    struct _screen_window*           window;
} VkScreenSurfaceCreateInfoQNX;

Where:

struct _screen_context* context
Is a pointer to the context used for this display
struct _screen_window* window
Is a pointer to the window used for this display
VkStructureType sType
Must be set to VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX
const void* pNext
Must be set to NULL
VkScreenSurfaceCreateFlagsQNX flags
Must be set to zero (0)

The extension also includes the functions vkCreateScreenSurfaceQNX and vkGetPhysicalDeviceScreenPresentationSupportQNX.

vkGetPhysicalDeviceScreenPresentationSupportQNX is used to check the status of the Vulkan WSI. It returns a Vulkan boolean VK_TRUE or VK_FALSE. A VK_FALSE is returned when the Vulkan WSI cannot create swap chains or can't use Screen for the swap chain using the physical device provided to the function.

VKAPI_ATTR VkBool32 VKAPI_CALL
    vkGetPhysicalDeviceScreenPresentationSupportQNX(
        VkPhysicalDevice                            physicalDevice,
        uint32_t                                    queueFamilyIndex,
        struct _screen_window*                      window);

vkCreateScreenSurfaceQNX is used to create the Vulkan Screen surface, explained below.

VKAPI_ATTR VkResult VKAPI_CALL vkCreateScreenSurfaceQNX(
    VkInstance                                  instance,
    const VkScreenSurfaceCreateInfoQNX*         pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkSurfaceKHR*                               pSurface);

To create a surface, you must first create the context and window:

int rc;
screen_context_t screen_context;
screen_window_t screen_window;
 
VkResult err;
VkInstance inst;
VkSurfaceKHR surface;
 
/* Code for creating your Vulkan instance goes here. */
...
 
rc = screen_create_context(&screen_context, 0);
if (rc) {
    /* Cannot create QNX Screen context */
}
 
rc = screen_create_window(&screen_window, screen_context);
if (rc) {
    /* Cannot create QNX Screen window */
}

Next, you will need to create a WSI surface for the window created in the above step:

VkScreenSurfaceCreateInfoQNX create_info;
 
createInfo.sType = VK_STRUCTURE_TYPE_SCREEN_SURFACE_CREATE_INFO_QNX;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.context = screen_context;
createInfo.window = screen_window;
 
err = vkCreateScreenSurfaceQNX(instance, &create_info, NULL, &surface);
if (err != VK_SUCCESS) {
    /* Cannot create QNX Screen Surface */
}

At this point you should have a valid surface of type VkSurfaceKHR. Use this surface to create a swap chain for the Screen window.

Create a swap chain

A swap chain is a series of window buffers. When setting up your swap chain, you will need to set its image extent, pixel format, number of buffers, presentation mode, and reference the surface you created in the above steps.

To create your swap chain, first you need to obtain surface information such as mode and formats:

VkPhysicalDevice gpu;
VkDevice device;
 
VkSurfaceCapabilitiesKHR surface_capabilities;
VkSwapchainKHR swapchain;
 
/* Obtain surface restrictions and capabilities */
vkGetPhysicalDeviceSurfaceCapabilitiesKHR
(gpu, surface, &surface_capabilities);
 
/* Obtain available presentation modes */
vkGetPhysicalDeviceSurfacePresentModesKHR(...);
 
/* Obrain available surface formats and color spaces */
vkGetPhysicalDeviceSurfaceFormatsKHR(...);

Next, initialize your swap chain structure:

VkSwapchainCreateInfoKHR swapchain_info = {
    .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
    .pNext = NULL,
    .surface = surface,
    .minImageCount = surface_capabilities.minImageCount,
    .imageFormat = VK_FORMAT_B8G8R8A8_UNORM,
    .imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
    .imageExtent = {
        .width = 1920,
        .height = 1080,
    },
    .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
    .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
    .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
    .imageArrayLayers = 1,
    .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
    .queueFamilyIndexCount = 0,
    .pQueueFamilyIndices = NULL,
    .presentMode = VK_PRESENT_MODE_FIFO_KHR,
    .oldSwapchain = NULL,
    .clipped = true,
};

Finally, create the swap chain:

err = vkCreateSwapchainKHR(device, &swapchain_info, NULL, &swapchain);
if (err != VK_SUCCESS) {
    /* Cannot create swap chain */
}

Choosing the correct pixel format for your swap chain

The imageFormat field of the VkSwapchainCreateInfoKHR structure needs to be compatible with your SCREEN_PROPERTY_FORMAT property.

To help you choose the correct format when creating the swap chain, refer to the table below. It contains the most popular Vulkan pixel formats and their corresponding Screen pixel formats.

Vulkan pixel format Screen pixel format

VK_FORMAT_R8G8B8A8_UNORM

VK_FORMAT_R8G8B8A8_SRGB

SCREEN_FORMAT_BGRA8888

VK_FORMAT_B8G8R8A8_UNORM

VK_FORMAT_B8G8R8A8_SRGB

SCREEN_FORMAT_RGBA8888
VK_FORMAT_A2R10G10B10_UNORM_PACK32 SCREEN_FORMAT_RGBA1010102
VK_FORMAT_A2B10G10R10_UNORM_PACK32 SCREEN_FORMAT_BGRA1010102
VK_FORMAT_R5G6B5_UNORM_PACK16 SCREEN_FORMAT_RGB565
VK_FORMAT_A1R5G5B5_UNORM_PACK16 SCREEN_FORMAT_RGBA5551

Choosing a presentation mode for your swap chain

The presentMode in the VkSwapchainCreateInfoKHR structure defines how the chain of buffers is swapped on the window. Choose from the following modes:

Debugging

To debug the process of loading Vulkan drivers, you can use VK_LOADER_DEBUG. The supported modes are:

For example, to enable debug mode for the vulkaninfo utility in the ICD loader, set the variable as shown:

VK_LOADER_DEBUG=all vulkaninfo

For more information on the VK_LOADER_DEBUG environment variable and its options, refer to Vulkan documentation online. For more information on the vulkaninfo utility, refer to the vulkaninfo page in the Utilities and binaries section of this guide.