5.2 展现表面
被渲染的图形数据以备展示的对象被被称为”表面“,由一个VkSurfaceKHR类型的handle表示。这种特殊的对象由VK_KHR_surface拓展引入。这个拓展提供了处理surface对象的功能,但是它在各平台基础上高度定制化,以提供surface与窗口的对接的功能。Interfaces are defined for Microsoft Windows, Mir and Wayland, X Windows via either the XCB or Xlib interface, and Android. 以后有更多的平台加入。
拓展中平台特定的原型和数据类型被包含在最主要的vulkan.h 头文件中,但是被平台特定的宏保护着。本书中的代码支持Windows平台和Linux平台(通过Xlib或者Xcb接口)。为了启用这些代码,在包含vulkan.h之前,我们必须定义VK_USE_PLATFORM_WIN32_KHR, VK_USE_PLATFORM_XLIB_KHR, 或者VK_USE_PLATFORM_LIB_XCB_KHR 这些宏之一。本书的源代码构建系统将会使用一个编译器命令行帮助你完成这个工作。
5.2.1 在Microsoft Windows上展现
在我们可以展示之前,我们需要知道在设备上是否有队列可以支持展示的操作。展示能力是每个队列族都有的能力。在Windows 平台,调用vkGetPhysicalDeviceWin32PresentationSupportKHR()函数来获知一个队列是否支持展示操作,函数原型如下:
VkBool32 vkGetPhysicalDeviceWin32PresentationSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex);
被查询的物理设备通过physicalDevice 参数传递,队列族的索引通过queueFamilyIndex 传递。如果至少有一个队列族支持展示,那么我们就可以继续来在设备上创建表面对象。为了创建表面,使用vkCreateWin32SurfaceKHR()函数,其原型如下:
VkResult vkCreateWin32SurfaceKHR(
VkInstance instance,
const VkWin32SurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSurfaceKHR* pSurface);
这个函数把一个原生的窗口handle和一个新创建的surface对象关联起来,并通过pSurface参数返回这个对象。只有一个Vulkan实例是必须的,它的handle通过instance参数传递。这个新的surface对象的描述信息通过pCreateInfo参数传递,它是一个VkWin32SurfaceCreateInfoKHR类型的指针,其定义如下:
typedef struct VkWin32SurfaceCreateInfoKHR {
VkStructure Type sType;
const void* pNext;
VkWin32SurfaceCreateFlagsKHR flags;
HINSTANCE hinstance;
HWND hwnd;
} VkWin32SurfaceCreateInfoKHR;
VkWin32SurfaceCreateInfoKHR的sType域应被置为VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR, 除非有其他的拓展使用pNext域,否则应该置为nullptr。flags被保留未来使用,应置为0.
hinstance参数应当置为应用程序或者module的HINSTANCE,它是用来创建原生窗口的。这是应用程序传递给WinMain的第一个参数或者调用Win32函数GetModuleHandle获取到。hwnd成员是原生窗口的handle,它可以把Vulkan的surface关联起来。这就是渲染结果将要交换的呈现的窗口。
5.2.2 在基于Xlib的平台上展现
在Xlib-Based系统上创建surface的过程是相似的。第一,我们需要知道平台是否支持在X Server上展现一个Xlib surface。调用函数vkGetPhysicalDeviceXlibPresentationSupportKHR()可以获取这个信息,其原型如下:
VkBool32 vkGetPhysicalDeviceXlibPresentationSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
Display* dpy,
VisualID visualID);
物理设备的handle通过physicalDevice参数指定,队列族通过queueFamilyIndex参数指定,vkGetPhysicalDeviceXlibPresentationSupportKHR()报告了在那个族的队列是否支持在一个X Server 上的Xlib surface展现结果。与X Server之间的联系由dpy参数表示,在每一种格式上展示都是支持的。在Xlib中,格式通过visual ID表示,surface的目标格式是通过visualID参数指定。
假设设备上至少一个队列族支持我们想要使用的格式来展示,我们就可以给一个Xlib窗口创建surface,需调用vkCreateXlibSurfaceKHR()函数,它的原型是:
VkResult vkCreateXlibSurfaceKHR(
VkInstance instance,
const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSurfaceKHR* pSurface);
vkCreateXlibSurfaceKHR()创建一个和Xlib窗口关联的surface。Vulkan实例因该通过instance参数传递,剩下的参数控制surface的创建,通过pCreateInfo参数传递。pCreateInfo是VkXlibSurfaceCreateInfoKHR类型数据实例的指针,该类型定义如下:
typedef struct VkXlibSurfaceCreateInfoKHR {
VkStructureType sType;
const void* pNext;
VkXlibSurfaceCreateFlagsKHR flags;
Display* dpy;
Window window;
} VkXlibSurfaceCreateInfoKHR;
sType域应被置为VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,pNext应置为nullptr。flags域保留使用且应置为0。
Dpy域是Xlib Display,表示和X Server的连接,window是Xlib Window类型的handle,这将和新创建的surface关联起来。
如果vkCreateXlibSurfaceKHR()需要主机内存,它将使用pAllocator参数传递过来的主机内存分配器。如果pAllocator为nullptr,那么内置的内存分配器将会被使用。
如果surface创建成功,返回的VkSurface类型数据的handle将会写入pSurface这个指针。
5.2.3 在Xcb上展现
Xcb是针对X协议的一个比Xlib更轻量级、底层次接口,也是对要求低延迟的应用来说是更好的选择。和Xlib、其他的平台,在Xcb系统上创建显示对象,我们需要知道是否有队列支持展现。可以调用vkGetPhysicalDeviceXcbPresentationSupportKHR(),其原型如下:
VkBool32 vkGetPhysicalDeviceXcbPresentationSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
xcb_connection_t* connection,
xcb_visualid_t visual_id);
被查询的物理设备通过physicalDevice参数传递,队列族的索引通过queueFamilyIndex 参数传递。和X Server的连接通过connection参数传递。再有,在每个visual ID基础上都有不同展示能力,需要查询的visual ID通过visual_id参数传递。
当你已经知道了设备上至少有一个队列族支持在你选择的visual ID上展示,你就可以调用vkCreateXcbSurfaceKHR() 函数来创建可以放渲染结果的surface,函数原型如下:
VkResult vkCreateXcbSurfaceKHR(
VkInstance instance,
const VkXcbSurfaceCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSurfaceKHR* pSurface);
Vulkan实例通过instance传递,剩下的参数通过一个VkXcbSurfaceCreateInfoKHR类型数据的指针pCreateInfo来传递,它控制surface的创建。VkXcbSurfaceCreateInfoKHR定义如下:
typedef struct VkXcbSurfaceCreateInfoKHR {
VkStructureType sType;
const void* pNext;
VkXcbSurfaceCreateFlagsKHR flags;
xcb_connection_t* connection;
xcb_window_t window;
} VkXcbSurfaceCreateInfoKHR;
VkXcbSurfaceCreateInfoKHR的sType域应置为VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,pNext应置为nullptr。flags域保留使用且应置为0。与X Server的连接通过connection与来传递,窗口的handle通过window传递。如果vkCreateXcbSurfaceKHR()调用成功,它将新创建的surface的handle写入到pSurface所指向的变量。如果它需要主机内存来构造这个handle且pAllocator不为nullptr,那么它将使用你提供的内存分配器来获取内存。