Browse Source

Add swapchain creation.

master
Amber 2 years ago
parent
commit
25d8a48c3c
  1. 207
      src/main.c

207
src/main.c

@ -17,6 +17,19 @@ const char* validation_layers[] = { @@ -17,6 +17,19 @@ const char* validation_layers[] = {
"VK_LAYER_KHRONOS_validation",
};
const char* required_extensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
typedef struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
uint32_t num_formats;
VkSurfaceFormatKHR* formats;
uint32_t num_modes;
VkPresentModeKHR* present_modes;
} SwapChainSupportDetails;
typedef struct ApplicationState {
GLFWwindow* window;
//information about the connection between application and vulkan itself
@ -31,6 +44,9 @@ typedef struct ApplicationState { @@ -31,6 +44,9 @@ typedef struct ApplicationState {
VkSurfaceKHR surface;
//presentation queue
VkQueue present_queue;
//swapchain details:
SwapChainSupportDetails details;
VkSwapchainKHR swapchain;
} ApplicationState;
typedef struct QueueFamilyIndices {
@ -164,6 +180,60 @@ QueueFamilyIndices find_queue_families(ApplicationState* state, VkPhysicalDevice @@ -164,6 +180,60 @@ QueueFamilyIndices find_queue_families(ApplicationState* state, VkPhysicalDevice
return ret;
}
//get supported formats and modes
SwapChainSupportDetails get_swapchain_details(ApplicationState* state, VkPhysicalDevice device) {
SwapChainSupportDetails ret = {0};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, state->surface, &ret.capabilities);
uint32_t format_count;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, state->surface, &format_count, NULL);
ret.num_formats = format_count;
ret.formats = malloc(sizeof(VkSurfaceFormatKHR) * format_count);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, state->surface, &format_count, ret.formats);
uint32_t present_modes_count;
vkGetPhysicalDeviceSurfacePresentModesKHR(device, state->surface, &present_modes_count, NULL);
ret.num_modes = present_modes_count;
ret.present_modes = malloc(sizeof(VkSurfaceFormatKHR) * present_modes_count);
vkGetPhysicalDeviceSurfacePresentModesKHR(device, state->surface, &format_count, ret.present_modes);
printf("Device has %x present modes and %x formats for surface\n", present_modes_count, format_count);
return ret;
}
bool extensions_supported(ApplicationState* state, VkPhysicalDevice device) {
uint32_t num_extensions;
vkEnumerateDeviceExtensionProperties(device, NULL, &num_extensions, NULL);
VkExtensionProperties* available_extensions = malloc(sizeof(VkExtensionProperties) * num_extensions);
vkEnumerateDeviceExtensionProperties(device, NULL, &num_extensions, available_extensions);
printf("Device extensions:\n");
for (int i = 0; i < num_extensions; i++) {
printf("\t%s\n", available_extensions[i].extensionName);
}
for (int i = 0; i < ARRSIZE(required_extensions); i++) {
bool found = false;
for (int j = 0; j < num_extensions; j++) {
if (!strcmp(required_extensions[i], available_extensions[j].extensionName)) {
found = true;
}
}
if (!found) {
return false;
}
}
free(available_extensions);
return true;
}
void pick_physical_device(ApplicationState* state) {
uint32_t device_count = 0;
vkEnumeratePhysicalDevices(state->instance, &device_count, NULL);
@ -189,13 +259,22 @@ void pick_physical_device(ApplicationState* state) { @@ -189,13 +259,22 @@ void pick_physical_device(ApplicationState* state) {
QueueFamilyIndices families = find_queue_families(state, device);
SwapChainSupportDetails details = get_swapchain_details(state, device);
//for now we only require that a graphics queue is present
if (families.graphics_present && families.present_present) {
if (families.graphics_present && families.present_present && extensions_supported(state, device) &&
//we only care that at least one mode and one format exist
details.num_modes != 0 && details.num_formats != 0) {
state->details = details;
printf("graphics queue at %x, present queue at %x\n", families.graphics_queue, families.present_queue);
break;
} else {
free (details.present_modes);
free (details.formats);
printf("device does not support all necessary extensions and queues");
}
}
//TODO print unsupported message
state->physical_device = devices[i];
free(devices);
}
@ -244,7 +323,8 @@ void create_logical_device(ApplicationState* state) { @@ -244,7 +323,8 @@ void create_logical_device(ApplicationState* state) {
create_info.pQueueCreateInfos = queue_create_infos;
create_info.queueCreateInfoCount = j;
create_info.pEnabledFeatures = &device_features;
create_info.enabledExtensionCount = 0;
create_info.enabledExtensionCount = ARRSIZE(required_extensions);
create_info.ppEnabledExtensionNames = required_extensions;
if (VALIDATE) {
create_info.enabledLayerCount = ARRSIZE(validation_layers);
@ -273,11 +353,131 @@ void create_surface(ApplicationState* state) { @@ -273,11 +353,131 @@ void create_surface(ApplicationState* state) {
}
}
//We prefer one format but it may not be available
VkSurfaceFormatKHR choose_swap_surface_format(VkSurfaceFormatKHR* formats, uint32_t num) {
for (int i = 0; i < num; i++) {
if (formats[i].format == VK_FORMAT_B8G8R8_SRGB && formats[i].colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
return formats[i];
}
}
return formats[0];
}
//presentation modes determines how images show up on screen
//VK_PRESENT_MODE_IMMEDIATE_KHR: Images submitted by your application are transferred to the screen right away, which may result in tearing.
//VK_PRESENT_MODE_FIFO_KHR: The swap chain is a queue where the display takes an image from the front of the queue when the display is
//refreshed and the program inserts rendered images at the back of the queue. If the queue is full then the program has to wait.
//This is most similar to vertical sync as found in modern games. The moment that the display is refreshed is known as "vertical blank".
//VK_PRESENT_MODE_FIFO_RELAXED_KHR: This mode only differs from the previous one if the application is late and the queue was empty at
//the last vertical blank. Instead of waiting for the next vertical blank, the image is transferred right away when it finally arrives.
//This may result in visible tearing.
//VK_PRESENT_MODE_MAILBOX_KHR: This is another variation of the second mode. Instead of blocking the application when the queue is full,
//the images that are already queued are simply replaced with the newer ones. This mode can be used to render frames as fast as possible
//while still avoiding tearing, resulting in fewer latency issues than standard vertical sync. This is commonly known as "triple buffering",
//although the existence of three buffers alone does not necessarily mean that the framerate is unlocked.
VkPresentModeKHR choose_swap_present_mode(VkPresentModeKHR* modes, uint32_t num) {
for (int i = 0; i < num; i++) {
if (modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
return modes[i];
}
}
//guaranteed to exist
return VK_PRESENT_MODE_FIFO_KHR;
}
uint32_t clamp(uint32_t val, uint32_t min, uint32_t max) {
if (val > max) {
return max;
} else if (val < min) {
return min;
}
return val;
}
//resolutoion of the swapchain images
VkExtent2D choose_swap_extent(ApplicationState* state, VkSurfaceCapabilitiesKHR capabilities) {
if (capabilities.currentExtent.width != UINT32_MAX) {
return capabilities.currentExtent;
} else {
int width, height;
glfwGetFramebufferSize(state->window, &width, &height);
VkExtent2D extent;
extent.width = clamp(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
extent.height = clamp(height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return extent;
}
}
void create_swap_chain(ApplicationState* state) {
VkSurfaceFormatKHR format = choose_swap_surface_format(state->details.formats, state->details.num_formats);
VkPresentModeKHR mode = choose_swap_present_mode(state->details.present_modes, state->details.num_modes);
VkExtent2D extent = choose_swap_extent(state, state->details.capabilities);
uint32_t image_count = state->details.capabilities.minImageCount + 1;
if (state->details.capabilities.maxImageCount > 0 && image_count > state->details.capabilities.maxImageCount) {
image_count = state->details.capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR create_info;
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info.surface = state->surface;
create_info.minImageCount = image_count;
create_info.imageFormat = format.format;
create_info.imageColorSpace = format.colorSpace;
create_info.imageExtent = extent;
//1 unless stereoscopic 3d
create_info.imageArrayLayers = 1;
//we directly render to this image
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
//next we need to determine if images will be shared by multiple queues
QueueFamilyIndices families = find_queue_families(state, state->physical_device);
uint32_t indices[] = {families.graphics_queue, families.present_queue};
if (families.present_queue != families.graphics_queue) {
create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
create_info.queueFamilyIndexCount = 2;
create_info.pQueueFamilyIndices = indices;
} else {
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info.queueFamilyIndexCount = 0;
create_info.pQueueFamilyIndices = NULL;
}
//no transformations (such as rotations and so on)
create_info.preTransform = state->details.capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = mode;
//pixels that are obscured by other windows do not matter
create_info.clipped = VK_TRUE;
//we don't handle resize for now
create_info.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(state->device, &create_info, NULL, &state->swapchain) != VK_SUCCESS) {
printf("failed to create swpachain\n");
exit(1);
}
printf("swapchain created\n");
}
void init_vulkan(ApplicationState* state) {
create_instance(state);
create_surface(state);
pick_physical_device(state);
create_logical_device(state);
create_swap_chain(state);
}
void main_loop(ApplicationState* state) {
@ -287,6 +487,7 @@ void main_loop(ApplicationState* state) { @@ -287,6 +487,7 @@ void main_loop(ApplicationState* state) {
}
void terminate(ApplicationState* state) {
vkDestroySwapchainKHR(state->device, state->swapchain, NULL);
vkDestroyDevice(state->device, NULL);
vkDestroySurfaceKHR(state->instance, state->surface, NULL);
vkDestroyInstance(state->instance, NULL);

Loading…
Cancel
Save