wl-graphics-demo

wl-graphics-demo.git
git clone git://git.lenczewski.org/wl-graphics-demo.git
Log | Files | Refs | README

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, &registry_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 }