Compare commits

..

2 commits

Author SHA1 Message Date
9a28978380
whoops 2024-09-12 12:39:00 +02:00
578fde4a09
big refactor but still shit 2024-09-12 12:35:56 +02:00
15 changed files with 834 additions and 652 deletions

View file

@ -6,6 +6,7 @@
#include <ptk_option.h>
#include <ptk_vk/components.h>
#include <ptk_vk/device.h>
#include <ptk_vk/draw.h>
#include <ptk_vk/init.h>
#include <ptk_vk/utils.h>
@ -196,7 +197,7 @@ int ptk_run(PtkHandle root) {
while (!glfwWindowShouldClose(m_window)) {
glfwPollEvents();
if (!vk_draw_frame()) {
if (!vk_draw_frame(m_window, g_surface)) {
break;
}
}

137
src/ptk_vk/device.c Normal file
View file

@ -0,0 +1,137 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#include <ptk_vk/device.h>
#include <ptk_vk/init.h>
#include <ptk_vk/swapchain.h>
#include <ptk_vk/utils.h>
#include <stdbool.h>
#include <string.h>
#include <ptk_array.h>
#include <ptk_list.h>
#include <ptk_log.h>
#include <ptk_option.h>
VkDevice g_dev = VK_NULL_HANDLE;
QueueFamilyIndices g_queue_family_indices = {0};
const size_t g_queue_family_count = sizeof(QueueFamilyIndices) / sizeof(PTK_OPTION(uint32_t));
PTK_LIST_DEFINE(VkQueueFamilyProperties);
static const PTK_ARRAY(constcharptr) m_device_extensions = PTK_ARRAY_NEW(constcharptr, {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
});
PTK_LIST_DEFINE(VkExtensionProperties);
bool are_extensions_supported(const VkPhysicalDevice physical_dev) {
PTK_LIST(VkExtensionProperties) available_extensions;
vkEnumerateDeviceExtensionProperties(physical_dev, NULL, &available_extensions.allocated, NULL);
available_extensions = PTK_LIST_NEW(VkExtensionProperties, available_extensions.allocated);
vkEnumerateDeviceExtensionProperties(physical_dev, NULL, &available_extensions.allocated, available_extensions.data);
PTK_LIST_FILLED(available_extensions);
size_t supported_extensions = 0;
for (size_t i = 0; i < m_device_extensions.size; ++i) {
PTK_LIST_FOR_EACH(const VkExtensionProperties, available_extensions, current_extension, {
if (strcmp(m_device_extensions.data[i], current_extension.extensionName) == 0) {
supported_extensions += 1;
break;
}
})
}
return supported_extensions == m_device_extensions.size;
}
static bool is_device_suitable(const VkPhysicalDevice physical_dev, const VkSurfaceKHR surface) {
PTK_LIST(VkQueueFamilyProperties) queue_families;
vkGetPhysicalDeviceQueueFamilyProperties(physical_dev, &queue_families.allocated, NULL);
queue_families = PTK_LIST_NEW(VkQueueFamilyProperties, queue_families.allocated);
vkGetPhysicalDeviceQueueFamilyProperties(physical_dev, &queue_families.allocated, queue_families.data);
PTK_LIST_FILLED(queue_families);
g_queue_family_indices.graphics = PTK_OPTION_NONE(uint32_t);
g_queue_family_indices.present = PTK_OPTION_NONE(uint32_t);
PTK_LIST_FOR_EACH_E(const VkQueueFamilyProperties, queue_families, i, queue_family, {
if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
g_queue_family_indices.graphics = PTK_OPTION_SOME(uint32_t, i);
}
VkBool32 present_support = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_dev, i, surface, &present_support);
if (present_support) {
g_queue_family_indices.present = PTK_OPTION_SOME(uint32_t, i);
}
})
const bool indices_found = g_queue_family_indices.graphics.exists
&& g_queue_family_indices.present.exists;
const bool extensions_supported = are_extensions_supported(physical_dev);
bool swapchain_adequate = false;
if (extensions_supported) {
const SwapchainSupportInfo swapchain_support = query_swapchain_support(physical_dev, surface);
swapchain_adequate = swapchain_support.formats.size != 0
&& swapchain_support.present_modes.size != 0;
PTK_LIST_FREE(swapchain_support.formats);
PTK_LIST_FREE(swapchain_support.present_modes);
}
PTK_LIST_FREE(queue_families);
return indices_found && extensions_supported && swapchain_adequate;
}
bool vk_create_logical_dev(VkPhysicalDevice physical_dev, VkSurfaceKHR surface, const PTK_ARRAY(constcharptr) validation_layers) {
if (!is_device_suitable(physical_dev, surface)) {
PTK_ERR("physical device isn't suitable");
return false;
}
VkDeviceQueueCreateInfo queue_create_infos[g_queue_family_count];
for (size_t i = 0; i < g_queue_family_count; ++i) {
const PTK_OPTION(uint32_t) index = *(((PTK_OPTION(uint32_t) *)&g_queue_family_indices) + i);
queue_create_infos[i] = (VkDeviceQueueCreateInfo){
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueFamilyIndex = index.value,
.queueCount = 1,
.pQueuePriorities = &(float){1.0f},
};
}
VK_TRY(false,
vkCreateDevice(
physical_dev,
&(VkDeviceCreateInfo){
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueCreateInfoCount = g_queue_family_count,
.pQueueCreateInfos = queue_create_infos,
.enabledLayerCount = validation_layers.size,
.ppEnabledLayerNames = validation_layers.data,
.enabledExtensionCount = m_device_extensions.size,
.ppEnabledExtensionNames = m_device_extensions.data,
.pEnabledFeatures = &(VkPhysicalDeviceFeatures){0},
},
NULL,
&g_dev
)
);
vkGetDeviceQueue(g_dev, g_queue_family_indices.graphics.value, 0, &g_graphics_queue);
vkGetDeviceQueue(g_dev, g_queue_family_indices.present.value, 0, &g_present_queue);
return true;
}

30
src/ptk_vk/device.h Normal file
View file

@ -0,0 +1,30 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#ifndef PTK_PTK_VK_DEVICE_H_
#define PTK_PTK_VK_DEVICE_H_
#include <ptk_array.h>
#include <ptk_option.h>
#include <vulkan/vulkan_core.h>
extern VkDevice g_dev;
PTK_OPTION_DEFINE(VkDevice);
// for PTK_ARRAY(constcharptr)
#include <ptk_vk/instance.h>
PTK_OPTION_DEFINE(uint32_t);
typedef struct {
PTK_OPTION(uint32_t) graphics;
PTK_OPTION(uint32_t) present;
} QueueFamilyIndices;
extern QueueFamilyIndices g_queue_family_indices;
extern const size_t g_queue_family_count;
bool vk_create_logical_dev(VkPhysicalDevice physical_dev, VkSurfaceKHR surface, const PTK_ARRAY(constcharptr) validation_layers);
#endif // PTK_PTK_VK_DEVICE_H_

