2 * Copyright (c) 2015-2016 The Khronos Group Inc.
3 * Copyright (c) 2015-2016 Valve Corporation
4 * Copyright (c) 2015-2016 LunarG, Inc.
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and/or associated documentation files (the "Materials"), to
8 * deal in the Materials without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Materials, and to permit persons to whom the Materials are
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice(s) and this permission notice shall be included in
14 * all copies or substantial portions of the Materials.
16 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23 * USE OR OTHER DEALINGS IN THE MATERIALS.
25 * Author: Chia-I Wu <olvaffe@gmail.com>
26 * Author: Cody Northrop <cody@lunarg.com>
27 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
28 * Author: Ian Elliott <ian@LunarG.com>
29 * Author: Jon Ashburn <jon@lunarg.com>
30 * Author: Piers Daniell <pdaniell@nvidia.com>
33 * Draw a textured triangle with depth testing. This is written against Intel
34 * ICD. It does not do state transition nor object memory binding like it
35 * should. It also does no error checking.
39 #define _ISOC11_SOURCE /* for aligned_alloc() */
52 #pragma comment(linker, "/subsystem:windows")
53 #define APP_NAME_STR_LEN 80
56 #include <vulkan/vulkan.h>
58 #define DEMO_TEXTURE_COUNT 1
59 #define VERTEX_BUFFER_BIND_ID 0
60 #define APP_SHORT_NAME "tri"
61 #define APP_LONG_NAME "The Vulkan Triangle Demo Program"
63 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
65 #if defined(NDEBUG) && defined(__GNUC__)
66 #define U_ASSERT_ONLY __attribute__((unused))
72 #define ERR_EXIT(err_msg, err_class) \
74 MessageBox(NULL, err_msg, err_class, MB_OK); \
79 #define ERR_EXIT(err_msg, err_class) \
87 #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
89 demo->fp##entrypoint = \
90 (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \
91 if (demo->fp##entrypoint == NULL) { \
92 ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \
93 "vkGetInstanceProcAddr Failure"); \
97 #define GET_DEVICE_PROC_ADDR(dev, entrypoint) \
99 demo->fp##entrypoint = \
100 (PFN_vk##entrypoint)vkGetDeviceProcAddr(dev, "vk" #entrypoint); \
101 if (demo->fp##entrypoint == NULL) { \
102 ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \
103 "vkGetDeviceProcAddr Failure"); \
107 struct texture_object {
111 VkImageLayout imageLayout;
115 int32_t tex_width, tex_height;
118 static int validation_error = 0;
120 VKAPI_ATTR VkBool32 VKAPI_CALL
121 dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
122 uint64_t srcObject, size_t location, int32_t msgCode,
123 const char *pLayerPrefix, const char *pMsg, void *pUserData) {
124 char *message = (char *)malloc(strlen(pMsg) + 100);
128 validation_error = 1;
130 if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
131 sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode,
133 } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
134 sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode,
141 MessageBox(NULL, message, "Alert", MB_OK);
143 printf("%s\n", message);
149 * false indicates that layer should not bail-out of an
150 * API call that had validation failures. This may mean that the
151 * app dies inside the driver due to invalid parameter(s).
152 * That's what would happen without validation layers, so we'll
153 * keep that behavior here.
158 VKAPI_ATTR VkBool32 VKAPI_CALL
159 BreakCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
160 uint64_t srcObject, size_t location, int32_t msgCode,
161 const char *pLayerPrefix, const char *pMsg,
172 typedef struct _SwapchainBuffers {
180 #define APP_NAME_STR_LEN 80
181 HINSTANCE connection; // hInstance - Windows Instance
182 char name[APP_NAME_STR_LEN]; // Name to put on the window/icon
183 HWND window; // hWnd - window handle
185 struct wl_display *display;
186 struct wl_registry *registry;
187 struct wl_compositor *compositor;
188 struct wl_seat *seat;
189 struct wl_surface *wl_surface;
191 struct wl_shell *shell;
192 struct wl_shell_surface *shell_surface;
194 VkSurfaceKHR surface;
196 bool use_staging_buffer;
199 VkPhysicalDevice gpu;
202 VkPhysicalDeviceProperties gpu_props;
203 VkQueueFamilyProperties *queue_props;
204 uint32_t graphics_queue_node_index;
206 uint32_t enabled_extension_count;
207 uint32_t enabled_layer_count;
208 char *extension_names[64];
209 char *device_validation_layers[64];
213 VkColorSpaceKHR color_space;
215 PFN_vkGetPhysicalDeviceSurfaceSupportKHR
216 fpGetPhysicalDeviceSurfaceSupportKHR;
217 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
218 fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
219 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR
220 fpGetPhysicalDeviceSurfaceFormatsKHR;
221 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
222 fpGetPhysicalDeviceSurfacePresentModesKHR;
223 PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
224 PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
225 PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
226 PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
227 PFN_vkQueuePresentKHR fpQueuePresentKHR;
228 uint32_t swapchainImageCount;
229 VkSwapchainKHR swapchain;
230 SwapchainBuffers *buffers;
232 VkCommandPool cmd_pool;
242 struct texture_object textures[DEMO_TEXTURE_COUNT];
248 VkPipelineVertexInputStateCreateInfo vi;
249 VkVertexInputBindingDescription vi_bindings[1];
250 VkVertexInputAttributeDescription vi_attrs[2];
253 VkCommandBuffer setup_cmd; // Command Buffer for initialization commands
254 VkCommandBuffer draw_cmd; // Command Buffer for drawing commands
255 VkPipelineLayout pipeline_layout;
256 VkDescriptorSetLayout desc_layout;
257 VkPipelineCache pipelineCache;
258 VkRenderPass render_pass;
261 VkShaderModule vert_shader_module;
262 VkShaderModule frag_shader_module;
264 VkDescriptorPool desc_pool;
265 VkDescriptorSet desc_set;
267 VkFramebuffer *framebuffers;
269 VkPhysicalDeviceMemoryProperties memory_properties;
275 PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback;
276 PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback;
277 VkDebugReportCallbackEXT msg_callback;
278 PFN_vkDebugReportMessageEXT DebugReportMessage;
281 float depthIncrement;
284 uint32_t current_buffer;
285 uint32_t queue_count;
288 // Forward declaration:
289 static void demo_resize(struct demo *demo);
291 static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits,
292 VkFlags requirements_mask,
293 uint32_t *typeIndex) {
296 // Search memtypes to find first index with those properties
297 for (i = 0; i < 32; i++) {
298 if ((typeBits & 1) == 1) {
299 // Type is available, does it match user properties?
300 if ((demo->memory_properties.memoryTypes[i].propertyFlags &
301 requirements_mask) == requirements_mask) {
308 // No memory types matched, return failure
312 static void demo_flush_init_cmd(struct demo *demo) {
313 VkResult U_ASSERT_ONLY err;
315 if (demo->setup_cmd == VK_NULL_HANDLE)
318 err = vkEndCommandBuffer(demo->setup_cmd);
321 const VkCommandBuffer cmd_bufs[] = {demo->setup_cmd};
322 VkFence nullFence = {VK_NULL_HANDLE};
323 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
325 .waitSemaphoreCount = 0,
326 .pWaitSemaphores = NULL,
327 .pWaitDstStageMask = NULL,
328 .commandBufferCount = 1,
329 .pCommandBuffers = cmd_bufs,
330 .signalSemaphoreCount = 0,
331 .pSignalSemaphores = NULL};
333 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
336 err = vkQueueWaitIdle(demo->queue);
339 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs);
340 demo->setup_cmd = VK_NULL_HANDLE;
343 static void demo_set_image_layout(struct demo *demo, VkImage image,
344 VkImageAspectFlags aspectMask,
345 VkImageLayout old_image_layout,
346 VkImageLayout new_image_layout,
347 VkAccessFlagBits srcAccessMask) {
349 VkResult U_ASSERT_ONLY err;
351 if (demo->setup_cmd == VK_NULL_HANDLE) {
352 const VkCommandBufferAllocateInfo cmd = {
353 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
355 .commandPool = demo->cmd_pool,
356 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
357 .commandBufferCount = 1,
360 err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->setup_cmd);
363 VkCommandBufferInheritanceInfo cmd_buf_hinfo = {
364 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
366 .renderPass = VK_NULL_HANDLE,
368 .framebuffer = VK_NULL_HANDLE,
369 .occlusionQueryEnable = VK_FALSE,
371 .pipelineStatistics = 0,
373 VkCommandBufferBeginInfo cmd_buf_info = {
374 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
377 .pInheritanceInfo = &cmd_buf_hinfo,
379 err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info);
383 VkImageMemoryBarrier image_memory_barrier = {
384 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
386 .srcAccessMask = srcAccessMask,
388 .oldLayout = old_image_layout,
389 .newLayout = new_image_layout,
391 .subresourceRange = {aspectMask, 0, 1, 0, 1}};
393 if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
394 /* Make sure anything that was copying from this image has completed */
395 image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
398 if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
399 image_memory_barrier.dstAccessMask =
400 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
403 if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
404 image_memory_barrier.dstAccessMask =
405 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
408 if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
409 /* Make sure any Copy or CPU writes to image are flushed */
410 image_memory_barrier.dstAccessMask =
411 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
414 VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier;
416 VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
417 VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
419 vkCmdPipelineBarrier(demo->setup_cmd, src_stages, dest_stages, 0, 0, NULL,
420 0, NULL, 1, pmemory_barrier);
423 static void demo_draw_build_cmd(struct demo *demo) {
424 const VkCommandBufferInheritanceInfo cmd_buf_hinfo = {
425 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
427 .renderPass = VK_NULL_HANDLE,
429 .framebuffer = VK_NULL_HANDLE,
430 .occlusionQueryEnable = VK_FALSE,
432 .pipelineStatistics = 0,
434 const VkCommandBufferBeginInfo cmd_buf_info = {
435 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
438 .pInheritanceInfo = &cmd_buf_hinfo,
440 const VkClearValue clear_values[2] = {
441 [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}},
442 [1] = {.depthStencil = {demo->depthStencil, 0}},
444 const VkRenderPassBeginInfo rp_begin = {
445 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
447 .renderPass = demo->render_pass,
448 .framebuffer = demo->framebuffers[demo->current_buffer],
449 .renderArea.offset.x = 0,
450 .renderArea.offset.y = 0,
451 .renderArea.extent.width = demo->width,
452 .renderArea.extent.height = demo->height,
453 .clearValueCount = 2,
454 .pClearValues = clear_values,
456 VkResult U_ASSERT_ONLY err;
458 err = vkBeginCommandBuffer(demo->draw_cmd, &cmd_buf_info);
461 vkCmdBeginRenderPass(demo->draw_cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE);
462 vkCmdBindPipeline(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
464 vkCmdBindDescriptorSets(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS,
465 demo->pipeline_layout, 0, 1, &demo->desc_set, 0,
469 memset(&viewport, 0, sizeof(viewport));
470 viewport.height = (float)demo->height;
471 viewport.width = (float)demo->width;
472 viewport.minDepth = (float)0.0f;
473 viewport.maxDepth = (float)1.0f;
474 vkCmdSetViewport(demo->draw_cmd, 0, 1, &viewport);
477 memset(&scissor, 0, sizeof(scissor));
478 scissor.extent.width = demo->width;
479 scissor.extent.height = demo->height;
480 scissor.offset.x = 0;
481 scissor.offset.y = 0;
482 vkCmdSetScissor(demo->draw_cmd, 0, 1, &scissor);
484 VkDeviceSize offsets[1] = {0};
485 vkCmdBindVertexBuffers(demo->draw_cmd, VERTEX_BUFFER_BIND_ID, 1,
486 &demo->vertices.buf, offsets);
488 vkCmdDraw(demo->draw_cmd, 3, 1, 0, 0);
489 vkCmdEndRenderPass(demo->draw_cmd);
491 VkImageMemoryBarrier prePresentBarrier = {
492 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
494 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
495 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
496 .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
497 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
498 .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
499 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
500 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
502 prePresentBarrier.image = demo->buffers[demo->current_buffer].image;
503 VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier;
504 vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
505 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0,
506 NULL, 1, pmemory_barrier);
508 err = vkEndCommandBuffer(demo->draw_cmd);
512 static void demo_draw(struct demo *demo) {
513 VkResult U_ASSERT_ONLY err;
514 VkSemaphore presentCompleteSemaphore;
515 VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = {
516 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
521 err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo,
522 NULL, &presentCompleteSemaphore);
525 // Get the index of the next available swapchain image:
526 err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX,
527 presentCompleteSemaphore,
528 (VkFence)0, // TODO: Show use of fence
529 &demo->current_buffer);
530 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
531 // demo->swapchain is out of date (e.g. the window was resized) and
532 // must be recreated:
535 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL);
537 } else if (err == VK_SUBOPTIMAL_KHR) {
538 // demo->swapchain is not as optimal as it could be, but the platform's
539 // presentation engine will still present the image correctly.
544 // Assume the command buffer has been run on current_buffer before so
545 // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL
546 demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image,
547 VK_IMAGE_ASPECT_COLOR_BIT,
548 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
549 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
551 demo_flush_init_cmd(demo);
553 // Wait for the present complete semaphore to be signaled to ensure
554 // that the image won't be rendered to until the presentation
555 // engine has fully released ownership to the application, and it is
556 // okay to render to the image.
558 // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
559 demo_draw_build_cmd(demo);
560 VkFence nullFence = VK_NULL_HANDLE;
561 VkPipelineStageFlags pipe_stage_flags =
562 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
563 VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
565 .waitSemaphoreCount = 1,
566 .pWaitSemaphores = &presentCompleteSemaphore,
567 .pWaitDstStageMask = &pipe_stage_flags,
568 .commandBufferCount = 1,
569 .pCommandBuffers = &demo->draw_cmd,
570 .signalSemaphoreCount = 0,
571 .pSignalSemaphores = NULL};
573 err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence);
576 VkPresentInfoKHR present = {
577 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
580 .pSwapchains = &demo->swapchain,
581 .pImageIndices = &demo->current_buffer,
584 // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER?
585 err = demo->fpQueuePresentKHR(demo->queue, &present);
586 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
587 // demo->swapchain is out of date (e.g. the window was resized) and
588 // must be recreated:
590 } else if (err == VK_SUBOPTIMAL_KHR) {
591 // demo->swapchain is not as optimal as it could be, but the platform's
592 // presentation engine will still present the image correctly.
597 err = vkQueueWaitIdle(demo->queue);
598 assert(err == VK_SUCCESS);
600 vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL);
603 static void demo_prepare_buffers(struct demo *demo) {
604 VkResult U_ASSERT_ONLY err;
605 VkSwapchainKHR oldSwapchain = demo->swapchain;
607 // Check the surface capabilities and formats
608 VkSurfaceCapabilitiesKHR surfCapabilities;
609 err = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR(
610 demo->gpu, demo->surface, &surfCapabilities);
613 uint32_t presentModeCount;
614 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(
615 demo->gpu, demo->surface, &presentModeCount, NULL);
617 VkPresentModeKHR *presentModes =
618 (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR));
619 assert(presentModes);
620 err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR(
621 demo->gpu, demo->surface, &presentModeCount, presentModes);
624 VkExtent2D swapchainExtent;
625 // width and height are either both -1, or both not -1.
626 if (surfCapabilities.currentExtent.width == (uint32_t)-1) {
627 // If the surface size is undefined, the size is set to
628 // the size of the images requested.
629 swapchainExtent.width = demo->width;
630 swapchainExtent.height = demo->height;
632 // If the surface size is defined, the swap chain size must match
633 swapchainExtent = surfCapabilities.currentExtent;
634 demo->width = surfCapabilities.currentExtent.width;
635 demo->height = surfCapabilities.currentExtent.height;
638 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
640 // Determine the number of VkImage's to use in the swap chain (we desire to
641 // own only 1 image at a time, besides the images being displayed and
642 // queued for display):
643 uint32_t desiredNumberOfSwapchainImages =
644 surfCapabilities.minImageCount + 1;
645 if ((surfCapabilities.maxImageCount > 0) &&
646 (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) {
647 // Application must settle for fewer images than desired:
648 desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount;
651 VkSurfaceTransformFlagsKHR preTransform;
652 if (surfCapabilities.supportedTransforms &
653 VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
654 preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
656 preTransform = surfCapabilities.currentTransform;
659 const VkSwapchainCreateInfoKHR swapchain = {
660 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
662 .surface = demo->surface,
663 .minImageCount = desiredNumberOfSwapchainImages,
664 .imageFormat = demo->format,
665 .imageColorSpace = demo->color_space,
668 .width = swapchainExtent.width, .height = swapchainExtent.height,
670 .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
671 .preTransform = preTransform,
672 .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
673 .imageArrayLayers = 1,
674 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
675 .queueFamilyIndexCount = 0,
676 .pQueueFamilyIndices = NULL,
677 .presentMode = swapchainPresentMode,
678 .oldSwapchain = oldSwapchain,
683 err = demo->fpCreateSwapchainKHR(demo->device, &swapchain, NULL,
687 // If we just re-created an existing swapchain, we should destroy the old
688 // swapchain at this point.
689 // Note: destroying the swapchain also cleans up all its associated
690 // presentable images once the platform is done with them.
691 if (oldSwapchain != VK_NULL_HANDLE) {
692 demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL);
695 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain,
696 &demo->swapchainImageCount, NULL);
699 VkImage *swapchainImages =
700 (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage));
701 assert(swapchainImages);
702 err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain,
703 &demo->swapchainImageCount,
707 demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) *
708 demo->swapchainImageCount);
709 assert(demo->buffers);
711 for (i = 0; i < demo->swapchainImageCount; i++) {
712 VkImageViewCreateInfo color_attachment_view = {
713 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
715 .format = demo->format,
718 .r = VK_COMPONENT_SWIZZLE_R,
719 .g = VK_COMPONENT_SWIZZLE_G,
720 .b = VK_COMPONENT_SWIZZLE_B,
721 .a = VK_COMPONENT_SWIZZLE_A,
723 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
728 .viewType = VK_IMAGE_VIEW_TYPE_2D,
732 demo->buffers[i].image = swapchainImages[i];
734 // Render loop will expect image to have been used before and in
735 // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
736 // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image
738 demo_set_image_layout(
739 demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT,
740 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
743 color_attachment_view.image = demo->buffers[i].image;
745 err = vkCreateImageView(demo->device, &color_attachment_view, NULL,
746 &demo->buffers[i].view);
750 demo->current_buffer = 0;
752 if (NULL != presentModes) {
757 static void demo_prepare_depth(struct demo *demo) {
758 const VkFormat depth_format = VK_FORMAT_D16_UNORM;
759 const VkImageCreateInfo image = {
760 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
762 .imageType = VK_IMAGE_TYPE_2D,
763 .format = depth_format,
764 .extent = {demo->width, demo->height, 1},
767 .samples = VK_SAMPLE_COUNT_1_BIT,
768 .tiling = VK_IMAGE_TILING_OPTIMAL,
769 .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
772 VkMemoryAllocateInfo mem_alloc = {
773 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
776 .memoryTypeIndex = 0,
778 VkImageViewCreateInfo view = {
779 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
781 .image = VK_NULL_HANDLE,
782 .format = depth_format,
783 .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
789 .viewType = VK_IMAGE_VIEW_TYPE_2D,
792 VkMemoryRequirements mem_reqs;
793 VkResult U_ASSERT_ONLY err;
794 bool U_ASSERT_ONLY pass;
796 demo->depth.format = depth_format;
799 err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image);
802 /* get memory requirements for this object */
803 vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs);
805 /* select memory size and type */
806 mem_alloc.allocationSize = mem_reqs.size;
807 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
808 0, /* No requirements */
809 &mem_alloc.memoryTypeIndex);
812 /* allocate memory */
813 err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->depth.mem);
818 vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0);
821 demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT,
822 VK_IMAGE_LAYOUT_UNDEFINED,
823 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
826 /* create image view */
827 view.image = demo->depth.image;
828 err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view);
833 demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors,
834 struct texture_object *tex_obj, VkImageTiling tiling,
835 VkImageUsageFlags usage, VkFlags required_props) {
836 const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
837 const int32_t tex_width = 2;
838 const int32_t tex_height = 2;
839 VkResult U_ASSERT_ONLY err;
840 bool U_ASSERT_ONLY pass;
842 tex_obj->tex_width = tex_width;
843 tex_obj->tex_height = tex_height;
845 const VkImageCreateInfo image_create_info = {
846 .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
848 .imageType = VK_IMAGE_TYPE_2D,
849 .format = tex_format,
850 .extent = {tex_width, tex_height, 1},
853 .samples = VK_SAMPLE_COUNT_1_BIT,
857 .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED
859 VkMemoryAllocateInfo mem_alloc = {
860 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
863 .memoryTypeIndex = 0,
866 VkMemoryRequirements mem_reqs;
869 vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image);
872 vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs);
874 mem_alloc.allocationSize = mem_reqs.size;
876 memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
877 required_props, &mem_alloc.memoryTypeIndex);
880 /* allocate memory */
881 err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &tex_obj->mem);
885 err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0);
888 if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
889 const VkImageSubresource subres = {
890 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
894 VkSubresourceLayout layout;
898 vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres,
901 err = vkMapMemory(demo->device, tex_obj->mem, 0,
902 mem_alloc.allocationSize, 0, &data);
905 for (y = 0; y < tex_height; y++) {
906 uint32_t *row = (uint32_t *)((char *)data + layout.rowPitch * y);
907 for (x = 0; x < tex_width; x++)
908 row[x] = tex_colors[(x & 1) ^ (y & 1)];
911 vkUnmapMemory(demo->device, tex_obj->mem);
914 tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
915 demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT,
916 VK_IMAGE_LAYOUT_PREINITIALIZED, tex_obj->imageLayout,
917 VK_ACCESS_HOST_WRITE_BIT);
918 /* setting the image layout does not reference the actual memory so no need
919 * to add a mem ref */
922 static void demo_destroy_texture_image(struct demo *demo,
923 struct texture_object *tex_obj) {
924 /* clean up staging resources */
925 vkDestroyImage(demo->device, tex_obj->image, NULL);
926 vkFreeMemory(demo->device, tex_obj->mem, NULL);
929 static void demo_prepare_textures(struct demo *demo) {
930 const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
931 VkFormatProperties props;
932 const uint32_t tex_colors[DEMO_TEXTURE_COUNT][2] = {
933 {0xffff0000, 0xff00ff00},
936 VkResult U_ASSERT_ONLY err;
938 vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props);
940 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
941 if ((props.linearTilingFeatures &
942 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) &&
943 !demo->use_staging_buffer) {
944 /* Device can texture using linear textures */
945 demo_prepare_texture_image(demo, tex_colors[i], &demo->textures[i],
946 VK_IMAGE_TILING_LINEAR,
947 VK_IMAGE_USAGE_SAMPLED_BIT,
948 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
949 } else if (props.optimalTilingFeatures &
950 VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) {
951 /* Must use staging buffer to copy linear texture to optimized */
952 struct texture_object staging_texture;
954 memset(&staging_texture, 0, sizeof(staging_texture));
955 demo_prepare_texture_image(demo, tex_colors[i], &staging_texture,
956 VK_IMAGE_TILING_LINEAR,
957 VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
958 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
960 demo_prepare_texture_image(
961 demo, tex_colors[i], &demo->textures[i],
962 VK_IMAGE_TILING_OPTIMAL,
963 (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT),
964 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
966 demo_set_image_layout(demo, staging_texture.image,
967 VK_IMAGE_ASPECT_COLOR_BIT,
968 staging_texture.imageLayout,
969 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
972 demo_set_image_layout(demo, demo->textures[i].image,
973 VK_IMAGE_ASPECT_COLOR_BIT,
974 demo->textures[i].imageLayout,
975 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
978 VkImageCopy copy_region = {
979 .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
980 .srcOffset = {0, 0, 0},
981 .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
982 .dstOffset = {0, 0, 0},
983 .extent = {staging_texture.tex_width,
984 staging_texture.tex_height, 1},
987 demo->setup_cmd, staging_texture.image,
988 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image,
989 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
991 demo_set_image_layout(demo, demo->textures[i].image,
992 VK_IMAGE_ASPECT_COLOR_BIT,
993 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
994 demo->textures[i].imageLayout,
997 demo_flush_init_cmd(demo);
999 demo_destroy_texture_image(demo, &staging_texture);
1001 /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */
1002 assert(!"No support for B8G8R8A8_UNORM as texture image format");
1005 const VkSamplerCreateInfo sampler = {
1006 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1008 .magFilter = VK_FILTER_NEAREST,
1009 .minFilter = VK_FILTER_NEAREST,
1010 .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
1011 .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
1012 .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
1013 .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
1015 .anisotropyEnable = VK_FALSE,
1017 .compareOp = VK_COMPARE_OP_NEVER,
1020 .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
1021 .unnormalizedCoordinates = VK_FALSE,
1023 VkImageViewCreateInfo view = {
1024 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
1026 .image = VK_NULL_HANDLE,
1027 .viewType = VK_IMAGE_VIEW_TYPE_2D,
1028 .format = tex_format,
1031 VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
1032 VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
1034 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
1038 /* create sampler */
1039 err = vkCreateSampler(demo->device, &sampler, NULL,
1040 &demo->textures[i].sampler);
1043 /* create image view */
1044 view.image = demo->textures[i].image;
1045 err = vkCreateImageView(demo->device, &view, NULL,
1046 &demo->textures[i].view);
1051 static void demo_prepare_vertices(struct demo *demo) {
1053 const float vb[3][5] = {
1054 /* position texcoord */
1055 { -1.0f, -1.0f, 0.25f, 0.0f, 0.0f },
1056 { 1.0f, -1.0f, 0.25f, 1.0f, 0.0f },
1057 { 0.0f, 1.0f, 1.0f, 0.5f, 1.0f },
1060 const VkBufferCreateInfo buf_info = {
1061 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
1064 .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1067 VkMemoryAllocateInfo mem_alloc = {
1068 .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
1070 .allocationSize = 0,
1071 .memoryTypeIndex = 0,
1073 VkMemoryRequirements mem_reqs;
1074 VkResult U_ASSERT_ONLY err;
1075 bool U_ASSERT_ONLY pass;
1078 memset(&demo->vertices, 0, sizeof(demo->vertices));
1080 err = vkCreateBuffer(demo->device, &buf_info, NULL, &demo->vertices.buf);
1083 vkGetBufferMemoryRequirements(demo->device, demo->vertices.buf, &mem_reqs);
1086 mem_alloc.allocationSize = mem_reqs.size;
1087 pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits,
1088 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
1089 &mem_alloc.memoryTypeIndex);
1092 err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->vertices.mem);
1095 err = vkMapMemory(demo->device, demo->vertices.mem, 0,
1096 mem_alloc.allocationSize, 0, &data);
1099 memcpy(data, vb, sizeof(vb));
1101 vkUnmapMemory(demo->device, demo->vertices.mem);
1103 err = vkBindBufferMemory(demo->device, demo->vertices.buf,
1104 demo->vertices.mem, 0);
1107 demo->vertices.vi.sType =
1108 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1109 demo->vertices.vi.pNext = NULL;
1110 demo->vertices.vi.vertexBindingDescriptionCount = 1;
1111 demo->vertices.vi.pVertexBindingDescriptions = demo->vertices.vi_bindings;
1112 demo->vertices.vi.vertexAttributeDescriptionCount = 2;
1113 demo->vertices.vi.pVertexAttributeDescriptions = demo->vertices.vi_attrs;
1115 demo->vertices.vi_bindings[0].binding = VERTEX_BUFFER_BIND_ID;
1116 demo->vertices.vi_bindings[0].stride = sizeof(vb[0]);
1117 demo->vertices.vi_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1119 demo->vertices.vi_attrs[0].binding = VERTEX_BUFFER_BIND_ID;
1120 demo->vertices.vi_attrs[0].location = 0;
1121 demo->vertices.vi_attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT;
1122 demo->vertices.vi_attrs[0].offset = 0;
1124 demo->vertices.vi_attrs[1].binding = VERTEX_BUFFER_BIND_ID;
1125 demo->vertices.vi_attrs[1].location = 1;
1126 demo->vertices.vi_attrs[1].format = VK_FORMAT_R32G32_SFLOAT;
1127 demo->vertices.vi_attrs[1].offset = sizeof(float) * 3;
1130 static void demo_prepare_descriptor_layout(struct demo *demo) {
1131 const VkDescriptorSetLayoutBinding layout_binding = {
1133 .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1134 .descriptorCount = DEMO_TEXTURE_COUNT,
1135 .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
1136 .pImmutableSamplers = NULL,
1138 const VkDescriptorSetLayoutCreateInfo descriptor_layout = {
1139 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
1142 .pBindings = &layout_binding,
1144 VkResult U_ASSERT_ONLY err;
1146 err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL,
1147 &demo->desc_layout);
1150 const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
1151 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
1153 .setLayoutCount = 1,
1154 .pSetLayouts = &demo->desc_layout,
1157 err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL,
1158 &demo->pipeline_layout);
1162 static void demo_prepare_render_pass(struct demo *demo) {
1163 const VkAttachmentDescription attachments[2] = {
1166 .format = demo->format,
1167 .samples = VK_SAMPLE_COUNT_1_BIT,
1168 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1169 .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
1170 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1171 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1172 .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1173 .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1177 .format = demo->depth.format,
1178 .samples = VK_SAMPLE_COUNT_1_BIT,
1179 .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
1180 .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1181 .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
1182 .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
1184 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1186 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1189 const VkAttachmentReference color_reference = {
1190 .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1192 const VkAttachmentReference depth_reference = {
1194 .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
1196 const VkSubpassDescription subpass = {
1197 .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
1199 .inputAttachmentCount = 0,
1200 .pInputAttachments = NULL,
1201 .colorAttachmentCount = 1,
1202 .pColorAttachments = &color_reference,
1203 .pResolveAttachments = NULL,
1204 .pDepthStencilAttachment = &depth_reference,
1205 .preserveAttachmentCount = 0,
1206 .pPreserveAttachments = NULL,
1208 const VkRenderPassCreateInfo rp_info = {
1209 .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
1211 .attachmentCount = 2,
1212 .pAttachments = attachments,
1214 .pSubpasses = &subpass,
1215 .dependencyCount = 0,
1216 .pDependencies = NULL,
1218 VkResult U_ASSERT_ONLY err;
1220 err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass);
1224 static VkShaderModule
1225 demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) {
1226 VkShaderModuleCreateInfo moduleCreateInfo;
1227 VkShaderModule module;
1228 VkResult U_ASSERT_ONLY err;
1230 moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
1231 moduleCreateInfo.pNext = NULL;
1233 moduleCreateInfo.codeSize = size;
1234 moduleCreateInfo.pCode = code;
1235 moduleCreateInfo.flags = 0;
1236 err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module);
1242 char *demo_read_spv(const char *filename, size_t *psize) {
1247 FILE *fp = fopen(filename, "rb");
1251 fseek(fp, 0L, SEEK_END);
1254 fseek(fp, 0L, SEEK_SET);
1256 shader_code = malloc(size);
1257 retVal = fread(shader_code, size, 1, fp);
1267 static VkShaderModule demo_prepare_vs(struct demo *demo) {
1268 void *vertShaderCode;
1271 vertShaderCode = demo_read_spv("tri-vert.spv", &size);
1273 demo->vert_shader_module =
1274 demo_prepare_shader_module(demo, vertShaderCode, size);
1276 free(vertShaderCode);
1278 return demo->vert_shader_module;
1281 static VkShaderModule demo_prepare_fs(struct demo *demo) {
1282 void *fragShaderCode;
1285 fragShaderCode = demo_read_spv("tri-frag.spv", &size);
1287 demo->frag_shader_module =
1288 demo_prepare_shader_module(demo, fragShaderCode, size);
1290 free(fragShaderCode);
1292 return demo->frag_shader_module;
1295 static void demo_prepare_pipeline(struct demo *demo) {
1296 VkGraphicsPipelineCreateInfo pipeline;
1297 VkPipelineCacheCreateInfo pipelineCache;
1299 VkPipelineVertexInputStateCreateInfo vi;
1300 VkPipelineInputAssemblyStateCreateInfo ia;
1301 VkPipelineRasterizationStateCreateInfo rs;
1302 VkPipelineColorBlendStateCreateInfo cb;
1303 VkPipelineDepthStencilStateCreateInfo ds;
1304 VkPipelineViewportStateCreateInfo vp;
1305 VkPipelineMultisampleStateCreateInfo ms;
1306 VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE];
1307 VkPipelineDynamicStateCreateInfo dynamicState;
1309 VkResult U_ASSERT_ONLY err;
1311 memset(dynamicStateEnables, 0, sizeof dynamicStateEnables);
1312 memset(&dynamicState, 0, sizeof dynamicState);
1313 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1314 dynamicState.pDynamicStates = dynamicStateEnables;
1316 memset(&pipeline, 0, sizeof(pipeline));
1317 pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1318 pipeline.layout = demo->pipeline_layout;
1320 vi = demo->vertices.vi;
1322 memset(&ia, 0, sizeof(ia));
1323 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1324 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1326 memset(&rs, 0, sizeof(rs));
1327 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1328 rs.polygonMode = VK_POLYGON_MODE_FILL;
1329 rs.cullMode = VK_CULL_MODE_BACK_BIT;
1330 rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
1331 rs.depthClampEnable = VK_FALSE;
1332 rs.rasterizerDiscardEnable = VK_FALSE;
1333 rs.depthBiasEnable = VK_FALSE;
1335 memset(&cb, 0, sizeof(cb));
1336 cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1337 VkPipelineColorBlendAttachmentState att_state[1];
1338 memset(att_state, 0, sizeof(att_state));
1339 att_state[0].colorWriteMask = 0xf;
1340 att_state[0].blendEnable = VK_FALSE;
1341 cb.attachmentCount = 1;
1342 cb.pAttachments = att_state;
1344 memset(&vp, 0, sizeof(vp));
1345 vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1346 vp.viewportCount = 1;
1347 dynamicStateEnables[dynamicState.dynamicStateCount++] =
1348 VK_DYNAMIC_STATE_VIEWPORT;
1349 vp.scissorCount = 1;
1350 dynamicStateEnables[dynamicState.dynamicStateCount++] =
1351 VK_DYNAMIC_STATE_SCISSOR;
1353 memset(&ds, 0, sizeof(ds));
1354 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1355 ds.depthTestEnable = VK_TRUE;
1356 ds.depthWriteEnable = VK_TRUE;
1357 ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
1358 ds.depthBoundsTestEnable = VK_FALSE;
1359 ds.back.failOp = VK_STENCIL_OP_KEEP;
1360 ds.back.passOp = VK_STENCIL_OP_KEEP;
1361 ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
1362 ds.stencilTestEnable = VK_FALSE;
1365 memset(&ms, 0, sizeof(ms));
1366 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1367 ms.pSampleMask = NULL;
1368 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
1370 // Two stages: vs and fs
1371 pipeline.stageCount = 2;
1372 VkPipelineShaderStageCreateInfo shaderStages[2];
1373 memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo));
1375 shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1376 shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
1377 shaderStages[0].module = demo_prepare_vs(demo);
1378 shaderStages[0].pName = "main";
1380 shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1381 shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1382 shaderStages[1].module = demo_prepare_fs(demo);
1383 shaderStages[1].pName = "main";
1385 pipeline.pVertexInputState = &vi;
1386 pipeline.pInputAssemblyState = &ia;
1387 pipeline.pRasterizationState = &rs;
1388 pipeline.pColorBlendState = &cb;
1389 pipeline.pMultisampleState = &ms;
1390 pipeline.pViewportState = &vp;
1391 pipeline.pDepthStencilState = &ds;
1392 pipeline.pStages = shaderStages;
1393 pipeline.renderPass = demo->render_pass;
1394 pipeline.pDynamicState = &dynamicState;
1396 memset(&pipelineCache, 0, sizeof(pipelineCache));
1397 pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
1399 err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL,
1400 &demo->pipelineCache);
1402 err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1,
1403 &pipeline, NULL, &demo->pipeline);
1406 vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL);
1408 vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL);
1409 vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL);
1412 static void demo_prepare_descriptor_pool(struct demo *demo) {
1413 const VkDescriptorPoolSize type_count = {
1414 .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1415 .descriptorCount = DEMO_TEXTURE_COUNT,
1417 const VkDescriptorPoolCreateInfo descriptor_pool = {
1418 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
1422 .pPoolSizes = &type_count,
1424 VkResult U_ASSERT_ONLY err;
1426 err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL,
1431 static void demo_prepare_descriptor_set(struct demo *demo) {
1432 VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT];
1433 VkWriteDescriptorSet write;
1434 VkResult U_ASSERT_ONLY err;
1437 VkDescriptorSetAllocateInfo alloc_info = {
1438 .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
1440 .descriptorPool = demo->desc_pool,
1441 .descriptorSetCount = 1,
1442 .pSetLayouts = &demo->desc_layout};
1443 err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set);
1446 memset(&tex_descs, 0, sizeof(tex_descs));
1447 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
1448 tex_descs[i].sampler = demo->textures[i].sampler;
1449 tex_descs[i].imageView = demo->textures[i].view;
1450 tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
1453 memset(&write, 0, sizeof(write));
1454 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1455 write.dstSet = demo->desc_set;
1456 write.descriptorCount = DEMO_TEXTURE_COUNT;
1457 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1458 write.pImageInfo = tex_descs;
1460 vkUpdateDescriptorSets(demo->device, 1, &write, 0, NULL);
1463 static void demo_prepare_framebuffers(struct demo *demo) {
1464 VkImageView attachments[2];
1465 attachments[1] = demo->depth.view;
1467 const VkFramebufferCreateInfo fb_info = {
1468 .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
1470 .renderPass = demo->render_pass,
1471 .attachmentCount = 2,
1472 .pAttachments = attachments,
1473 .width = demo->width,
1474 .height = demo->height,
1477 VkResult U_ASSERT_ONLY err;
1480 demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount *
1481 sizeof(VkFramebuffer));
1482 assert(demo->framebuffers);
1484 for (i = 0; i < demo->swapchainImageCount; i++) {
1485 attachments[0] = demo->buffers[i].view;
1486 err = vkCreateFramebuffer(demo->device, &fb_info, NULL,
1487 &demo->framebuffers[i]);
1492 static void demo_prepare(struct demo *demo) {
1493 VkResult U_ASSERT_ONLY err;
1495 const VkCommandPoolCreateInfo cmd_pool_info = {
1496 .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
1498 .queueFamilyIndex = demo->graphics_queue_node_index,
1499 .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
1501 err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL,
1505 const VkCommandBufferAllocateInfo cmd = {
1506 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
1508 .commandPool = demo->cmd_pool,
1509 .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
1510 .commandBufferCount = 1,
1512 err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->draw_cmd);
1515 demo_prepare_buffers(demo);
1516 demo_prepare_depth(demo);
1517 demo_prepare_textures(demo);
1518 demo_prepare_vertices(demo);
1519 demo_prepare_descriptor_layout(demo);
1520 demo_prepare_render_pass(demo);
1521 demo_prepare_pipeline(demo);
1523 demo_prepare_descriptor_pool(demo);
1524 demo_prepare_descriptor_set(demo);
1526 demo_prepare_framebuffers(demo);
1528 demo->prepared = true;
1532 static void demo_run(struct demo *demo) {
1533 if (!demo->prepared)
1537 if (demo->depthStencil > 0.99f)
1538 demo->depthIncrement = -0.001f;
1539 if (demo->depthStencil < 0.8f)
1540 demo->depthIncrement = 0.001f;
1542 demo->depthStencil += demo->depthIncrement;
1545 if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) {
1546 PostQuitMessage(validation_error);
1550 // On MS-Windows, make this a global, so it's available to WndProc()
1553 // MS-Windows event handling function:
1554 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
1555 char tmp_str[] = APP_LONG_NAME;
1561 PostQuitMessage(validation_error);
1564 if (demo.prepared) {
1569 // Resize the application to the new window size, except when
1570 // it was minimized. Vulkan doesn't support images or swapchains
1571 // with width=0 and height=0.
1572 if (wParam != SIZE_MINIMIZED) {
1573 demo.width = lParam & 0xffff;
1574 demo.height = lParam & 0xffff0000 >> 16;
1581 return (DefWindowProc(hWnd, uMsg, wParam, lParam));
1584 static void demo_create_window(struct demo *demo) {
1585 WNDCLASSEX win_class;
1587 // Initialize the window class structure:
1588 win_class.cbSize = sizeof(WNDCLASSEX);
1589 win_class.style = CS_HREDRAW | CS_VREDRAW;
1590 win_class.lpfnWndProc = WndProc;
1591 win_class.cbClsExtra = 0;
1592 win_class.cbWndExtra = 0;
1593 win_class.hInstance = demo->connection; // hInstance
1594 win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
1595 win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
1596 win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
1597 win_class.lpszMenuName = NULL;
1598 win_class.lpszClassName = demo->name;
1599 win_class.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
1600 // Register window class:
1601 if (!RegisterClassEx(&win_class)) {
1602 // It didn't work, so try to give a useful error:
1603 printf("Unexpected error trying to start the application!\n");
1607 // Create window with the registered class:
1608 RECT wr = {0, 0, demo->width, demo->height};
1609 AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
1610 demo->window = CreateWindowEx(0,
1611 demo->name, // class name
1612 demo->name, // app name
1613 WS_OVERLAPPEDWINDOW | // window style
1614 WS_VISIBLE | WS_SYSMENU,
1615 100, 100, // x/y coords
1616 wr.right - wr.left, // width
1617 wr.bottom - wr.top, // height
1618 NULL, // handle to parent
1619 NULL, // handle to menu
1620 demo->connection, // hInstance
1621 NULL); // no extra parameters
1622 if (!demo->window) {
1623 // It didn't work, so try to give a useful error:
1624 printf("Cannot create a window in which to draw!\n");
1631 static void demo_run(struct demo *demo) {
1633 while (!demo->quit) {
1636 if (demo->depthStencil > 0.99f)
1637 demo->depthIncrement = -0.001f;
1638 if (demo->depthStencil < 0.8f)
1639 demo->depthIncrement = 0.001f;
1641 demo->depthStencil += demo->depthIncrement;
1643 // Wait for work to finish before updating MVP.
1644 vkDeviceWaitIdle(demo->device);
1646 if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount)
1653 handle_ping(void *data, struct wl_shell_surface *shell_surface,
1655 printf("%s\n", __func__);
1656 wl_shell_surface_pong(shell_surface, serial);
1660 handle_configure(void *data, struct wl_shell_surface *shell_surface,
1661 uint32_t edges, int32_t width, int32_t height) {
1662 printf("%s, (%d, %d)\n", __func__, width, height);
1666 handle_popup_done(void *data, struct wl_shell_surface *shell_surface) {
1669 static const struct wl_shell_surface_listener shell_surface_listener = {
1675 static void demo_create_window(struct demo *demo) {
1676 demo->wl_surface = wl_compositor_create_surface(demo->compositor);
1678 demo->shell_surface = wl_shell_get_shell_surface(demo->shell, demo->wl_surface);
1680 if (demo->shell_surface) {
1681 wl_shell_surface_add_listener(demo->shell_surface,
1682 &shell_surface_listener, demo);
1683 wl_shell_surface_set_toplevel(demo->shell_surface);
1686 wl_surface_set_user_data(demo->wl_surface, demo);
1687 wl_shell_surface_set_title(demo->shell_surface, APP_SHORT_NAME);
1692 * Return 1 (true) if all layer names specified in check_names
1693 * can be found in given layer properties.
1695 static VkBool32 demo_check_layers(uint32_t check_count, char **check_names,
1696 uint32_t layer_count,
1697 VkLayerProperties *layers) {
1699 for (i = 0; i < check_count; i++) {
1701 for (j = 0; j < layer_count; j++) {
1702 if (!strcmp(check_names[i], layers[j].layerName)) {
1708 fprintf(stderr, "Cannot find layer: %s\n", check_names[i]);
1715 static void demo_init_vk(struct demo *demo) {
1717 uint32_t instance_extension_count = 0;
1718 uint32_t instance_layer_count = 0;
1719 uint32_t device_validation_layer_count = 0;
1720 char **instance_validation_layers = NULL;
1722 demo->enabled_extension_count = 0;
1723 demo->enabled_layer_count = 0;
1725 char *instance_validation_layers_alt1[] = {
1726 "VK_LAYER_LUNARG_standard_validation"
1729 char *instance_validation_layers_alt2[] = {
1730 "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation",
1731 "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_object_tracker",
1732 "VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation",
1733 "VK_LAYER_LUNARG_swapchain", "VK_LAYER_GOOGLE_unique_objects"
1736 /* Look for validation layers */
1737 VkBool32 validation_found = 0;
1738 if (demo->validate) {
1740 err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL);
1743 instance_validation_layers = instance_validation_layers_alt1;
1744 if (instance_layer_count > 0) {
1745 VkLayerProperties *instance_layers =
1746 malloc(sizeof (VkLayerProperties) * instance_layer_count);
1747 err = vkEnumerateInstanceLayerProperties(&instance_layer_count,
1752 validation_found = demo_check_layers(
1753 ARRAY_SIZE(instance_validation_layers_alt1),
1754 instance_validation_layers, instance_layer_count,
1756 if (validation_found) {
1757 demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt1);
1758 demo->device_validation_layers[0] = "VK_LAYER_LUNARG_standard_validation";
1759 device_validation_layer_count = 1;
1761 // use alternative set of validation layers
1762 instance_validation_layers = instance_validation_layers_alt2;
1763 demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers_alt2);
1764 validation_found = demo_check_layers(
1765 ARRAY_SIZE(instance_validation_layers_alt2),
1766 instance_validation_layers, instance_layer_count,
1768 device_validation_layer_count =
1769 ARRAY_SIZE(instance_validation_layers_alt2);
1770 for (i = 0; i < device_validation_layer_count; i++) {
1771 demo->device_validation_layers[i] =
1772 instance_validation_layers[i];
1775 free(instance_layers);
1778 if (!validation_found) {
1779 ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find"
1780 "required validation layer.\n\n"
1781 "Please look at the Getting Started guide for additional "
1783 "vkCreateInstance Failure");
1787 /* Look for instance extensions */
1788 VkBool32 surfaceExtFound = 0;
1789 VkBool32 platformSurfaceExtFound = 0;
1790 memset(demo->extension_names, 0, sizeof(demo->extension_names));
1792 err = vkEnumerateInstanceExtensionProperties(
1793 NULL, &instance_extension_count, NULL);
1796 if (instance_extension_count > 0) {
1798 VkExtensionProperties *instance_extensions =
1799 malloc(sizeof(VkExtensionProperties) * instance_extension_count);
1800 err = vkEnumerateInstanceExtensionProperties(
1801 NULL, &instance_extension_count, instance_extensions);
1803 for (i = 0; i < instance_extension_count; i++) {
1804 if (!strcmp(VK_KHR_SURFACE_EXTENSION_NAME,
1805 instance_extensions[i].extensionName)) {
1806 surfaceExtFound = 1;
1807 demo->extension_names[demo->enabled_extension_count++] =
1808 VK_KHR_SURFACE_EXTENSION_NAME;
1811 if (!strcmp(VK_KHR_WIN32_SURFACE_EXTENSION_NAME,
1812 instance_extensions[i].extensionName)) {
1813 platformSurfaceExtFound = 1;
1814 demo->extension_names[demo->enabled_extension_count++] =
1815 VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
1818 if (!strcmp(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
1819 instance_extensions[i].extensionName)) {
1820 platformSurfaceExtFound = 1;
1821 demo->extension_names[demo->enabled_extension_count++] =
1822 VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME;
1825 if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
1826 instance_extensions[i].extensionName)) {
1827 if (demo->validate) {
1828 demo->extension_names[demo->enabled_extension_count++] =
1829 VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
1832 assert(demo->enabled_extension_count < 64);
1835 free(instance_extensions);
1838 if (!surfaceExtFound) {
1839 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
1840 "the " VK_KHR_SURFACE_EXTENSION_NAME
1841 " extension.\n\nDo you have a compatible "
1842 "Vulkan installable client driver (ICD) installed?\nPlease "
1843 "look at the Getting Started guide for additional "
1845 "vkCreateInstance Failure");
1847 if (!platformSurfaceExtFound) {
1849 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
1850 "the " VK_KHR_WIN32_SURFACE_EXTENSION_NAME
1851 " extension.\n\nDo you have a compatible "
1852 "Vulkan installable client driver (ICD) installed?\nPlease "
1853 "look at the Getting Started guide for additional "
1855 "vkCreateInstance Failure");
1857 ERR_EXIT("vkEnumerateInstanceExtensionProperties failed to find "
1858 "the " VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME
1859 " extension.\n\nDo you have a compatible "
1860 "Vulkan installable client driver (ICD) installed?\nPlease "
1861 "look at the Getting Started guide for additional "
1863 "vkCreateInstance Failure");
1866 const VkApplicationInfo app = {
1867 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
1869 .pApplicationName = APP_SHORT_NAME,
1870 .applicationVersion = 0,
1871 .pEngineName = APP_SHORT_NAME,
1873 .apiVersion = VK_API_VERSION_1_0,
1875 VkInstanceCreateInfo inst_info = {
1876 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
1878 .pApplicationInfo = &app,
1879 .enabledLayerCount = demo->enabled_layer_count,
1880 .ppEnabledLayerNames = (const char *const *)instance_validation_layers,
1881 .enabledExtensionCount = demo->enabled_extension_count,
1882 .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
1887 err = vkCreateInstance(&inst_info, NULL, &demo->inst);
1888 if (err == VK_ERROR_INCOMPATIBLE_DRIVER) {
1889 ERR_EXIT("Cannot find a compatible Vulkan installable client driver "
1890 "(ICD).\n\nPlease look at the Getting Started guide for "
1891 "additional information.\n",
1892 "vkCreateInstance Failure");
1893 } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) {
1894 ERR_EXIT("Cannot find a specified extension library"
1895 ".\nMake sure your layers path is set appropriately\n",
1896 "vkCreateInstance Failure");
1898 ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan "
1899 "installable client driver (ICD) installed?\nPlease look at "
1900 "the Getting Started guide for additional information.\n",
1901 "vkCreateInstance Failure");
1904 /* Make initial call to query gpu_count, then second call for gpu info*/
1905 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
1906 assert(!err && gpu_count > 0);
1908 if (gpu_count > 0) {
1909 VkPhysicalDevice *physical_devices =
1910 malloc(sizeof(VkPhysicalDevice) * gpu_count);
1911 err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count,
1914 /* For tri demo we just grab the first physical device */
1915 demo->gpu = physical_devices[0];
1916 free(physical_devices);
1918 ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices."
1919 "\n\nDo you have a compatible Vulkan installable client"
1920 " driver (ICD) installed?\nPlease look at the Getting Started"
1921 " guide for additional information.\n",
1922 "vkEnumeratePhysicalDevices Failure");
1925 /* Look for validation layers */
1926 if (demo->validate) {
1927 validation_found = 0;
1928 demo->enabled_layer_count = 0;
1929 uint32_t device_layer_count = 0;
1931 vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL);
1934 if (device_layer_count > 0) {
1935 VkLayerProperties *device_layers =
1936 malloc(sizeof (VkLayerProperties) * device_layer_count);
1937 err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count,
1942 validation_found = demo_check_layers(device_validation_layer_count,
1943 demo->device_validation_layers,
1946 demo->enabled_layer_count = device_validation_layer_count;
1948 free(device_layers);
1951 if (!validation_found) {
1952 ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find "
1953 "a required validation layer.\n\n"
1954 "Please look at the Getting Started guide for additional "
1956 "vkCreateDevice Failure");
1960 /* Look for device extensions */
1961 uint32_t device_extension_count = 0;
1962 VkBool32 swapchainExtFound = 0;
1963 demo->enabled_extension_count = 0;
1964 memset(demo->extension_names, 0, sizeof(demo->extension_names));
1966 err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL,
1967 &device_extension_count, NULL);
1970 if (device_extension_count > 0) {
1972 VkExtensionProperties *device_extensions =
1973 malloc(sizeof(VkExtensionProperties) * device_extension_count);
1974 err = vkEnumerateDeviceExtensionProperties(
1975 demo->gpu, NULL, &device_extension_count, device_extensions);
1978 for (i = 0; i < device_extension_count; i++) {
1979 if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME,
1980 device_extensions[i].extensionName)) {
1981 swapchainExtFound = 1;
1982 demo->extension_names[demo->enabled_extension_count++] =
1983 VK_KHR_SWAPCHAIN_EXTENSION_NAME;
1985 assert(demo->enabled_extension_count < 64);
1988 free(device_extensions);
1991 if (!swapchainExtFound) {
1992 ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find "
1993 "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME
1994 " extension.\n\nDo you have a compatible "
1995 "Vulkan installable client driver (ICD) installed?\nPlease "
1996 "look at the Getting Started guide for additional "
1998 "vkCreateInstance Failure");
2001 if (demo->validate) {
2002 demo->CreateDebugReportCallback =
2003 (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(
2004 demo->inst, "vkCreateDebugReportCallbackEXT");
2005 demo->DestroyDebugReportCallback =
2006 (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(
2007 demo->inst, "vkDestroyDebugReportCallbackEXT");
2008 if (!demo->CreateDebugReportCallback) {
2010 "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n",
2011 "vkGetProcAddr Failure");
2013 if (!demo->DestroyDebugReportCallback) {
2015 "GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT\n",
2016 "vkGetProcAddr Failure");
2018 demo->DebugReportMessage =
2019 (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(
2020 demo->inst, "vkDebugReportMessageEXT");
2021 if (!demo->DebugReportMessage) {
2022 ERR_EXIT("GetProcAddr: Unable to find vkDebugReportMessageEXT\n",
2023 "vkGetProcAddr Failure");
2026 VkDebugReportCallbackCreateInfoEXT dbgCreateInfo;
2027 dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
2028 dbgCreateInfo.flags =
2029 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
2030 dbgCreateInfo.pfnCallback = demo->use_break ? BreakCallback : dbgFunc;
2031 dbgCreateInfo.pUserData = NULL;
2032 dbgCreateInfo.pNext = NULL;
2033 err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL,
2034 &demo->msg_callback);
2038 case VK_ERROR_OUT_OF_HOST_MEMORY:
2039 ERR_EXIT("CreateDebugReportCallback: out of host memory\n",
2040 "CreateDebugReportCallback Failure");
2043 ERR_EXIT("CreateDebugReportCallback: unknown failure\n",
2044 "CreateDebugReportCallback Failure");
2049 // Having these GIPA queries of device extension entry points both
2050 // BEFORE and AFTER vkCreateDevice is a good test for the loader
2051 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR);
2052 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR);
2053 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR);
2054 GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR);
2055 GET_INSTANCE_PROC_ADDR(demo->inst, CreateSwapchainKHR);
2056 GET_INSTANCE_PROC_ADDR(demo->inst, DestroySwapchainKHR);
2057 GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR);
2058 GET_INSTANCE_PROC_ADDR(demo->inst, AcquireNextImageKHR);
2059 GET_INSTANCE_PROC_ADDR(demo->inst, QueuePresentKHR);
2061 vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props);
2063 // Query with NULL data to get count
2064 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
2067 demo->queue_props = (VkQueueFamilyProperties *)malloc(
2068 demo->queue_count * sizeof(VkQueueFamilyProperties));
2069 vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count,
2071 assert(demo->queue_count >= 1);
2073 VkPhysicalDeviceFeatures features;
2074 vkGetPhysicalDeviceFeatures(demo->gpu, &features);
2076 if (!features.shaderClipDistance) {
2077 ERR_EXIT("Required device feature `shaderClipDistance` not supported\n",
2078 "GetPhysicalDeviceFeatures failure");
2081 // Graphics queue and MemMgr queue can be separate.
2082 // TODO: Add support for separate queues, including synchronization,
2083 // and appropriate tracking for QueueSubmit
2086 static void demo_init_device(struct demo *demo) {
2087 VkResult U_ASSERT_ONLY err;
2089 float queue_priorities[1] = {0.0};
2090 const VkDeviceQueueCreateInfo queue = {
2091 .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
2093 .queueFamilyIndex = demo->graphics_queue_node_index,
2095 .pQueuePriorities = queue_priorities};
2097 VkPhysicalDeviceFeatures features = {
2098 .shaderClipDistance = VK_TRUE,
2101 VkDeviceCreateInfo device = {
2102 .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
2104 .queueCreateInfoCount = 1,
2105 .pQueueCreateInfos = &queue,
2106 .enabledLayerCount = demo->enabled_layer_count,
2107 .ppEnabledLayerNames =
2108 (const char *const *)((demo->validate)
2109 ? demo->device_validation_layers
2111 .enabledExtensionCount = demo->enabled_extension_count,
2112 .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
2113 .pEnabledFeatures = &features,
2116 err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device);
2120 static void demo_init_vk_swapchain(struct demo *demo) {
2121 VkResult U_ASSERT_ONLY err;
2124 // Create a WSI surface for the window:
2126 VkWin32SurfaceCreateInfoKHR createInfo;
2127 createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
2128 createInfo.pNext = NULL;
2129 createInfo.flags = 0;
2130 createInfo.hinstance = demo->connection;
2131 createInfo.hwnd = demo->window;
2134 vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
2137 VkWaylandSurfaceCreateInfoKHR createInfo;
2138 createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
2139 createInfo.pNext = NULL;
2140 createInfo.flags = 0;
2141 createInfo.display = demo->display;
2142 createInfo.surface = demo->wl_surface;
2144 err = vkCreateWaylandSurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
2147 // Iterate over each queue to learn whether it supports presenting:
2148 VkBool32 *supportsPresent =
2149 (VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32));
2150 for (i = 0; i < demo->queue_count; i++) {
2151 demo->fpGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface,
2152 &supportsPresent[i]);
2155 // Search for a graphics and a present queue in the array of queue
2156 // families, try to find one that supports both
2157 uint32_t graphicsQueueNodeIndex = UINT32_MAX;
2158 uint32_t presentQueueNodeIndex = UINT32_MAX;
2159 for (i = 0; i < demo->queue_count; i++) {
2160 if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) {
2161 if (graphicsQueueNodeIndex == UINT32_MAX) {
2162 graphicsQueueNodeIndex = i;
2165 if (supportsPresent[i] == VK_TRUE) {
2166 graphicsQueueNodeIndex = i;
2167 presentQueueNodeIndex = i;
2172 if (presentQueueNodeIndex == UINT32_MAX) {
2173 // If didn't find a queue that supports both graphics and present, then
2174 // find a separate present queue.
2176 for (i = 0; i < demo->queue_count; ++i) {
2177 if (supportsPresent[i] == VK_TRUE) {
2178 presentQueueNodeIndex = i;
2183 free(supportsPresent);
2185 // Generate error if could not find both a graphics and a present queue
2186 if (graphicsQueueNodeIndex == UINT32_MAX ||
2187 presentQueueNodeIndex == UINT32_MAX) {
2188 ERR_EXIT("Could not find a graphics and a present queue\n",
2189 "Swapchain Initialization Failure");
2192 // TODO: Add support for separate queues, including presentation,
2193 // synchronization, and appropriate tracking for QueueSubmit.
2194 // NOTE: While it is possible for an application to use a separate graphics
2195 // and a present queues, this demo program assumes it is only using
2197 if (graphicsQueueNodeIndex != presentQueueNodeIndex) {
2198 ERR_EXIT("Could not find a common graphics and a present queue\n",
2199 "Swapchain Initialization Failure");
2202 demo->graphics_queue_node_index = graphicsQueueNodeIndex;
2204 demo_init_device(demo);
2206 vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0,
2209 // Get the list of VkFormat's that are supported:
2210 uint32_t formatCount;
2211 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
2212 &formatCount, NULL);
2214 VkSurfaceFormatKHR *surfFormats =
2215 (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
2216 err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface,
2217 &formatCount, surfFormats);
2219 // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
2220 // the surface has no preferred format. Otherwise, at least one
2221 // supported format will be returned.
2222 if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
2223 demo->format = VK_FORMAT_B8G8R8A8_UNORM;
2225 assert(formatCount >= 1);
2226 demo->format = surfFormats[0].format;
2228 demo->color_space = surfFormats[0].colorSpace;
2233 // Get Memory information and properties
2234 vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
2238 registry_handle_global(void *data, struct wl_registry *registry,
2239 uint32_t name, const char *interface, uint32_t version) {
2240 struct demo *d = data;
2242 printf("%s, interface: %s\n", __func__, interface);
2244 if (strcmp(interface, "wl_compositor") == 0) {
2246 wl_registry_bind(registry, name,
2247 &wl_compositor_interface, 1);
2248 } else if (strcmp(interface, "wl_shm") == 0) {
2249 d->shm = wl_registry_bind(registry, name,
2250 &wl_shm_interface, 1);
2251 } else if (strcmp(interface, "wl_shell") == 0) {
2253 wl_registry_bind(registry, name,
2254 &wl_shell_interface, 1);
2259 registry_handle_global_remove(void *data, struct wl_registry *registry,
2263 static const struct wl_registry_listener registry_listener = {
2264 registry_handle_global,
2265 registry_handle_global_remove
2268 static void demo_init_connection(struct demo *demo) {
2270 demo->display = wl_display_connect(NULL);
2272 if (demo->display == NULL) {
2273 printf("Cannot find a compatible Vulkan installable client driver "
2274 "(ICD).\nExiting ...\n");
2279 demo->registry = wl_display_get_registry(demo->display);
2280 wl_registry_add_listener(demo->registry, ®istry_listener, demo);
2281 wl_display_dispatch(demo->display);
2285 static void demo_init(struct demo *demo, const int argc, const char *argv[])
2289 memset(demo, 0, sizeof(*demo));
2290 demo->frameCount = INT32_MAX;
2292 for (i = 1; i < argc; i++) {
2293 if (strcmp(argv[i], "--use_staging") == 0) {
2294 demo->use_staging_buffer = true;
2297 if (strcmp(argv[i], "--break") == 0) {
2298 demo->use_break = true;
2301 if (strcmp(argv[i], "--validate") == 0) {
2302 demo->validate = true;
2305 if (strcmp(argv[i], "--c") == 0 && demo->frameCount == INT32_MAX &&
2306 i < argc - 1 && sscanf(argv[i + 1], "%d", &demo->frameCount) == 1 &&
2307 demo->frameCount >= 0) {
2312 fprintf(stderr, "Usage:\n %s [--use_staging] [--validate] [--break] "
2313 "[--c <framecount>]\n",
2319 demo_init_connection(demo);
2323 demo->height = 2560;
2324 demo->depthStencil = 1.0;
2325 demo->depthIncrement = -0.01f;
2328 static void demo_cleanup(struct demo *demo) {
2331 demo->prepared = false;
2333 for (i = 0; i < demo->swapchainImageCount; i++) {
2334 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
2336 free(demo->framebuffers);
2337 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
2339 if (demo->setup_cmd) {
2340 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd);
2342 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd);
2343 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
2345 vkDestroyPipeline(demo->device, demo->pipeline, NULL);
2346 vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
2347 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
2348 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
2350 vkDestroyBuffer(demo->device, demo->vertices.buf, NULL);
2351 vkFreeMemory(demo->device, demo->vertices.mem, NULL);
2353 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
2354 vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
2355 vkDestroyImage(demo->device, demo->textures[i].image, NULL);
2356 vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
2357 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
2360 for (i = 0; i < demo->swapchainImageCount; i++) {
2361 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
2364 vkDestroyImageView(demo->device, demo->depth.view, NULL);
2365 vkDestroyImage(demo->device, demo->depth.image, NULL);
2366 vkFreeMemory(demo->device, demo->depth.mem, NULL);
2368 demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL);
2369 free(demo->buffers);
2371 vkDestroyDevice(demo->device, NULL);
2372 if (demo->validate) {
2373 demo->DestroyDebugReportCallback(demo->inst, demo->msg_callback, NULL);
2375 vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
2376 vkDestroyInstance(demo->inst, NULL);
2378 free(demo->queue_props);
2381 static void demo_resize(struct demo *demo) {
2384 // Don't react to resize until after first initialization.
2385 if (!demo->prepared) {
2388 // In order to properly resize the window, we must re-create the swapchain
2389 // AND redo the command buffers, etc.
2391 // First, perform part of the demo_cleanup() function:
2392 demo->prepared = false;
2394 for (i = 0; i < demo->swapchainImageCount; i++) {
2395 vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL);
2397 free(demo->framebuffers);
2398 vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL);
2400 if (demo->setup_cmd) {
2401 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd);
2403 vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd);
2404 vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL);
2406 vkDestroyPipeline(demo->device, demo->pipeline, NULL);
2407 vkDestroyRenderPass(demo->device, demo->render_pass, NULL);
2408 vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL);
2409 vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL);
2411 vkDestroyBuffer(demo->device, demo->vertices.buf, NULL);
2412 vkFreeMemory(demo->device, demo->vertices.mem, NULL);
2414 for (i = 0; i < DEMO_TEXTURE_COUNT; i++) {
2415 vkDestroyImageView(demo->device, demo->textures[i].view, NULL);
2416 vkDestroyImage(demo->device, demo->textures[i].image, NULL);
2417 vkFreeMemory(demo->device, demo->textures[i].mem, NULL);
2418 vkDestroySampler(demo->device, demo->textures[i].sampler, NULL);
2421 for (i = 0; i < demo->swapchainImageCount; i++) {
2422 vkDestroyImageView(demo->device, demo->buffers[i].view, NULL);
2425 vkDestroyImageView(demo->device, demo->depth.view, NULL);
2426 vkDestroyImage(demo->device, demo->depth.image, NULL);
2427 vkFreeMemory(demo->device, demo->depth.mem, NULL);
2429 free(demo->buffers);
2431 // Second, re-perform the demo_prepare() function, which will re-create the
2437 // Include header required for parsing the command line options.
2438 #include <shellapi.h>
2440 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
2441 LPSTR pCmdLine, int nCmdShow) {
2443 bool done; // flag saying when app is complete
2447 // Use the CommandLine functions to get the command line arguments.
2448 // Unfortunately, Microsoft outputs
2449 // this information as wide characters for Unicode, and we simply want the
2450 // Ascii version to be compatible
2451 // with the non-Windows side. So, we have to convert the information to
2452 // Ascii character strings.
2453 LPWSTR *commandLineArgs = CommandLineToArgvW(GetCommandLineW(), &argc);
2454 if (NULL == commandLineArgs) {
2459 argv = (char **)malloc(sizeof(char *) * argc);
2463 for (int iii = 0; iii < argc; iii++) {
2464 size_t wideCharLen = wcslen(commandLineArgs[iii]);
2465 size_t numConverted = 0;
2467 argv[iii] = (char *)malloc(sizeof(char) * (wideCharLen + 1));
2468 if (argv[iii] != NULL) {
2469 wcstombs_s(&numConverted, argv[iii], wideCharLen + 1,
2470 commandLineArgs[iii], wideCharLen + 1);
2478 demo_init(&demo, argc, argv);
2480 // Free up the items we had to allocate for the command line arguments.
2481 if (argc > 0 && argv != NULL) {
2482 for (int iii = 0; iii < argc; iii++) {
2483 if (argv[iii] != NULL) {
2490 demo.connection = hInstance;
2491 strncpy(demo.name, "tri", APP_NAME_STR_LEN);
2492 demo_create_window(&demo);
2493 demo_init_vk_swapchain(&demo);
2495 demo_prepare(&demo);
2497 done = false; // initialize loop condition variable
2498 /* main message loop*/
2500 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
2501 if (msg.message == WM_QUIT) // check for a quit message
2503 done = true; // if found, quit app
2505 /* Translate and dispatch to event queue*/
2506 TranslateMessage(&msg);
2507 DispatchMessage(&msg);
2509 RedrawWindow(demo.window, NULL, NULL, RDW_INTERNALPAINT);
2512 demo_cleanup(&demo);
2514 return (int)msg.wParam;
2517 int main(const int argc, const char *argv[]) {
2520 demo_init(&demo, argc, argv);
2521 demo_create_window(&demo);
2522 demo_init_vk_swapchain(&demo);
2524 demo_prepare(&demo);
2527 demo_cleanup(&demo);
2529 return validation_error;