Vulkan rendering APIs
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.
- Configure your Vulkan display
- Create a rendering surface
- Create a swap chain
- Choosing the correct pixel format for your swap chain
- Choosing a presentation mode for your swap chain
- 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-icds, 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:
- VK_PRESENT_MODE_FIFO_KHR: the presentation engine waits for the next vertical blanking period to update the current image. An internal FIFO queue is used to hold pending requests. New requests are added to the end of the queue. If the queue is not empty, one request is removed from the front of the queue during each vertical blanking period.
- VK_PRESENT_MODE_IMMEDIATE_KHR: the presentation engine does not wait for a vertical blanking period to update the current image. There is no internal queueing and requests are updated immediately.
- VK_PRESENT_MODE_MAILBOX_KHR: the presentation engine waits for the next vertical blanking period to update the current image. An internal single entry queue is used to hold pending requests. Incoming requests replace any that are in the queue. Replaced requests become available for re-use.
Debugging
To debug the process of loading Vulkan drivers, you can use VK_LOADER_DEBUG. The supported modes are:
- all
- warn
- info
- perf
- error
- debug
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.