Hello everyone, the next will introduce Vulkan Window Surface .
Vulkan
Is a free platform features associated with the API
collection. It can not interact directly with the window system. In order to render the results are presented to the screen, you need to establish Vulkan
a connection between the window system, we need to use WSI
(a form of system integration) extension. In this section, we will discuss first, that VK_KHR_surface . It exposes VkSurfaceKHR , it represents surface
a type of abstract, to present the rendered image to use. We want to use in the program to surface
be introduced by our already GLFW
extended its associated open form of support. In simple terms surface is connected to form a bridge Vulkan system.
VK_KHR_surface extension is an instance-level extension, so far we've had it enabled, it is included in glfwGetRequiredInstanceExtensions returned list. The list also includes a number of other WSI extension will use in the next few panels.
You need to instance
create a form immediately after creation surface
, because it will affect the choice of a physical device. The reason why this section will surface
create logic included in the discussions, is because the form surface
for rendering, presentation is a relatively big problem, if premature creating a physical device to join this part, will confuse the basic physical device settings work. In addition the form surface
itself to Vulkan
also not compulsory. Vulkan
Allowed to do so, it does not need the same OpenGL
as is necessary to create a form surface
.
First, create a Window Surface
Now set out to create a form surface
, a class member debugCallback
to join members of the variables under Surface .
VkSurfaceKHR surface;
Although VkSurfaceKHR object and its use is not associated with the platform, but the details need to rely on the creation of a specific form of the system. For example, in the Windows
platform, it takes WIndows
on the HWND and HMODULE handle. Thus providing the appropriate extension for a particular platform, in Windows
the as VK_KHR_win32_surface , it is automatically included in glfwGetRequiredInstanceExtensions list.
We will demonstrate how to use the platform-specific extensions to create Windows
on the surface
bridge, but does not actually use it in the tutorial. The use of such libraries GLFW avoid writing no sense of cross-platform-dependent code. GLFW
In fact by glfwCreateWindowSurface a good deal of difference platform. Of course, the ideal is to rely on them to help us before the completion of specific work, look behind the realization is helpful.
Because a form surface
is a Vulkan
subject that needs filling VkWin32SurfaceCreateInfoKHR structure, there are two important parameters: HWND and hInstance . If you are familiar windows
under development you should know that these are the window handle and process.
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = glfwGetWin32Window(window);
createInfo.hinstance = GetModuleHandle(nullptr);
glfwGetWin32Window function is used to obtain the original form object from GLFW the HWND . GetModuleHandle function returns the current process HINSTANCE handle.
After completion of the filling structure, you can use vkCreateWin32SurfaceKHR create surface bridge, and before obtaining create, destroy DebugReportCallEXT , as here, the same need to instance
create get surface
in function. Here are the parameters involved instance
, surface
information created, custom allocator and ultimately save surface
handle variables.
auto CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");
if (!CreateWin32SurfaceKHR || CreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("failed to create window surface!");
}
This process is similar to the other platforms, for example Linux
, using the X11 interface window system, by vkCreateXcbSurfaceKHR establish a connection function.
glfwCreateWindowSurface function according to the difference of different platforms, on the implementation details will be different. We will now integrate them into our program. From initVulkan add a function CreateSurface , arranged in createInstnace and setupDebugCallback after the function.
void initVulkan() {
createInstance();
setupDebugCallback();
createSurface();
pickPhysicalDevice();
createLogicalDevice();
}
void createSurface() {
}
GLFW
Do not use the structure, but chose a very direct parameters passed to the calling function.
void createSurface() {
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("failed to create window surface!");
}
}
Parameter is VkInstance , GLFW
the form of the pointer, and a dispenser for storing custom VkSurfaceKHR variable pointer. Unified platform for different return VkResult . GLFW
It does not provide a dedicated function to destroy surface
, but simply by Vulkan
the original API
completion:
void cleanup() {
...
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
...
}
Finally, make sure to clean surface is completed before the instance is destroyed.
Second, the inquiry demonstrate support
Although the Vulkan
implementation of integrated functionality to support the form, but that does not mean each physical device in the system supports it. Therefore, we need to expand isDeviceSuitable function, ensure that the device can be rendered to the image we have created surface
. Because presentation
is a characteristic feature of the queue, so the solution to the problem is to find the support presentation
of a cluster queue, queue finally get to meet the surface
need to create.
The reality is that support graphics
command queue and cluster support presentation
command queue cluster may not be the same cluster. Therefore, we need to modify QueueFamilyIndices structure to support the differentiation of storage.
struct QueueFamilyIndices {
int graphicsFamily = -1;
int presentFamily = -1;
bool isComplete() {
return graphicsFamily >= 0 && presentFamily >= 0;
}
};
Next, we modify findQueueFamilies function to find with presentation
queues cluster functions. Core code is used to check the function vkGetPhysicalDeviceSurfaceSupportKHR , it physical device, a queue and cluster index surface
as parameters. In VK_QUEUE_GRAPHICS_BIT add a call to a function in the same loop:
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
Then the need to check a Boolean value and stored in presentation
a queue clustered indexes:
if (queueFamily.queueCount > 0 && presentSupport) {
indices.presentFamily = i;
}
Note that, in order to support graphics
and presentation
functionality, our actual environment may be obtained in the same queue cluster, it may be different, for in our program structure and data selection logic will follow are from different clusters are processing queue , so that we can deal with the unified both cases. In addition, for performance reasons, we can also add logic to explicitly specify the physical equipment used graphics
and presentation
functions from the same cluster queue.
Third, create a presentation queue
The remaining thing is to modify the logical device creation process is to create presentation
the queue and get VkQueue handle. Add save queue handle member variables:
VkQueue presentQueue;
Next, we need more VkDeviceQueueCreateInfo structures to create queues with different functions. An elegant way is to create a set collection to ensure the uniqueness of the queue for the queue clusters of clusters of different functions:
#include <set>
...
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};
float queuePriority = 1.0f;
for (int queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
But also modify VkDeviceCreateInfo point set of queues:
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
If the queue is the same cluster, then we need to pass the time index. Finally, add a call to retrieve the queue handle:
vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);
In this example, the queue is the same cluster, the two handles may have the same value.