]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/vkxlib/vulkancontext.cpp
34c0c511f749c6757820f1121893e2417117efbf
[libs/gui.git] / source / graphics / vkxlib / vulkancontext.cpp
1 #include <stdexcept>
2 #include <vector>
3 #define VK_USE_PLATFORM_XLIB_KHR
4 #include <vulkan/vulkan.h>
5 #include <msp/core/application.h>
6 #include <msp/io/print.h>
7 #include "display_private.h"
8 #include "vulkancontext.h"
9 #include "vulkancontext_platform.h"
10 #include "window_private.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace Graphics {
16
17 string vulkan_error::get_error_message(unsigned code)
18 {
19         switch(static_cast<VkResult>(code))
20         {
21         case VK_SUCCESS: return "success";
22         case VK_NOT_READY: return "not ready";
23         case VK_TIMEOUT: return "timeout";
24         case VK_EVENT_SET: return "event set";
25         case VK_EVENT_RESET: return "event reset";
26         case VK_INCOMPLETE: return "incomplete";
27         case VK_ERROR_OUT_OF_HOST_MEMORY: return "out of host memory";
28         case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "out of device memory";
29         case VK_ERROR_INITIALIZATION_FAILED: return "initialization failed";
30         case VK_ERROR_DEVICE_LOST: return "device lost";
31         case VK_ERROR_MEMORY_MAP_FAILED: return "memory map failed";
32         case VK_ERROR_LAYER_NOT_PRESENT: return "layer not present";
33         case VK_ERROR_EXTENSION_NOT_PRESENT: return "extension not present";
34         case VK_ERROR_FEATURE_NOT_PRESENT: return "feature not present";
35         case VK_ERROR_INCOMPATIBLE_DRIVER: return "incompatible driver";
36         case VK_ERROR_TOO_MANY_OBJECTS: return "too many objects";
37         case VK_ERROR_FORMAT_NOT_SUPPORTED: return "format not supported";
38         case VK_ERROR_FRAGMENTED_POOL: return "fragmented pool";
39         case VK_ERROR_UNKNOWN: return "unknown";
40         case VK_ERROR_OUT_OF_POOL_MEMORY: return "out of pool memory";
41         case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "invalidernal handle";
42         case VK_ERROR_FRAGMENTATION: return "fragmentation";
43         case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "invalid opaque capture address";
44         case VK_ERROR_SURFACE_LOST_KHR: return "surface lost";
45         case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "native window in use";
46         case VK_SUBOPTIMAL_KHR: return "suboptimal";
47         case VK_ERROR_OUT_OF_DATE_KHR: return "out of date";
48         case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "incompatible display";
49         case VK_ERROR_VALIDATION_FAILED_EXT: return "validation failed";
50         case VK_ERROR_INVALID_SHADER_NV: return "invalid shader";
51         case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "invalid drm format modifier plane layout";
52         case VK_ERROR_NOT_PERMITTED_EXT: return "not permitted";
53         case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "full screen exclusive mode lost";
54         case VK_THREAD_IDLE_KHR: return "thread idle";
55         case VK_THREAD_DONE_KHR: return "thread done";
56         case VK_OPERATION_DEFERRED_KHR: return "operation deferred";
57         case VK_OPERATION_NOT_DEFERRED_KHR: return "operation not deferred";
58         case VK_PIPELINE_COMPILE_REQUIRED_EXT: return "pipeline compile required";
59         default: return format("VkResult(%d)", code);
60         }
61 }
62
63 VulkanOptions::VulkanOptions():
64         enable_validation(false),
65         enable_debug_report(false)
66 { }
67
68
69 void VulkanContext::platform_init(const VulkanOptions &opts)
70 {
71         priv = new Private;
72         VulkanFunctions &f = priv->functions;
73
74         try
75         {
76                 f.vkCreateInstance = get_function<PFN_vkCreateInstance>("vkCreateInstance");
77
78                 vector<const char *> layers;
79                 if(opts.enable_validation)
80                         layers.push_back("VK_LAYER_KHRONOS_validation");
81
82                 vector<const char *> extensions;
83                 extensions.push_back("VK_KHR_surface");
84                 extensions.push_back("VK_KHR_xlib_surface");
85                 extensions.push_back("VK_EXT_debug_utils");
86                 if(opts.enable_debug_report)
87                         extensions.push_back("VK_EXT_debug_report");
88
89                 VkApplicationInfo app_info = { };
90                 app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
91                 app_info.pApplicationName = Application::get_name().c_str();
92                 app_info.pEngineName = "MSP";
93                 app_info.apiVersion = (1<<22)|(2<<12);
94
95                 VkInstanceCreateInfo instance_create_info = { };
96                 instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
97                 instance_create_info.pApplicationInfo = &app_info;
98                 instance_create_info.enabledLayerCount = layers.size();
99                 instance_create_info.ppEnabledLayerNames = layers.data();
100                 instance_create_info.enabledExtensionCount = extensions.size();
101                 instance_create_info.ppEnabledExtensionNames = extensions.data();
102
103                 VkResult result = f.vkCreateInstance(&instance_create_info, 0, &priv->instance);
104                 if(result!=VK_SUCCESS)
105                         throw vulkan_error(result, "vkCreateInstance");
106
107                 f.vkDestroyInstance = get_function<PFN_vkDestroyInstance>("vkDestroyInstance");
108                 f.vkEnumeratePhysicalDevices = get_function<PFN_vkEnumeratePhysicalDevices>("vkEnumeratePhysicalDevices");
109                 f.vkGetPhysicalDeviceQueueFamilyProperties = get_function<PFN_vkGetPhysicalDeviceQueueFamilyProperties>("vkGetPhysicalDeviceQueueFamilyProperties");
110                 f.vkGetPhysicalDeviceSurfaceSupport = get_function<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>("vkGetPhysicalDeviceSurfaceSupportKHR");
111                 f.vkCreateDevice = get_function<PFN_vkCreateDevice>("vkCreateDevice");
112                 f.vkDestroyDevice = get_function<PFN_vkDestroyDevice>("vkDestroyDevice");
113                 f.vkGetDeviceQueue = get_function<PFN_vkGetDeviceQueue>("vkGetDeviceQueue");
114                 f.vkCreateXlibSurface = get_function<PFN_vkCreateXlibSurfaceKHR>("vkCreateXlibSurfaceKHR");
115                 f.vkDestroySurface = get_function<PFN_vkDestroySurfaceKHR>("vkDestroySurfaceKHR");
116                 f.vkCreateDebugReportCallback = get_function<PFN_vkCreateDebugReportCallbackEXT>("vkCreateDebugReportCallbackEXT");
117                 f.vkDestroyDebugReportCallback = get_function<PFN_vkDestroyDebugReportCallbackEXT>("vkDestroyDebugReportCallbackEXT");
118
119                 if(opts.enable_debug_report)
120                 {
121                         VkDebugReportCallbackCreateInfoEXT debug_report_create_info = { };
122                         debug_report_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
123                         debug_report_create_info.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT|VK_DEBUG_REPORT_ERROR_BIT_EXT|VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
124                         debug_report_create_info.pfnCallback = &Private::debug_report_func;
125                         debug_report_create_info.pUserData = this;
126
127                         result = f.vkCreateDebugReportCallback(priv->instance, &debug_report_create_info, 0, &priv->debug_report_callback);
128                         if(result!=VK_SUCCESS)
129                                 throw vulkan_error(result, "vkCreateDebugReportCallback");
130                 }
131                 else
132                         priv->debug_report_callback = 0;
133
134                 VkXlibSurfaceCreateInfoKHR surface_create_info = { };
135                 surface_create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
136                 surface_create_info.dpy = display.get_private().display;
137                 surface_create_info.window = window.get_private().window;
138
139                 result = f.vkCreateXlibSurface(priv->instance, &surface_create_info, 0, &priv->surface);
140                 if(result!=VK_SUCCESS)
141                         throw vulkan_error(result, "vkCreateXlibSurface");
142
143                 unsigned n_phys_devices = 0;
144                 result = f.vkEnumeratePhysicalDevices(priv->instance, &n_phys_devices, 0);
145                 if(result!=VK_SUCCESS)
146                         throw vulkan_error(result, "vkEnumeratePhysicalDevices");
147                 else if(!n_phys_devices)
148                         throw runtime_error("no physical device found");
149                 vector<VkPhysicalDevice> phys_devices(n_phys_devices);
150                 result = f.vkEnumeratePhysicalDevices(priv->instance, &n_phys_devices, &phys_devices[0]);
151                 if(result!=VK_SUCCESS)
152                         throw vulkan_error(result, "vkEnumeratePhysicalDevices");
153
154                 priv->physical_device = 0;
155                 unsigned gfx_queue_index = 0;
156                 for(unsigned i=0; i<n_phys_devices; ++i)
157                 {
158                         VkPhysicalDevice phys_device = phys_devices[i];
159
160                         unsigned n_queue_families = 0;
161                         f.vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &n_queue_families, 0);
162                         vector<VkQueueFamilyProperties> queue_family_props(n_queue_families);
163                         f.vkGetPhysicalDeviceQueueFamilyProperties(phys_device, &n_queue_families, queue_family_props.data());
164
165                         for(; gfx_queue_index<n_queue_families; ++gfx_queue_index)
166                                 if((queue_family_props[gfx_queue_index].queueFlags&VK_QUEUE_GRAPHICS_BIT))
167                                         break;
168
169                         if(gfx_queue_index>=n_queue_families)
170                                 continue;
171
172                         VkBool32 supported = VK_FALSE;
173                         result = f.vkGetPhysicalDeviceSurfaceSupport(phys_device, gfx_queue_index, priv->surface, &supported);
174                         if(result!=VK_SUCCESS)
175                                 continue;
176
177                         if(supported)
178                                 priv->physical_device = phys_devices[0];
179                 }
180
181                 if(!priv->physical_device)
182                         throw runtime_error("no usable physical devices found");
183
184                 priv->graphics_queue_family = gfx_queue_index;
185
186                 float queue_priority = 1.0f;
187                 VkDeviceQueueCreateInfo queue_create_info = { };
188                 queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
189                 queue_create_info.queueFamilyIndex = gfx_queue_index;
190                 queue_create_info.queueCount = 1;
191                 queue_create_info.pQueuePriorities = &queue_priority;
192
193                 extensions.clear();
194                 extensions.push_back("VK_KHR_swapchain");
195
196                 VkDeviceCreateInfo device_create_info = { };
197                 device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
198                 device_create_info.queueCreateInfoCount = 1;
199                 device_create_info.pQueueCreateInfos = &queue_create_info;
200                 device_create_info.enabledExtensionCount = extensions.size();
201                 device_create_info.ppEnabledExtensionNames = extensions.data();
202
203                 result = f.vkCreateDevice(priv->physical_device, &device_create_info, 0, &priv->device);
204                 if(result!=VK_SUCCESS)
205                         throw vulkan_error(result, "vkCreateDevice");
206
207                 f.vkGetDeviceQueue(priv->device, gfx_queue_index, 0, &priv->graphics_queue);
208         }
209         catch(...)
210         {
211                 if(priv->device)
212                         f.vkDestroyDevice(priv->device, 0);
213                 if(priv->surface)
214                         f.vkDestroySurface(priv->instance, priv->surface, 0);
215                 if(priv->debug_report_callback)
216                         f.vkDestroyDebugReportCallback(priv->instance, priv->debug_report_callback, 0);
217                 if(priv->instance)
218                         f.vkDestroyInstance(priv->instance, 0);
219                 delete priv;
220                 throw;
221         }
222 }
223
224 VulkanContext::~VulkanContext()
225 {
226         const VulkanFunctions &f = priv->functions;
227         f.vkDestroyDevice(priv->device, 0);
228         f.vkDestroySurface(priv->instance, priv->surface, 0);
229         if(priv->debug_report_callback)
230                 f.vkDestroyDebugReportCallback(priv->instance, priv->debug_report_callback, 0);
231         f.vkDestroyInstance(priv->instance, 0);
232         delete priv;
233 }
234
235 void (*VulkanContext::_get_function(const std::string &name) const)()
236 {
237         return vkGetInstanceProcAddr(priv->instance, name.c_str());
238 }
239
240
241 VulkanContext::Private::Private():
242         instance(0),
243         physical_device(0),
244         device(0),
245         graphics_queue(0),
246         debug_report_callback(0)
247 { }
248
249 VkBool32 VulkanContext::Private::debug_report_func(VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT obj_type, uint64_t obj_id, size_t, int32_t, const char *layer_prefix, const char *message, void *)
250 {
251         IO::print(IO::cerr, "Vulkan debug report from %s: Object %d of type %d: %s\n", layer_prefix, obj_type, obj_id, message);
252         return VK_FALSE;
253 }
254
255 } // namespace Graphics
256 } // namespace Msp
257