wl-vk.c (10411B)
1 #include <wayland-client.h> 2 #include "xdg-shell.h" 3 4 #define VK_USE_PLATFORM_WAYLAND_KHR 1 5 #include <vulkan/vulkan.h> 6 #include <vulkan/vulkan_wayland.h> 7 8 #include <alloca.h> 9 #include <assert.h> 10 #include <stdbool.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #define ARRLEN(arr) (sizeof (arr) / sizeof (arr)[0]) 16 17 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 18 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 19 #define CLAMP(v, min, max) (MIN(MAX(v, min), max)) 20 21 struct wl_display *display; 22 struct wl_compositor *compositor; 23 struct xdg_wm_base *wm_base; 24 char running; 25 26 #define DEFAULT_WIDTH 256 27 #define DEFAULT_HEIGHT 256 28 29 #define MAX_FRAMES_IN_FLIGHT 2 30 31 struct window { 32 struct wl_surface *surface; 33 struct xdg_surface *xdg_surface; 34 struct xdg_toplevel *xdg_toplevel; 35 36 char resize; 37 uint32_t width, height; 38 39 struct { 40 VkInstance instance; 41 VkDebugUtilsMessengerEXT debug_messenger; 42 VkSurfaceKHR surface; 43 44 /* boilerplate vulkan state */ 45 VkPhysicalDevice physical_device; 46 VkDevice device; 47 48 VkQueue graphics_queue; 49 VkQueue present_queue; 50 51 VkSwapchainKHR swapchain; 52 VkFormat swapchain_image_format; 53 VkExtent2D swapchain_extent; 54 55 struct { 56 VkImage *ptr; 57 uint32_t len; 58 } swapchain_images; 59 60 struct { 61 VkImageView *ptr; 62 uint32_t len; 63 } swapchain_image_views; 64 65 struct { 66 VkFramebuffer *ptr; 67 uint32_t len; 68 } swapchain_framebuffers; 69 70 struct { 71 VkSemaphore *ptr; 72 uint32_t len; 73 } swapchain_submit_semaphores; 74 75 VkRenderPass render_pass; 76 VkPipelineLayout pipeline_layout; 77 VkPipeline pipeline; 78 79 VkCommandPool command_pool; 80 VkCommandBuffer command_buffers[MAX_FRAMES_IN_FLIGHT]; 81 82 struct { 83 VkSemaphore image_available[MAX_FRAMES_IN_FLIGHT]; 84 VkFence in_flight[MAX_FRAMES_IN_FLIGHT]; 85 } sync_objects; 86 87 uint32_t frame_index; 88 } vk; 89 }; 90 91 static void 92 wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) 93 { 94 xdg_wm_base_pong(wm_base, serial); 95 } 96 97 static const struct xdg_wm_base_listener xdg_wm_base_listener = { 98 .ping = wm_base_ping, 99 }; 100 101 static void 102 registry_add_global(void *data, struct wl_registry *registry, uint32_t name, char const *interface, uint32_t version) 103 { 104 if (!strcmp(interface, wl_compositor_interface.name)) { 105 compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 6); 106 } else if (!strcmp(interface, xdg_wm_base_interface.name)) { 107 wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 6); 108 xdg_wm_base_add_listener(wm_base, &xdg_wm_base_listener, NULL); 109 } 110 } 111 112 static void 113 registry_remove_global(void *data, struct wl_registry *registry, uint32_t name) 114 { 115 } 116 117 static const struct wl_registry_listener registry_listener = { 118 .global = registry_add_global, 119 .global_remove = registry_remove_global, 120 }; 121 122 static void 123 xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) 124 { 125 xdg_surface_ack_configure(xdg_surface, serial); 126 } 127 128 static const struct xdg_surface_listener xdg_surface_listener = { 129 .configure = xdg_surface_configure, 130 }; 131 132 static void 133 xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel, 134 int32_t width, int32_t height, struct wl_array *states) 135 { 136 fprintf(stderr, "xdg_toplevel_configure: width: %d, height: %d\n", width, height); 137 138 struct window *window = data; 139 140 window->resize = 1; 141 window->width = width ? width : DEFAULT_WIDTH; 142 window->height = height ? height : DEFAULT_HEIGHT; 143 144 fprintf(stderr, "Created vk_window\n"); 145 } 146 147 static void 148 xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel) 149 { 150 running = 0; 151 152 fprintf(stderr, "Closing vk_window\n"); 153 } 154 155 static void 156 xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *toplevel, 157 int32_t width, int32_t height) 158 { 159 fprintf(stderr, "xdg_toplevel_configure_bounds: width: %d, height: %d\n", width, height); 160 161 struct window *window = data; 162 163 window->resize = 1; 164 window->width = width ? width : DEFAULT_WIDTH; 165 window->height = height ? height : DEFAULT_HEIGHT; 166 167 fprintf(stderr, "Resized vk_window\n"); 168 } 169 170 static void 171 xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *toplevel, 172 struct wl_array *caps) 173 { 174 } 175 176 static const struct xdg_toplevel_listener xdg_toplevel_listener = { 177 .configure = xdg_toplevel_configure, 178 .close = xdg_toplevel_close, 179 .configure_bounds = xdg_toplevel_configure_bounds, 180 .wm_capabilities = xdg_toplevel_wm_capabilities, 181 }; 182 183 static VKAPI_ATTR VkBool32 VKAPI_CALL 184 vk_debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, 185 VkDebugUtilsMessageTypeFlagsEXT type, 186 const VkDebugUtilsMessengerCallbackDataEXT *callback_data, 187 void *user_data) 188 { 189 (void) severity; 190 (void) type; 191 (void) user_data; 192 193 fprintf(stderr, "vulkan: validation: %s\n", callback_data->pMessage); 194 195 return VK_FALSE; 196 } 197 198 void 199 create_vk_window(struct window *window) 200 { 201 window->surface = wl_compositor_create_surface(compositor); 202 203 window->xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, window->surface); 204 xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, NULL); 205 206 window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); 207 xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, window); 208 209 xdg_toplevel_set_title(window->xdg_toplevel, "wl-vk-example"); 210 211 wl_surface_commit(window->surface); 212 wl_display_roundtrip(display); 213 214 { 215 // NOTE: debug code to display available extensions and layers 216 uint32_t extension_count; 217 vkEnumerateInstanceExtensionProperties(NULL, &extension_count, NULL); 218 219 fprintf(stderr, "Extension count: %" PRIu32 "\n", extension_count); 220 221 VkExtensionProperties *extensions = alloca(extension_count * sizeof *extensions); 222 vkEnumerateInstanceExtensionProperties(NULL, &extension_count, extensions); 223 224 for (uint32_t i = 0; i < extension_count; i++) 225 fprintf(stderr, "\t%s\n", extensions[i].extensionName); 226 227 uint32_t layer_count; 228 vkEnumerateInstanceLayerProperties(&layer_count, NULL); 229 230 fprintf(stderr, "Layer count: %" PRIu32 "\n", layer_count); 231 232 VkLayerProperties *layers = alloca(layer_count * sizeof *layers); 233 vkEnumerateInstanceLayerProperties(&layer_count, layers); 234 235 for (uint32_t i = 0; i < layer_count; i++) 236 fprintf(stderr, "\t%s\n", layers[i].layerName); 237 } 238 239 VkApplicationInfo app_info = { 240 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, 241 .pApplicationName = "wl-vk-example", 242 .applicationVersion = VK_MAKE_VERSION(1, 0, 0), 243 .pEngineName = "No Engine", 244 .engineVersion = VK_MAKE_VERSION(1, 0, 0), 245 .apiVersion = VK_API_VERSION_1_0, 246 }; 247 248 const char *instance_extensions[] = { 249 VK_KHR_SURFACE_EXTENSION_NAME, 250 VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, 251 VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, 252 VK_EXT_DEBUG_UTILS_EXTENSION_NAME, 253 }; 254 255 const char *instance_layers[] = { 256 "VK_LAYER_KHRONOS_validation", 257 }; 258 259 uint32_t debug_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT 260 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT 261 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT 262 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 263 264 uint32_t debug_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT 265 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT 266 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 267 268 VkDebugUtilsMessengerCreateInfoEXT debug_info = { 269 .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 270 .messageSeverity = debug_severity, 271 .messageType = debug_type, 272 .pfnUserCallback = vk_debug_callback, 273 .pUserData = NULL, 274 }; 275 276 VkInstanceCreateInfo instance_info = { 277 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 278 .pApplicationInfo = &app_info, 279 .enabledExtensionCount = ARRLEN(instance_extensions), 280 .ppEnabledExtensionNames = instance_extensions, 281 .enabledLayerCount = ARRLEN(instance_layers), 282 .ppEnabledLayerNames = instance_layers, 283 .pNext = &debug_info, 284 }; 285 286 VkInstance vk_instance; 287 if (vkCreateInstance(&instance_info, NULL, &window->vk.instance) != VK_SUCCESS) { 288 fprintf(stderr, "Failed to create Vulkan instance!\n"); 289 exit(EXIT_FAILURE); 290 } 291 292 PFN_vkCreateDebugUtilsMessengerEXT vk_create_debug_utils_messenger_ext = 293 (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(window->vk.instance, 294 "vkCreateDebugUtilsMessengerEXT"); 295 if (!vk_create_debug_utils_messenger_ext) { 296 fprintf(stderr, "Failed to get proc address of vkCreateDebugUtilsMessengerEXT!\n"); 297 exit(EXIT_FAILURE); 298 } 299 300 if (vk_create_debug_utils_messenger_ext(window->vk.instance, &debug_info, NULL, &window->vk.debug_messenger) != VK_SUCCESS) { 301 fprintf(stderr, "Failed to create Vulkan debug messenger!\n"); 302 exit(EXIT_FAILURE); 303 } 304 305 VkWaylandSurfaceCreateInfoKHR surface_info = { 306 .sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 307 .display = display, 308 .surface = window->surface, 309 }; 310 311 VkSurfaceKHR vk_surface; 312 if (vkCreateWaylandSurfaceKHR(window->vk.instance, &surface_info, NULL, &window->vk.surface) != VK_SUCCESS) { 313 fprintf(stderr, "Failed to create Vulkan wayland surface!\n"); 314 exit(EXIT_FAILURE); 315 } 316 } 317 318 void 319 destroy_vk_window(struct window *window) 320 { 321 vkDestroySurfaceKHR(window->vk.instance, window->vk.surface, NULL); 322 323 PFN_vkDestroyDebugUtilsMessengerEXT vk_destroy_debug_utils_messenger_ext = 324 (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(window->vk.instance, 325 "vkDestroyDebugUtilsMessengerEXT"); 326 if (!vk_destroy_debug_utils_messenger_ext) { 327 fprintf(stderr, "Failed to get proc address of vkDestroyDebugUtilsMessengerEXT!\n"); 328 } 329 330 vk_destroy_debug_utils_messenger_ext(window->vk.instance, window->vk.debug_messenger, NULL); 331 332 vkDestroyInstance(window->vk.instance, NULL); 333 334 xdg_toplevel_destroy(window->xdg_toplevel); 335 xdg_surface_destroy(window->xdg_surface); 336 wl_surface_destroy(window->surface); 337 } 338 339 static void 340 vk_init(struct window *window); 341 342 static void 343 vk_draw(struct window *window); 344 345 static void 346 vk_free(struct window *window); 347 348 // vulkan boilerplate code moved out to separate file to avoid clutter 349 #include "vulkan.c" 350 351 int 352 main(void) 353 { 354 fprintf(stderr, "Hello, World!\n"); 355 356 display = wl_display_connect(NULL); 357 358 struct wl_registry *registry = wl_display_get_registry(display); 359 wl_registry_add_listener(registry, ®istry_listener, NULL); 360 wl_display_roundtrip(display); 361 362 struct window window; 363 create_vk_window(&window); 364 365 vk_init(&window); 366 367 running = 1; 368 while (running) { 369 wl_display_dispatch_pending(display); 370 vk_draw(&window); 371 } 372 373 vk_free(&window); 374 375 destroy_vk_window(&window); 376 377 wl_display_disconnect(display); 378 379 fprintf(stderr, "Goodbye, World!\n"); 380 381 return 0; 382 }