View file

@ -2,7 +2,9 @@
#include <ptk_vk/draw.h>
#include <ptk_vk/device.h>
#include <ptk_vk/init.h>
#include <ptk_vk/swapchain.h>
#include <ptk_vk/utils.h>
#include <ptk_log.h>
@ -12,7 +14,7 @@
bool g_framebuffer_resized = false;
uint32_t g_current_frame = 0;
bool vk_draw_frame(void) {
bool vk_draw_frame(GLFWwindow *window, VkSurfaceKHR surface) {
vkWaitForFences(g_dev, 1, &g_in_flight_fences.data[g_current_frame], VK_TRUE, UINT64_MAX);
uint32_t image_index;
@ -26,7 +28,7 @@ bool vk_draw_frame(void) {
);
if (acquire_next_image_result == VK_ERROR_OUT_OF_DATE_KHR) {
if (!vk_recreate_swapchain()) {
if (!vk_recreate_swapchain(window, surface)) {
return false;
}
return true;
@ -83,7 +85,7 @@ bool vk_draw_frame(void) {
g_framebuffer_resized
) {
g_framebuffer_resized = false;
if (!vk_recreate_swapchain()) {
if (!vk_recreate_swapchain(window, surface)) {
return false;
}
PTK_TRACE("recreated swapchain");

View file

@ -6,9 +6,14 @@
#include <stdbool.h>
#include <stdint.h>
#ifndef GLFW_INCLUDE_VULKAN
#define GLFW_INCLUDE_VULKAN
#endif
#include <GLFW/glfw3.h>
extern bool g_framebuffer_resized;
extern uint32_t g_current_frame;
bool vk_draw_frame(void);
bool vk_draw_frame(GLFWwindow *window, VkSurfaceKHR surface);
#endif // PTK_PTK_VK_DRAW_H_

View file

@ -3,7 +3,12 @@
#include <ptk_vk/init.h>
#include <ptk_vk/components.h>
#include <ptk_vk/device.h>
#include <ptk_vk/draw.h>
#include <ptk_vk/instance.h>
#include <ptk_vk/physical_device.h>
#include <ptk_vk/render_pass.h>
#include <ptk_vk/swapchain.h>
#include <ptk_vk/utils.h>
#include <ptk_log.h>
@ -15,71 +20,25 @@
#include <errno.h>
PTK_LIST_DEFINE(VkImage);
PTK_LIST_DEFINE(VkImageView);
PTK_OPTION_DEFINE(uint32_t);
PTK_LIST_DEFINE(VkSurfaceFormatKHR);
PTK_LIST_DEFINE(VkPresentModeKHR);
PTK_LIST_DEFINE(VkFramebuffer);
PTK_ARRAY_DEFINE(VkVertexInputAttributeDescription);
PTK_LIST_DEFINE(VkExtensionProperties);
PTK_LIST_DEFINE(VkQueueFamilyProperties);
PTK_OPTION_DEFINE(VkShaderModule);
PTK_LIST_DEFINE(VkBuffer);
PTK_LIST_DEFINE(VkDeviceMemory);
typedef void *voidptr;
PTK_LIST_DEFINE(voidptr);
PTK_LIST_DEFINE(VkDescriptorSet);
typedef const char *constcharptr;
PTK_ARRAY_DEFINE(constcharptr);
PTK_LIST_DEFINE(constcharptr);
#ifdef DEBUG
PTK_LIST_DEFINE(VkLayerProperties);
#endif
typedef struct {
VkSurfaceCapabilitiesKHR capabilities;
PTK_LIST(VkSurfaceFormatKHR) formats;
PTK_LIST(VkPresentModeKHR) present_modes;
} SwapchainSupportInfo;
static GLFWwindow *m_window = NULL;
static VkInstance m_instance = VK_NULL_HANDLE;
static VkPhysicalDevice m_physical_dev = VK_NULL_HANDLE;
VkDevice g_dev = VK_NULL_HANDLE;
static VkSurfaceKHR m_surface = VK_NULL_HANDLE;
VkSwapchainKHR g_swapchain = VK_NULL_HANDLE;
static VkFormat m_swapchain_image_format;
static VkExtent2D m_swapchain_extent;
static PTK_LIST(VkImage) m_swapchain_images;
static PTK_LIST(VkImageView) m_swapchain_image_views;
static struct {
PTK_OPTION(uint32_t) graphics;
PTK_OPTION(uint32_t) present;
} m_queue_family_indices = {0};
static const size_t m_queue_family_count = sizeof(m_queue_family_indices) / sizeof(PTK_OPTION(uint32_t));
VkSurfaceKHR g_surface = VK_NULL_HANDLE;
VkQueue g_graphics_queue = VK_NULL_HANDLE;
VkQueue g_present_queue = VK_NULL_HANDLE;
static const PTK_ARRAY(constcharptr) m_device_extensions = PTK_ARRAY_NEW(constcharptr, {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
});
static VkRenderPass m_render_pass;
static VkDescriptorSetLayout m_descriptor_set_layout;
static VkPipelineLayout m_pipeline_layout;
static VkPipeline m_pipeline;
static PTK_LIST(VkFramebuffer) m_swapchain_framebuffers;
const size_t g_max_frames_in_flight = 2;
static VkCommandPool m_command_pool;
@ -145,450 +104,10 @@ static PTK_LIST(VkDescriptorSet) m_descriptor_sets;
static const PTK_ARRAY(constcharptr) m_validation_layers = PTK_ARRAY_NEW(constcharptr, {
"VK_LAYER_KHRONOS_validation"
});
bool check_validation_layers(const PTK_ARRAY(constcharptr) validation_layers) {
PTK_LIST(VkLayerProperties) available_layers;
vkEnumerateInstanceLayerProperties(&available_layers.allocated, NULL);
available_layers = PTK_LIST_NEW(VkLayerProperties, available_layers.allocated);
vkEnumerateInstanceLayerProperties(&available_layers.allocated, available_layers.data);
PTK_LIST_FILLED(available_layers);
for (size_t i = 0; i < validation_layers.size; ++i) {
const char *layer_name = validation_layers.data[i];
bool layer_found = false;
PTK_LIST_FOR_EACH(const VkLayerProperties, available_layers, layer_properties, {
if (strcmp(layer_name, layer_properties.layerName) == 0) {
layer_found = true;
break;
}
})
if (!layer_found) {
return false;
}
}
return true;
}
#else
static const PTK_ARRAY(constcharptr) m_validation_layers = PTK_ARRAY_EMPTY(constcharptr);
#endif
bool create_vk_instance(const char *title, const PtkVersion version) {
#ifdef DEBUG
if (!check_validation_layers(m_validation_layers)) {
PTK_ERR("couldn't find requested validation layer");
return false;
}
#endif
PTK_ARRAY(constcharptr) extension_names = PTK_ARRAY_EMPTY(constcharptr);
extension_names.data = glfwGetRequiredInstanceExtensions(&extension_names.size);
VK_TRY(false,
vkCreateInstance(
&(VkInstanceCreateInfo){
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.pApplicationInfo = &(VkApplicationInfo){
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = title,
.applicationVersion = VK_MAKE_API_VERSION(0, version.major, version.minor, version.patch),
.pEngineName = PTK_ENGINE_NAME,
.engineVersion = VK_MAKE_API_VERSION(0, PTK_VERSION_MAJOR, PTK_VERSION_MINOR, PTK_VERSION_PATCH),
.apiVersion = VK_API_VERSION_1_3,
},
.enabledLayerCount = m_validation_layers.size,
.ppEnabledLayerNames = m_validation_layers.data,
.enabledExtensionCount = extension_names.size,
.ppEnabledExtensionNames = extension_names.data,
},
NULL,
&m_instance
)
);
return true;
}
bool select_physical_dev(void) {
uint32_t dev_count = 0;
vkEnumeratePhysicalDevices(m_instance, &dev_count, NULL);
if (dev_count == 0) {
PTK_ERR("failed to find GPU with vulkan support");
return false;
}
VkPhysicalDevice devs[dev_count];
vkEnumeratePhysicalDevices(m_instance, &dev_count, devs);
VkPhysicalDeviceProperties dev_props[dev_count];
uint32_t dgpus[dev_count];
uint32_t dgpu_count = 0;
uint32_t igpus[dev_count];
uint32_t igpu_count = 0;
VkPhysicalDeviceMemoryProperties dev_mem_props[dev_count];
uint32_t dev_mem_counts[dev_count];
VkDeviceSize dev_mem_totals[dev_count];
for (size_t i = 0; i < dev_count; ++i) {
vkGetPhysicalDeviceProperties(devs[i], &dev_props[i]);
if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
dgpus[dgpu_count] = i;
dgpu_count += 1;
} else if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
igpus[igpu_count] = i;
igpu_count += 1;
}
vkGetPhysicalDeviceMemoryProperties(devs[i], &dev_mem_props[i]);
dev_mem_counts[i] = dev_mem_props[i].memoryHeapCount;
dev_mem_totals[i] = 0;
for (size_t j = 0; j < dev_mem_counts[i]; ++j) {
dev_mem_totals[i] += dev_mem_props[i].memoryHeaps[j].size;
}
}
VkDeviceSize max_mem_size = 0;
size_t dev_best_index = 0;
if (dgpu_count != 0) {
for (size_t i = 0; i < dgpu_count; ++i) {
if (dev_mem_totals[i] > max_mem_size) {
dev_best_index = dgpus[i];
max_mem_size = dev_mem_totals[i];
}
}
} else if (igpu_count != 0) {
for (size_t i = 0; i < igpu_count; ++i) {
if (dev_mem_totals[i] > max_mem_size) {
dev_best_index = igpus[i];
max_mem_size = dev_mem_totals[i];
}
}
}
PTK_DEBUG("best device index: %ld", dev_best_index);
PTK_DEBUG("device name: %s", dev_props[dev_best_index].deviceName);
PTK_DEBUG("device type: %s", (dgpu_count != 0 ? "dgpu" : (igpu_count != 0 ? "igpu" : "probably cpu")));
PTK_DEBUG("total memory: %lu", dev_mem_totals[dev_best_index]);
m_physical_dev = devs[dev_best_index];
return true;
}
VkSurfaceFormatKHR select_swap_surface_format(const PTK_LIST(VkSurfaceFormatKHR) available_formats) {
PTK_LIST_FOR_EACH(const VkSurfaceFormatKHR, available_formats, current_format, {
if (current_format.format == VK_FORMAT_B8G8R8A8_SRGB && current_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return current_format;
}
})
return available_formats.data[0];
}
VkPresentModeKHR select_swap_present_mode(const PTK_LIST(VkPresentModeKHR) available_present_modes) {
PTK_LIST_FOR_EACH(const VkPresentModeKHR, available_present_modes, current_present_mode, {
if (current_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) {
return current_present_mode;
}
})
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D select_swap_extent(const VkSurfaceCapabilitiesKHR capabilities) {
if (capabilities.currentExtent.width != UINT32_MAX) {
return capabilities.currentExtent;
}
int width, height;
glfwGetFramebufferSize(m_window, &width, &height);
VkExtent2D actual_extent = {
.width = (uint32_t)width,
.height = (uint32_t)height,
};
if (actual_extent.width < capabilities.minImageExtent.width) {
actual_extent.width = capabilities.minImageExtent.width;
} else if (actual_extent.width > capabilities.maxImageExtent.width) {
actual_extent.width = capabilities.maxImageExtent.width;
}
if (actual_extent.height < capabilities.minImageExtent.height) {
actual_extent.height = capabilities.minImageExtent.height;
} else if (actual_extent.height > capabilities.maxImageExtent.height) {
actual_extent.height = capabilities.maxImageExtent.height;
}
return actual_extent;
}
SwapchainSupportInfo query_swapchain_support(const VkPhysicalDevice dev) {
SwapchainSupportInfo info;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, m_surface, &info.capabilities);
const uint32_t width = info.capabilities.currentExtent.width;
const uint32_t height = info.capabilities.currentExtent.height;
if (
info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR
|| info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR
) {
info.capabilities.currentExtent.height = width;
info.capabilities.currentExtent.width = height;
}
vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &info.formats.allocated, NULL);
if (info.formats.allocated != 0) {
info.formats = PTK_LIST_NEW(VkSurfaceFormatKHR, info.formats.allocated);
vkGetPhysicalDeviceSurfaceFormatsKHR(dev, m_surface, &info.formats.allocated, info.formats.data);
info.formats.size = info.formats.allocated;
}
vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface, &info.present_modes.allocated, NULL);
if (info.present_modes.allocated != 0) {
info.present_modes = PTK_LIST_NEW(VkPresentModeKHR, info.present_modes.allocated);
vkGetPhysicalDeviceSurfacePresentModesKHR(dev, m_surface, &info.present_modes.allocated, info.present_modes.data);
info.present_modes.size = info.present_modes.allocated;
}
return info;
}
bool are_extensions_supported(const VkPhysicalDevice dev) {
PTK_LIST(VkExtensionProperties) available_extensions;
vkEnumerateDeviceExtensionProperties(dev, NULL, &available_extensions.allocated, NULL);
available_extensions = PTK_LIST_NEW(VkExtensionProperties, available_extensions.allocated);
vkEnumerateDeviceExtensionProperties(dev, NULL, &available_extensions.allocated, available_extensions.data);
PTK_LIST_FILLED(available_extensions);
size_t supported_extensions = 0;
for (size_t i = 0; i < m_device_extensions.size; ++i) {
PTK_LIST_FOR_EACH(const VkExtensionProperties, available_extensions, current_extension, {
if (strcmp(m_device_extensions.data[i], current_extension.extensionName) == 0) {
supported_extensions += 1;
break;
}
})
}
return supported_extensions == m_device_extensions.size;
}
bool is_device_suitable(const VkPhysicalDevice dev) {
PTK_LIST(VkQueueFamilyProperties) queue_families;
vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_families.allocated, NULL);
queue_families = PTK_LIST_NEW(VkQueueFamilyProperties, queue_families.allocated);
vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_families.allocated, queue_families.data);
PTK_LIST_FILLED(queue_families);
m_queue_family_indices.graphics = PTK_OPTION_NONE(uint32_t);
m_queue_family_indices.present = PTK_OPTION_NONE(uint32_t);
PTK_LIST_FOR_EACH_E(const VkQueueFamilyProperties, queue_families, i, queue_family, {
if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
m_queue_family_indices.graphics = PTK_OPTION_SOME(uint32_t, i);
}
VkBool32 present_support = VK_FALSE;
vkGetPhysicalDeviceSurfaceSupportKHR(dev, i, m_surface, &present_support);
if (present_support) {
m_queue_family_indices.present = PTK_OPTION_SOME(uint32_t, i);
}
})
const bool indices_found = m_queue_family_indices.graphics.exists
&& m_queue_family_indices.present.exists;
const bool extensions_supported = are_extensions_supported(dev);
bool swapchain_adequate = false;
if (extensions_supported) {
const SwapchainSupportInfo swapchain_support = query_swapchain_support(dev);
swapchain_adequate = swapchain_support.formats.size != 0
&& swapchain_support.present_modes.size != 0;
PTK_LIST_FREE(swapchain_support.formats);
PTK_LIST_FREE(swapchain_support.present_modes);
}
PTK_LIST_FREE(queue_families);
return indices_found && extensions_supported && swapchain_adequate;
}
bool create_logical_dev(void) {
if (!is_device_suitable(m_physical_dev)) {
PTK_ERR("physical device isn't suitable");
return false;
}
VkDeviceQueueCreateInfo queue_create_infos[m_queue_family_count];
for (size_t i = 0; i < m_queue_family_count; ++i) {
const PTK_OPTION(uint32_t) index = *(((PTK_OPTION(uint32_t) *)&m_queue_family_indices) + i);
queue_create_infos[i] = (VkDeviceQueueCreateInfo){
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueFamilyIndex = index.value,
.queueCount = 1,
.pQueuePriorities = &(float){1.0f},
};
}
VK_TRY(false,
vkCreateDevice(
m_physical_dev,
&(VkDeviceCreateInfo){
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.queueCreateInfoCount = m_queue_family_count,
.pQueueCreateInfos = queue_create_infos,
.enabledLayerCount = m_validation_layers.size,
.ppEnabledLayerNames = m_validation_layers.data,
.enabledExtensionCount = m_device_extensions.size,
.ppEnabledExtensionNames = m_device_extensions.data,
.pEnabledFeatures = &(VkPhysicalDeviceFeatures){0},
},
NULL,
&g_dev
)
);
vkGetDeviceQueue(g_dev, m_queue_family_indices.graphics.value, 0, &g_graphics_queue);
vkGetDeviceQueue(g_dev, m_queue_family_indices.present.value, 0, &g_present_queue);
return true;
}
bool create_swapchain(void) {
const SwapchainSupportInfo swapchain_support = query_swapchain_support(m_physical_dev);
const VkSurfaceFormatKHR surface_format = select_swap_surface_format(swapchain_support.formats);
const VkPresentModeKHR present_mode = select_swap_present_mode(swapchain_support.present_modes);
const VkExtent2D extent = select_swap_extent(swapchain_support.capabilities);
uint32_t image_count = swapchain_support.capabilities.minImageCount + 1;
if (swapchain_support.capabilities.maxImageCount > 0 && image_count > swapchain_support.capabilities.maxImageCount) {
image_count = swapchain_support.capabilities.maxImageCount;
}
const bool queue_families_differ = m_queue_family_indices.graphics.value != m_queue_family_indices.present.value;
VK_TRY(false,
vkCreateSwapchainKHR(
g_dev,
&(VkSwapchainCreateInfoKHR){
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = NULL,
.flags = 0,
.surface = m_surface,
.minImageCount = image_count,
.imageFormat = surface_format.format,
.imageColorSpace = surface_format.colorSpace,
.imageExtent = extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode =
queue_families_differ
? VK_SHARING_MODE_CONCURRENT
: VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount =
queue_families_differ
? 2
: 0,
.pQueueFamilyIndices =
queue_families_differ
? (const uint32_t []){
m_queue_family_indices.graphics.value,
m_queue_family_indices.present.value
}
: NULL,
.preTransform = swapchain_support.capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = present_mode,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
},
NULL,
&g_swapchain
)
);
vkGetSwapchainImagesKHR(g_dev, g_swapchain, &m_swapchain_images.allocated, NULL);
m_swapchain_images = PTK_LIST_NEW(VkImage, m_swapchain_images.allocated);
vkGetSwapchainImagesKHR(g_dev, g_swapchain, &m_swapchain_images.allocated, m_swapchain_images.data);
PTK_LIST_FILLED(m_swapchain_images);
m_swapchain_image_format = surface_format.format;
m_swapchain_extent = extent;
PTK_LIST_FREE(swapchain_support.formats);
PTK_LIST_FREE(swapchain_support.present_modes);
return true;
}
bool create_image_views(void) {
m_swapchain_image_views = PTK_LIST_NEW(VkImageView, m_swapchain_images.size);
PTK_LIST_FOR_EACH_E(VkImage, m_swapchain_images, i, swapchain_image, {
VK_TRY(false,
vkCreateImageView(
g_dev,
&(VkImageViewCreateInfo){
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.image = swapchain_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = m_swapchain_image_format,
.components = (VkComponentMapping){
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = (VkImageSubresourceRange){
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
NULL,
&m_swapchain_image_views.data[i]
)
);
m_swapchain_image_views.size += 1;
})
return true;
}
PTK_STRING read_spv(const char *filename) {
errno = 0;
FILE *file = fopen(filename, "r");
@ -648,61 +167,6 @@ PTK_OPTION(VkShaderModule) create_shader_module(const PTK_STRING code) {
return PTK_OPTION_SOME(VkShaderModule, shader_module);
}
bool create_render_pass(void) {
VK_TRY(false,
vkCreateRenderPass(
g_dev,
&(VkRenderPassCreateInfo){
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.attachmentCount = 1,
.pAttachments = &(VkAttachmentDescription){
.flags = 0,
.format = m_swapchain_image_format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
.subpassCount = 1,
.pSubpasses = &(VkSubpassDescription){
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.pInputAttachments = NULL,
.colorAttachmentCount = 1,
.pColorAttachments = &(VkAttachmentReference){
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
},
.pResolveAttachments = NULL,
.pDepthStencilAttachment = NULL,
.preserveAttachmentCount = 0,
.pPreserveAttachments = NULL,
},
.dependencyCount = 1,
.pDependencies = &(VkSubpassDependency){
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dependencyFlags = 0,
},
},
NULL,
&m_render_pass
)
);
return true;
}
bool create_descriptor_set_layout(void) {
VK_TRY(false,
vkCreateDescriptorSetLayout(
@ -812,8 +276,8 @@ bool create_graphics_pipeline(void) {
.pViewports = &(VkViewport){
.x = 0.0f,
.y = 0.0f,
.width = (float) m_swapchain_extent.width,
.height = (float) m_swapchain_extent.height,
.width = (float) g_swapchain_extent.width,
.height = (float) g_swapchain_extent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f,
},
@ -823,7 +287,7 @@ bool create_graphics_pipeline(void) {
.x = 0,
.y = 0,
},
.extent = m_swapchain_extent,
.extent = g_swapchain_extent,
},
};
@ -918,7 +382,7 @@ bool create_graphics_pipeline(void) {
.pColorBlendState = &color_blending,
.pDynamicState = &dynamic_state,
.layout = m_pipeline_layout,
.renderPass = m_render_pass,
.renderPass = g_render_pass,
.subpass = 0,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = -1,
@ -934,36 +398,6 @@ bool create_graphics_pipeline(void) {
return true;
}
bool create_framebuffers(void) {
m_swapchain_framebuffers = PTK_LIST_NEW(VkFramebuffer, m_swapchain_image_views.size);
VkFramebuffer fb;
PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, swapchain_image_view, {
VK_TRY(false,
vkCreateFramebuffer(
g_dev,
&(VkFramebufferCreateInfo){
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.renderPass = m_render_pass,
.attachmentCount = 1,
.pAttachments = &swapchain_image_view,
.width = m_swapchain_extent.width,
.height = m_swapchain_extent.height,
.layers = 1,
},
NULL,
&fb
)
);
PTK_LIST_ADD(VkFramebuffer, m_swapchain_framebuffers, fb);
})
return true;
}
bool create_command_pool(void) {
VK_TRY(false,
vkCreateCommandPool(
@ -972,7 +406,7 @@ bool create_command_pool(void) {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = NULL,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = m_queue_family_indices.graphics.value,
.queueFamilyIndex = g_queue_family_indices.graphics.value,
},
NULL,
&m_command_pool
@ -984,7 +418,7 @@ bool create_command_pool(void) {
PTK_OPTION(uint32_t) find_memory_type(const uint32_t type_filter, const VkMemoryPropertyFlags props) {
VkPhysicalDeviceMemoryProperties mem_props;
vkGetPhysicalDeviceMemoryProperties(m_physical_dev, &mem_props);
vkGetPhysicalDeviceMemoryProperties(g_physical_dev, &mem_props);
for (uint32_t i = 0; i < mem_props.memoryTypeCount; ++i) {
if (type_filter & (1 << i) && (mem_props.memoryTypes[i].propertyFlags & props) == props) {
@ -1345,14 +779,14 @@ bool vk_record_command_buffer(const VkCommandBuffer command_buffer, const uint32
&(VkRenderPassBeginInfo){
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.pNext = NULL,
.renderPass = m_render_pass,
.framebuffer = m_swapchain_framebuffers.data[image_index],
.renderPass = g_render_pass,
.framebuffer = g_swapchain_framebuffers.data[image_index],
.renderArea = (VkRect2D){
.offset = (VkOffset2D){
.x = 0,
.y = 0,
},
.extent = m_swapchain_extent,
.extent = g_swapchain_extent,
},
.clearValueCount = 1,
.pClearValues = &(VkClearValue){
@ -1423,55 +857,9 @@ bool create_sync_objects(void) {
return true;
}
void cleanup_swapchain(void) {
PTK_LIST_FOR_EACH(VkFramebuffer, m_swapchain_framebuffers, fb, {
vkDestroyFramebuffer(g_dev, fb, NULL);
})
PTK_LIST_FREE(m_swapchain_framebuffers);
PTK_LIST_FREE(m_swapchain_images);
PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, iv, {
vkDestroyImageView(g_dev, iv, NULL);
})
PTK_LIST_FREE(m_swapchain_image_views);
vkDestroySwapchainKHR(g_dev, g_swapchain, NULL);
}
bool vk_recreate_swapchain(void) {
int width = 0, height = 0;
glfwGetFramebufferSize(m_window, &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(m_window, &width, &height);
glfwWaitEvents();
}
vkDeviceWaitIdle(g_dev);
cleanup_swapchain();
if (!create_swapchain()) {
PTK_ERR("failed creating new swapchain");
return false;
}
if (!create_image_views()) {
PTK_ERR("failed creating new image views");
return false;
}
if (!create_framebuffers()) {
PTK_ERR("failed creating new framebuffers");
return false;
}
return true;
}
bool vk_update_uniform_buffer(const size_t current_frame) {
m_uniform_buffer_object.window_size.w = m_swapchain_extent.width;
m_uniform_buffer_object.window_size.h = m_swapchain_extent.height;
m_uniform_buffer_object.window_size.w = g_swapchain_extent.width;
m_uniform_buffer_object.window_size.h = g_swapchain_extent.height;
memcpy(m_uniform_buffers_mapped.data[current_frame], &m_uniform_buffer_object, sizeof(m_uniform_buffer_object));
@ -1483,34 +871,34 @@ bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const
m_uniform_buffer_object.initial_window_size.w = width;
m_uniform_buffer_object.initial_window_size.h = height;
if (!create_vk_instance(title, version)) {
if (!vk_instance_create(title, version, m_validation_layers)) {
PTK_ERR("failed creating VkInstance");
return false;
}
if (!select_physical_dev()) {
if (!vk_select_physical_dev(g_instance)) {
PTK_ERR("failed selecting physical device");
return false;
}
VK_TRY(false, glfwCreateWindowSurface(m_instance, m_window, NULL, &m_surface));
VK_TRY(false, glfwCreateWindowSurface(g_instance, m_window, NULL, &g_surface));
if (!create_logical_dev()) {
if (!vk_create_logical_dev(g_physical_dev, g_surface, m_validation_layers)) {
PTK_ERR("failed creating logical device");
return false;
}
if (!create_swapchain()) {
if (!vk_create_swapchain(m_window, g_dev, g_physical_dev, g_surface)) {
PTK_ERR("failed creating swapchain");
return false;
}
if (!create_image_views()) {
if (!vk_create_image_views(g_dev)) {
PTK_ERR("failed creating image views");
return false;
}
if (!create_render_pass()) {
if (!vk_create_render_pass()) {
PTK_ERR("failed creating render pass");
return false;
}
@ -1525,7 +913,7 @@ bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const
return false;
}
if (!create_framebuffers()) {
if (!vk_create_framebuffers()) {
PTK_ERR("failed creating framebuffers");
return false;
}
@ -1574,12 +962,12 @@ bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const
}
void vk_cleanup(void) {
cleanup_swapchain();
vk_cleanup_swapchain(g_dev);
vkDestroyPipeline(g_dev, m_pipeline, NULL);
vkDestroyPipelineLayout(g_dev, m_pipeline_layout, NULL);
vkDestroyRenderPass(g_dev, m_render_pass, NULL);
vkDestroyRenderPass(g_dev, g_render_pass, NULL);
for (size_t i = 0; i < g_max_frames_in_flight; ++i) {
vkDestroyBuffer(g_dev, m_uniform_buffers.data[i], NULL);
@ -1609,8 +997,8 @@ void vk_cleanup(void) {
vkDestroyDevice(g_dev, NULL);
vkDestroySurfaceKHR(m_instance, m_surface, NULL);
vkDestroyInstance(m_instance, NULL);
vkDestroySurfaceKHR(g_instance, g_surface, NULL);
vkDestroyInstance(g_instance, NULL);
glfwDestroyWindow(m_window);

View file

@ -14,9 +14,6 @@ PTK_LIST_DEFINE(VkCommandBuffer);
PTK_LIST_DEFINE(VkSemaphore);
PTK_LIST_DEFINE(VkFence);
extern VkDevice g_dev;
extern VkSwapchainKHR g_swapchain;
extern VkQueue g_graphics_queue;
extern VkQueue g_present_queue;
@ -28,14 +25,14 @@ extern PTK_LIST(VkSemaphore) g_image_available_semaphores;
extern PTK_LIST(VkSemaphore) g_render_finished_semaphores;
extern PTK_LIST(VkFence) g_in_flight_fences;
extern VkSurfaceKHR g_surface;
bool vk_init(GLFWwindow *window, const size_t width, const size_t height, const char *title, const PtkVersion version);
bool vk_transfer_vertex_data(void);
bool vk_record_command_buffer(const VkCommandBuffer command_buffer, const uint32_t image_index);
bool vk_recreate_swapchain(void);
bool vk_update_uniform_buffer(const size_t current_frame);
void vk_cleanup(void);

89
src/ptk_vk/instance.c Normal file
View file

@ -0,0 +1,89 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#include <ptk_vk/instance.h>
#include <ptk_vk/utils.h>
#include <ptk_array.h>
#ifndef GLFW_INCLUDE_VULKAN
#define GLFW_INCLUDE_VULKAN
#endif
#include <GLFW/glfw3.h>
#ifdef DEBUG
#include <string.h>
PTK_LIST_DEFINE(VkLayerProperties);
bool check_validation_layers(const PTK_ARRAY(constcharptr) validation_layers) {
PTK_LIST(VkLayerProperties) available_layers;
vkEnumerateInstanceLayerProperties(&available_layers.allocated, NULL);
available_layers = PTK_LIST_NEW(VkLayerProperties, available_layers.allocated);
vkEnumerateInstanceLayerProperties(&available_layers.allocated, available_layers.data);
PTK_LIST_FILLED(available_layers);
for (size_t i = 0; i < validation_layers.size; ++i) {
const char *layer_name = validation_layers.data[i];
bool layer_found = false;
PTK_LIST_FOR_EACH(const VkLayerProperties, available_layers, layer_properties, {
if (strcmp(layer_name, layer_properties.layerName) == 0) {
layer_found = true;
break;
}
})
if (!layer_found) {
return false;
}
}
return true;
}
#endif
VkInstance g_instance;
bool vk_instance_create(const char *title, const PtkVersion version, const PTK_ARRAY(constcharptr) validation_layers) {
#ifdef DEBUG
if (!check_validation_layers(validation_layers)) {
PTK_ERR("couldn't find requested validation layer");
return PTK_OPTION_NONE(VkInstance);
}
#endif
PTK_ARRAY(constcharptr) extension_names = PTK_ARRAY_EMPTY(constcharptr);
extension_names.data = glfwGetRequiredInstanceExtensions(&extension_names.size);
VK_TRY(false,
vkCreateInstance(
&(VkInstanceCreateInfo){
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.pApplicationInfo = &(VkApplicationInfo){
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = NULL,
.pApplicationName = title,
.applicationVersion = VK_MAKE_API_VERSION(0, version.major, version.minor, version.patch),
.pEngineName = PTK_ENGINE_NAME,
.engineVersion = VK_MAKE_API_VERSION(0, PTK_VERSION_MAJOR, PTK_VERSION_MINOR, PTK_VERSION_PATCH),
.apiVersion = VK_API_VERSION_1_3,
},
.enabledLayerCount = validation_layers.size,
.ppEnabledLayerNames = validation_layers.data,
.enabledExtensionCount = extension_names.size,
.ppEnabledExtensionNames = extension_names.data,
},
NULL,
&g_instance
)
);
return true;
}

20
src/ptk_vk/instance.h Normal file
View file

@ -0,0 +1,20 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#ifndef PTK_PTK_VK_INSTANCE_H_
#define PTK_PTK_VK_INSTANCE_H_
#include <vulkan/vulkan_core.h>
#include <ptk.h>
#include <ptk_array.h>
#include <ptk_option.h>
extern VkInstance g_instance;
PTK_OPTION_DEFINE(VkInstance);
typedef const char *constcharptr;
PTK_ARRAY_DEFINE(constcharptr);
bool vk_instance_create(const char *title, const PtkVersion version, const PTK_ARRAY(constcharptr) validation_layers);
#endif // PTK_PTK_VK_INSTANCE_H_

View file

@ -0,0 +1,81 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#include <ptk_vk/physical_device.h>
#include <ptk_log.h>
#include <vulkan/vulkan.h>
VkPhysicalDevice g_physical_dev;
bool vk_select_physical_dev(const VkInstance instance) {
uint32_t dev_count = 0;
vkEnumeratePhysicalDevices(instance, &dev_count, NULL);
if (dev_count == 0) {
PTK_ERR("failed to find GPU with vulkan support");
return false;
}
VkPhysicalDevice devs[dev_count];
vkEnumeratePhysicalDevices(instance, &dev_count, devs);
VkPhysicalDeviceProperties dev_props[dev_count];
uint32_t dgpus[dev_count];
uint32_t dgpu_count = 0;
uint32_t igpus[dev_count];
uint32_t igpu_count = 0;
VkPhysicalDeviceMemoryProperties dev_mem_props[dev_count];
uint32_t dev_mem_counts[dev_count];
VkDeviceSize dev_mem_totals[dev_count];
for (size_t i = 0; i < dev_count; ++i) {
vkGetPhysicalDeviceProperties(devs[i], &dev_props[i]);
if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
dgpus[dgpu_count] = i;
dgpu_count += 1;
} else if (dev_props[i].deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
igpus[igpu_count] = i;
igpu_count += 1;
}
vkGetPhysicalDeviceMemoryProperties(devs[i], &dev_mem_props[i]);
dev_mem_counts[i] = dev_mem_props[i].memoryHeapCount;
dev_mem_totals[i] = 0;
for (size_t j = 0; j < dev_mem_counts[i]; ++j) {
dev_mem_totals[i] += dev_mem_props[i].memoryHeaps[j].size;
}
}
VkDeviceSize max_mem_size = 0;
size_t dev_best_index = 0;
if (dgpu_count != 0) {
for (size_t i = 0; i < dgpu_count; ++i) {
if (dev_mem_totals[i] > max_mem_size) {
dev_best_index = dgpus[i];
max_mem_size = dev_mem_totals[i];
}
}
} else if (igpu_count != 0) {
for (size_t i = 0; i < igpu_count; ++i) {
if (dev_mem_totals[i] > max_mem_size) {
dev_best_index = igpus[i];
max_mem_size = dev_mem_totals[i];
}
}
}
PTK_DEBUG("best device index: %ld", dev_best_index);
PTK_DEBUG("device name: %s", dev_props[dev_best_index].deviceName);
PTK_DEBUG("device type: %s", (dgpu_count != 0 ? "dgpu" : (igpu_count != 0 ? "igpu" : "probably cpu")));
PTK_DEBUG("total memory: %lu", dev_mem_totals[dev_best_index]);
g_physical_dev = devs[dev_best_index];
return true;
}

View file

@ -0,0 +1,16 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#ifndef PTK_PTK_VK_PHYSICAL_DEVICE_H_
#define PTK_PTK_VK_PHYSICAL_DEVICE_H_
#include <vulkan/vulkan_core.h>
#include <ptk_option.h>
extern VkPhysicalDevice g_physical_dev;
PTK_OPTION_DEFINE(VkPhysicalDevice);
bool vk_select_physical_dev(const VkInstance instance);
#endif // PTK_PTK_VK_PHYSICAL_DEVICE_H_

66
src/ptk_vk/render_pass.c Normal file
View file

@ -0,0 +1,66 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#include <ptk_vk/render_pass.h>
#include <ptk_vk/swapchain.h>
#include <ptk_vk/device.h>
#include <ptk_vk/utils.h>
VkRenderPass g_render_pass;
bool vk_create_render_pass(void) {
VK_TRY(false,
vkCreateRenderPass(
g_dev,
&(VkRenderPassCreateInfo){
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.attachmentCount = 1,
.pAttachments = &(VkAttachmentDescription){
.flags = 0,
.format = g_swapchain_image_format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
.subpassCount = 1,
.pSubpasses = &(VkSubpassDescription){
.flags = 0,
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.pInputAttachments = NULL,
.colorAttachmentCount = 1,
.pColorAttachments = &(VkAttachmentReference){
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
},
.pResolveAttachments = NULL,
.pDepthStencilAttachment = NULL,
.preserveAttachmentCount = 0,
.pPreserveAttachments = NULL,
},
.dependencyCount = 1,
.pDependencies = &(VkSubpassDependency){
.srcSubpass = VK_SUBPASS_EXTERNAL,
.dstSubpass = 0,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
.dependencyFlags = 0,
},
},
NULL,
&g_render_pass
)
);
return true;
}

14
src/ptk_vk/render_pass.h Normal file
View file

@ -0,0 +1,14 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#ifndef PTK_PTK_VK_RENDER_PASS_H_
#define PTK_PTK_VK_RENDER_PASS_H_
#include <stdbool.h>
#include <vulkan/vulkan.h>
extern VkRenderPass g_render_pass;
bool vk_create_render_pass(void);
#endif // PTK_PTK_VK_RENDER_PASS_H_

296
src/ptk_vk/swapchain.c Normal file
View file

@ -0,0 +1,296 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#include <ptk_vk/swapchain.h>
#include <ptk_vk/device.h>
#include <ptk_vk/physical_device.h>
#include <ptk_vk/render_pass.h>
#include <ptk_vk/utils.h>
#include <ptk_list.h>
#ifndef GLFW_INCLUDE_VULKAN
#define GLFW_INCLUDE_VULKAN
#endif
#include <GLFW/glfw3.h>
VkSwapchainKHR g_swapchain = VK_NULL_HANDLE;
PTK_LIST_DEFINE(VkImage);
PTK_LIST_DEFINE(VkImageView);
static PTK_LIST(VkImage) m_swapchain_images;
static PTK_LIST(VkImageView) m_swapchain_image_views;
VkFormat g_swapchain_image_format;
VkExtent2D g_swapchain_extent;
PTK_LIST(VkFramebuffer) g_swapchain_framebuffers;
VkSurfaceFormatKHR select_swap_surface_format(const PTK_LIST(VkSurfaceFormatKHR) available_formats) {
PTK_LIST_FOR_EACH(const VkSurfaceFormatKHR, available_formats, current_format, {
if (current_format.format == VK_FORMAT_B8G8R8A8_SRGB && current_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return current_format;
}
})
return available_formats.data[0];
}
VkPresentModeKHR select_swap_present_mode(const PTK_LIST(VkPresentModeKHR) available_present_modes) {
PTK_LIST_FOR_EACH(const VkPresentModeKHR, available_present_modes, current_present_mode, {
if (current_present_mode == VK_PRESENT_MODE_MAILBOX_KHR) {
return current_present_mode;
}
})
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D select_swap_extent(const VkSurfaceCapabilitiesKHR capabilities, GLFWwindow *window) {
if (capabilities.currentExtent.width != UINT32_MAX) {
return capabilities.currentExtent;
}
int width, height;
glfwGetFramebufferSize(window, &width, &height);
VkExtent2D actual_extent = {
.width = (uint32_t)width,
.height = (uint32_t)height,
};
if (actual_extent.width < capabilities.minImageExtent.width) {
actual_extent.width = capabilities.minImageExtent.width;
} else if (actual_extent.width > capabilities.maxImageExtent.width) {
actual_extent.width = capabilities.maxImageExtent.width;
}
if (actual_extent.height < capabilities.minImageExtent.height) {
actual_extent.height = capabilities.minImageExtent.height;
} else if (actual_extent.height > capabilities.maxImageExtent.height) {
actual_extent.height = capabilities.maxImageExtent.height;
}
return actual_extent;
}
SwapchainSupportInfo query_swapchain_support(const VkPhysicalDevice dev, const VkSurfaceKHR surface) {
SwapchainSupportInfo info;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(dev, surface, &info.capabilities);
const uint32_t width = info.capabilities.currentExtent.width;
const uint32_t height = info.capabilities.currentExtent.height;
if (
info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR
|| info.capabilities.currentTransform & VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR
) {
info.capabilities.currentExtent.height = width;
info.capabilities.currentExtent.width = height;
}
vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &info.formats.allocated, NULL);
if (info.formats.allocated != 0) {
info.formats = PTK_LIST_NEW(VkSurfaceFormatKHR, info.formats.allocated);
vkGetPhysicalDeviceSurfaceFormatsKHR(dev, surface, &info.formats.allocated, info.formats.data);
info.formats.size = info.formats.allocated;
}
vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &info.present_modes.allocated, NULL);
if (info.present_modes.allocated != 0) {
info.present_modes = PTK_LIST_NEW(VkPresentModeKHR, info.present_modes.allocated);
vkGetPhysicalDeviceSurfacePresentModesKHR(dev, surface, &info.present_modes.allocated, info.present_modes.data);
info.present_modes.size = info.present_modes.allocated;
}
return info;
}
bool vk_create_image_views(VkDevice dev) {
m_swapchain_image_views = PTK_LIST_NEW(VkImageView, m_swapchain_images.size);
PTK_LIST_FOR_EACH_E(VkImage, m_swapchain_images, i, swapchain_image, {
VK_TRY(false,
vkCreateImageView(
dev,
&(VkImageViewCreateInfo){
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.image = swapchain_image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = g_swapchain_image_format,
.components = (VkComponentMapping){
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY,
},
.subresourceRange = (VkImageSubresourceRange){
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
NULL,
&m_swapchain_image_views.data[i]
)
);
m_swapchain_image_views.size += 1;
})
return true;
}
bool vk_create_swapchain(GLFWwindow *window, VkDevice dev, VkPhysicalDevice physical_dev, VkSurfaceKHR surface) {
const SwapchainSupportInfo swapchain_support = query_swapchain_support(physical_dev, surface);
const VkSurfaceFormatKHR surface_format = select_swap_surface_format(swapchain_support.formats);
const VkPresentModeKHR present_mode = select_swap_present_mode(swapchain_support.present_modes);
const VkExtent2D extent = select_swap_extent(swapchain_support.capabilities, window);
uint32_t image_count = swapchain_support.capabilities.minImageCount + 1;
if (swapchain_support.capabilities.maxImageCount > 0 && image_count > swapchain_support.capabilities.maxImageCount) {
image_count = swapchain_support.capabilities.maxImageCount;
}
const bool queue_families_differ = g_queue_family_indices.graphics.value != g_queue_family_indices.present.value;
VK_TRY(false,
vkCreateSwapchainKHR(
dev,
&(VkSwapchainCreateInfoKHR){
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = NULL,
.flags = 0,
.surface = surface,
.minImageCount = image_count,
.imageFormat = surface_format.format,
.imageColorSpace = surface_format.colorSpace,
.imageExtent = extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
.imageSharingMode =
queue_families_differ
? VK_SHARING_MODE_CONCURRENT
: VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount =
queue_families_differ
? 2
: 0,
.pQueueFamilyIndices =
queue_families_differ
? (const uint32_t []){
g_queue_family_indices.graphics.value,
g_queue_family_indices.present.value
}
: NULL,
.preTransform = swapchain_support.capabilities.currentTransform,
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
.presentMode = present_mode,
.clipped = VK_TRUE,
.oldSwapchain = VK_NULL_HANDLE,
},
NULL,
&g_swapchain
)
);
vkGetSwapchainImagesKHR(dev, g_swapchain, &m_swapchain_images.allocated, NULL);
m_swapchain_images = PTK_LIST_NEW(VkImage, m_swapchain_images.allocated);
vkGetSwapchainImagesKHR(dev, g_swapchain, &m_swapchain_images.allocated, m_swapchain_images.data);
PTK_LIST_FILLED(m_swapchain_images);
g_swapchain_image_format = surface_format.format;
g_swapchain_extent = extent;
PTK_LIST_FREE(swapchain_support.formats);
PTK_LIST_FREE(swapchain_support.present_modes);
return true;
}
bool vk_recreate_swapchain(GLFWwindow *window, VkSurfaceKHR surface) {
int width = 0, height = 0;
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
vkDeviceWaitIdle(g_dev);
vk_cleanup_swapchain(g_dev);
if (!vk_create_swapchain(window, g_dev, g_physical_dev, surface)) {
PTK_ERR("failed creating new swapchain");
return false;
}
if (!vk_create_image_views(g_dev)) {
PTK_ERR("failed creating new image views");
return false;
}
if (!vk_create_framebuffers()) {
PTK_ERR("failed creating new framebuffers");
return false;
}
return true;
}
bool vk_create_framebuffers(void) {
g_swapchain_framebuffers = PTK_LIST_NEW(VkFramebuffer, m_swapchain_image_views.size);
VkFramebuffer fb;
PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, swapchain_image_view, {
VK_TRY(false,
vkCreateFramebuffer(
g_dev,
&(VkFramebufferCreateInfo){
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.pNext = NULL,
.flags = 0,
.renderPass = g_render_pass,
.attachmentCount = 1,
.pAttachments = &swapchain_image_view,
.width = g_swapchain_extent.width,
.height = g_swapchain_extent.height,
.layers = 1,
},
NULL,
&fb
)
);
PTK_LIST_ADD(VkFramebuffer, g_swapchain_framebuffers, fb);
})
return true;
}
void vk_cleanup_swapchain(VkDevice dev) {
PTK_LIST_FOR_EACH(VkFramebuffer, g_swapchain_framebuffers, fb, {
vkDestroyFramebuffer(dev, fb, NULL);
})
PTK_LIST_FREE(g_swapchain_framebuffers);
PTK_LIST_FREE(m_swapchain_images);
PTK_LIST_FOR_EACH(VkImageView, m_swapchain_image_views, iv, {
vkDestroyImageView(dev, iv, NULL);
})
PTK_LIST_FREE(m_swapchain_image_views);
vkDestroySwapchainKHR(dev, g_swapchain, NULL);
}

40
src/ptk_vk/swapchain.h Normal file
View file

@ -0,0 +1,40 @@
// Copyright (jacekpoz 2024). Licensed under the EUPL-1.2 or later.
#ifndef PTK_PTK_VK_SWAPCHAIN_H_
#define PTK_PTK_VK_SWAPCHAIN_H_
#include <vulkan/vulkan_core.h>
#include <GLFW/glfw3.h>
#include <ptk_list.h>
#include <ptk_option.h>
PTK_LIST_DEFINE(VkFramebuffer);
extern VkSwapchainKHR g_swapchain;
extern VkFormat g_swapchain_image_format;
extern VkExtent2D g_swapchain_extent;
extern PTK_LIST(VkFramebuffer) g_swapchain_framebuffers;
PTK_LIST_DEFINE(VkSurfaceFormatKHR);
PTK_LIST_DEFINE(VkPresentModeKHR);
typedef struct {
VkSurfaceCapabilitiesKHR capabilities;
PTK_LIST(VkSurfaceFormatKHR) formats;
PTK_LIST(VkPresentModeKHR) present_modes;
} SwapchainSupportInfo;
SwapchainSupportInfo query_swapchain_support(const VkPhysicalDevice dev, const VkSurfaceKHR surface);
bool vk_create_swapchain(GLFWwindow *window, VkDevice dev, VkPhysicalDevice physical_dev, VkSurfaceKHR surface);
bool vk_create_image_views(VkDevice dev);
bool vk_recreate_swapchain(GLFWwindow *window, VkSurfaceKHR surface);
bool vk_create_framebuffers(void);
void vk_cleanup_swapchain(VkDevice dev);
#endif // PTK_PTK_VK_SWAPCHAIN_H_