kiba-engine
swap_chain.c
1 #include <kiba/renderer/vulkan/swap_chain.h>
2 
3 #include <kiba/renderer/vulkan/image.h>
4 
5 #include <limits.h>
6 
7 #include <kiba/renderer/vulkan/depth.h>
8 #include <kiba/renderer/vulkan/framebuffers.h>
9 
10 b8 vulkan_verify_swap_chain_format_availability(vulkan_context *context);
11 b8 vulkan_verify_swap_chain_present_mode_availability(vulkan_context *context);
12 b8 vulkan_query_swap_chain_capabilities(vulkan_context *context);
13 
14 b8 vulkan_swap_chain_create(vulkan_context *context) {
15  // setup and verification
16  if (!vulkan_verify_swap_chain_format_availability(context)) {
17  KB_INFO("did not find required swap chain format");
18  return false;
19  }
20  if (!vulkan_verify_swap_chain_present_mode_availability(context)) {
21  KB_INFO("did not find required swap chain present mode");
22  return false;
23  }
24  if (!vulkan_query_swap_chain_capabilities(context)) {
25  KB_INFO("could not query swap chain capabilities");
26  return false;
27  }
28 
29  // create swap chain
30  VkSwapchainCreateInfoKHR swap_chain_info = {
31  .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
32  .surface = context->surface,
33  .imageExtent = context->swap_chain.extent,
34  .minImageCount = context->swap_chain.image_count,
35  .imageFormat = context->swap_chain.requirements.format.format,
36  .imageColorSpace = context->swap_chain.requirements.format.colorSpace,
37  .presentMode = context->swap_chain.requirements.present_mode,
38  .imageArrayLayers = 1,
39  .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
40  .preTransform = context->swap_chain.transform,
41  // TODO: make configurable, maybe requirements
42  .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
43  .clipped = VK_TRUE,
44  // TODO: swap chain recreation
45  .oldSwapchain = VK_NULL_HANDLE,
46  };
47  u32 queue_family_indices[] = {context->device.graphics_queue.index, context->device.present_queue.index};
48  if (context->device.graphics_queue.index != context->device.present_queue.index) {
49  swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
50  swap_chain_info.queueFamilyIndexCount = 2;
51  swap_chain_info.pQueueFamilyIndices = queue_family_indices;
52  } else {
53  swap_chain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
54  }
55 
56  VK_CALL_B8(vkCreateSwapchainKHR(context->device.logical,
57  &swap_chain_info,
58  &context->alloc.vulkan_callbacks,
59  &context->swap_chain.swap_chain));
60 
61  // get images
62  u32 image_count = 0;
63  VK_CALL_B8(vkGetSwapchainImagesKHR(context->device.logical, context->swap_chain.swap_chain, &image_count, 0));
64  context->swap_chain.image_count = image_count;
65  context->swap_chain.images = array_create(VkImage, image_count, &context->alloc.kiba_alloc);
66  if (context->swap_chain.images == KB_NULL) {
67  KB_ERROR("could not allocate array for swap chain images");
68  return false;
69  }
70  context->swap_chain.image_views = array_create(VkImageView, image_count, &context->alloc.kiba_alloc);
71  if (context->swap_chain.image_views == KB_NULL) {
72  KB_ERROR("could not allocate array for swap chain image views");
73  return false;
74  }
75  VK_CALL_B8(vkGetSwapchainImagesKHR(context->device.logical,
76  context->swap_chain.swap_chain,
77  &image_count,
78  context->swap_chain.images));
79  array_resize(&context->swap_chain.images, image_count);
80  array_resize(&context->swap_chain.image_views, image_count);
81 
82  usize i = 0;
83  array_for_each(VkImage, image, context->swap_chain.images) {
84  if (!vulkan_image_view_create(context,
85  *image,
86  context->swap_chain.requirements.format.format,
87  VK_IMAGE_ASPECT_COLOR_BIT,
88  &context->swap_chain.image_views[i++])) {
89  return false;
90  }
91  }
92 
93  return true;
94 }
95 
96 b8 vulkan_swap_chain_recreate(vulkan_context *context) {
97  // wait until async stuff done
98  vkDeviceWaitIdle(context->device.logical);
99 
100  vulkan_framebuffers_destroy(context);
101  vulkan_depth_destroy(context);
102  vulkan_swap_chain_destroy(context);
103 
104  // NOTE: may also recreate render pass e.g. in case window is dragged to high-res display
105  // TODO: should also listen for resize events
106  // TODO: should also handle minimization
107 
108  return vulkan_swap_chain_create(context) && vulkan_depth_create(context) && vulkan_framebuffers_create(context);
109 }
110 
111 void vulkan_swap_chain_destroy(vulkan_context *context) {
112  array_for_each(VkImageView, view, context->swap_chain.image_views) { vulkan_image_view_destroy(context, view); }
113  array_destroy(&context->swap_chain.image_views);
114  array_destroy(&context->swap_chain.images);
115  vkDestroySwapchainKHR(context->device.logical, context->swap_chain.swap_chain, &context->alloc.vulkan_callbacks);
116 }
117 
118 b8 vulkan_verify_swap_chain_format_availability(vulkan_context *context) {
119  u32 available_format_count = 0;
120  VK_CALL_B8(
121  vkGetPhysicalDeviceSurfaceFormatsKHR(context->device.physical, context->surface, &available_format_count, 0));
122  array_of(VkSurfaceFormatKHR) available_formats =
123  array_create(VkSurfaceFormatKHR, available_format_count, &context->alloc.kiba_alloc);
124  if (available_formats == KB_NULL) {
125  KB_ERROR("could not create array to store available swap chain formats");
126  return false;
127  }
128  VK_CALL_B8(vkGetPhysicalDeviceSurfaceFormatsKHR(context->device.physical,
129  context->surface,
130  &available_format_count,
131  available_formats));
132  array_resize(&available_formats, available_format_count);
133 
134  b8 found_format = false;
135  array_for_each(VkSurfaceFormatKHR, surface_format, available_formats) {
136  if (surface_format->format == context->swap_chain.requirements.format.format
137  && surface_format->colorSpace == context->swap_chain.requirements.format.colorSpace) {
138  found_format = true;
139  break;
140  }
141  }
142 
143  array_destroy(&available_formats);
144  return found_format;
145 }
146 
147 b8 vulkan_verify_swap_chain_present_mode_availability(vulkan_context *context) {
148  u32 available_present_mode_count = 0;
149  VK_CALL_B8(vkGetPhysicalDeviceSurfacePresentModesKHR(context->device.physical,
150  context->surface,
151  &available_present_mode_count,
152  0));
153  array_of(VkPresentModeKHR) available_present_modes =
154  array_create(VkPresentModeKHR, available_present_mode_count, &context->alloc.kiba_alloc);
155  if (available_present_modes == KB_NULL) {
156  KB_ERROR("could not create array to store available swap chain present modes");
157  return false;
158  }
159  VK_CALL_B8(vkGetPhysicalDeviceSurfacePresentModesKHR(context->device.physical,
160  context->surface,
161  &available_present_mode_count,
162  available_present_modes));
163  array_resize(&available_present_modes, available_present_mode_count);
164 
165  b8 found_present_mode = false;
166  array_for_each(VkPresentModeKHR, present_mode, available_present_modes) {
167  if (*present_mode == context->swap_chain.requirements.present_mode) {
168  found_present_mode = true;
169  break;
170  }
171  }
172 
173  array_destroy(&available_present_modes);
174  return found_present_mode;
175 }
176 
177 b8 vulkan_query_swap_chain_capabilities(vulkan_context *context) {
178  VkSurfaceCapabilitiesKHR capabilities;
179  VK_CALL_B8(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context->device.physical, context->surface, &capabilities));
180  if (capabilities.currentExtent.width == UINT_MAX) {
181  context->swap_chain.extent.height =
182  KB_CLAMP(context->window->h, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
183  context->swap_chain.extent.width =
184  KB_CLAMP(context->window->w, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
185  } else {
186  context->swap_chain.extent = capabilities.currentExtent;
187  }
188  context->swap_chain.image_count = capabilities.minImageCount;
189  if (capabilities.maxImageCount > capabilities.minImageCount) {
190  ++context->swap_chain.image_count;
191  }
192  context->swap_chain.transform = capabilities.currentTransform;
193  return true;
194 }
#define KB_CLAMP(x, min, max)
Clamp an input value to a certain range.
Definition: defines.h:55
#define KB_NULL
Value of an invalid ptr (nullptr).
Definition: defines.h:18
#define KB_ERROR(...)
Log entry with error log level.
Definition: log.h:142
#define KB_INFO(...)
Log entry with info log level.
Definition: log.h:162
u32 h
Height of the window in pixels.
Definition: window.h:25
u32 w
Width of the window in pixels.
Definition: window.h:23