summaryrefslogtreecommitdiff
path: root/Rend/rend_vk_internal.c
diff options
context:
space:
mode:
Diffstat (limited to 'Rend/rend_vk_internal.c')
-rw-r--r--Rend/rend_vk_internal.c1014
1 files changed, 1014 insertions, 0 deletions
diff --git a/Rend/rend_vk_internal.c b/Rend/rend_vk_internal.c
new file mode 100644
index 0000000..4f1679f
--- /dev/null
+++ b/Rend/rend_vk_internal.c
@@ -0,0 +1,1014 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vulkan/vulkan.h>
+#include <vulkan/vulkan_core.h>
+
+#include "rend.h"
+#include "rend_internal.h"
+
+#define CHECK_VK_RESULT(r) \
+{ \
+ assert(r == VK_SUCCESS && __LINE__); \
+}
+
+typedef struct RendVkImage RendVkImage;
+typedef struct RendVkArenaAllocator RendVkArenaAllocator;
+
+typedef struct {
+ VkSurfaceCapabilitiesKHR capabilities;
+ VkSurfaceFormatKHR *format;
+ VkPresentModeKHR *present_modes;
+ uint32_t format_count;
+ uint32_t present_mode_count;
+} RendVkSwapchainSupport;
+
+typedef struct {
+ VkDevice logical_device;
+
+ VkPhysicalDeviceProperties properties;
+ VkPhysicalDeviceMemoryProperties memory;
+ VkPhysicalDevice physical_device;
+ VkPhysicalDeviceFeatures features;
+
+ VkSurfaceKHR surface;
+ RendVkSwapchainSupport swapchain_support;
+ VkFormat depth_format;
+
+ VkQueue graphics_queue;
+ VkQueue present_queue;
+ VkQueue compute_queue;
+ VkQueue transfer_queue;
+ uint32_t graphics_family_index;
+ uint32_t present_family_index;
+ uint32_t compute_family_index;
+ uint32_t transfer_family_index;
+
+ uint32_t host_index;
+ uint32_t device_index;
+} RendVkDevice;
+
+typedef struct RendVkPage {
+ RendMemory memory;
+ size_t head;
+ int reserved;
+} RendVkPage;
+
+typedef struct RendVkPagedArena {
+ RendVkPage *page_darr;
+ uint32_t capacity;
+ uint32_t elements;
+} RendVkPagedArena;
+
+struct RendVkArenaAllocator {
+ VkAllocationCallbacks *allocator;
+ RendVkPagedArena *mem_arenas;
+ VkDevice logical_device;
+ VkPhysicalDevice physical_device;
+ VkDeviceSize gpu_alignment;
+ VkDeviceSize block_min_size;
+ uint32_t heap_index_count;
+ VkPhysicalDeviceMemoryProperties properties;
+};
+
+struct RendVkImage {
+ VkDevice logical_device;
+ VkImage handle;
+ VkImageView view;
+
+ VkMemoryRequirements requirements;
+
+ VkImageType img_type;
+ uint32_t width, height;
+ VkFormat format;
+ VkImageTiling tiling;
+ VkImageUsageFlags usage;
+ uint32_t depth;
+ uint32_t mip_levels;
+ uint32_t layers;
+ VkSampleCountFlags sample_count_flags;
+ VkSharingMode sharing_mode;
+
+ RendMemory *memory;
+};
+
+typedef struct RendVkAllocatorState {
+ const char *name;
+} RendVkAllocatorState;
+
+typedef struct RendVkAllocatorHeader {
+ uint32_t offset;
+} RendVkAllocatorHeader;
+
+static bool rend_vk_allocator_is_power_of_two(uintptr_t x);
+static uintptr_t rend_vk_allocator_align_forward(uintptr_t ptr, size_t align);
+static void *rend_vk_allocator_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+static void *rend_vk_allocator_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope);
+static void rend_vk_allocator_free(void *pUserData, void *pMemory);
+static void rend_vk_allocator_internal_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope);
+static void rend_vk_allocator_free_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope);
+
+static VkAllocationCallbacks rend_vk_allocator = {
+ .pfnAllocation = rend_vk_allocator_alloc,
+ .pfnReallocation = rend_vk_allocator_realloc,
+ .pfnFree = rend_vk_allocator_free,
+ .pfnInternalAllocation = rend_vk_allocator_internal_notification,
+ .pfnInternalFree = rend_vk_allocator_free_notification,
+};
+
+static const char *rend_vk_allocator_scope_name[] = {
+ [VK_SYSTEM_ALLOCATION_SCOPE_CACHE] = "Cache",
+ [VK_SYSTEM_ALLOCATION_SCOPE_COMMAND] = "Command",
+ [VK_SYSTEM_ALLOCATION_SCOPE_DEVICE] = "Device",
+ [VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE] = "Instance",
+ [VK_SYSTEM_ALLOCATION_SCOPE_OBJECT] = "Object",
+};
+
+static VkInstance vk_instance = 0;
+static VkDebugUtilsMessengerEXT vk_debug_messenger = 0;
+static RendVkDevice vk_device = {0};
+static VkAllocationCallbacks *vk_allocator = &rend_vk_allocator;
+
+static VkFormat vk_format_from_rend_format[] = {
+ [REND_FORMAT_R8_UNORM] = VK_FORMAT_R8_UNORM,
+ [REND_FORMAT_R8G8_UNORM] = VK_FORMAT_R8G8_UNORM,
+ [REND_FORMAT_R8G8B8A8_UNORM] = VK_FORMAT_R8G8B8A8_UNORM,
+ [REND_FORMAT_B8G8R8A8_UNORM] = VK_FORMAT_B8G8R8A8_UNORM,
+ [REND_FORMAT_R8G8B8A8_SRGB] = VK_FORMAT_R8G8B8A8_SRGB,
+ [REND_FORMAT_B8G8R8A8_SRGB] = VK_FORMAT_B8G8R8A8_SRGB,
+ [REND_FORMAT_R32_SFLOAT] = VK_FORMAT_R32_SFLOAT,
+ [REND_FORMAT_R32G32_SFLOAT] = VK_FORMAT_R32G32_SFLOAT,
+ [REND_FORMAT_R32G32B32_SFLOAT] = VK_FORMAT_R32G32B32_SFLOAT,
+ [REND_FORMAT_R32G32B32A32_SFLOAT]= VK_FORMAT_R32G32B32A32_SFLOAT,
+ [REND_FORMAT_R16_SFLOAT] = VK_FORMAT_R16_SFLOAT,
+ [REND_FORMAT_R16G16_SFLOAT] = VK_FORMAT_R16G16_SFLOAT,
+ [REND_FORMAT_R16G16B16A16_SFLOAT]= VK_FORMAT_R16G16B16A16_SFLOAT,
+ [REND_FORMAT_R32_UINT] = VK_FORMAT_R32_UINT,
+ [REND_FORMAT_R32_SINT] = VK_FORMAT_R32_SINT,
+ [REND_FORMAT_R32G32B32A32_UINT] = VK_FORMAT_R32G32B32A32_UINT,
+ [REND_FORMAT_R16G16B16A16_UINT] = VK_FORMAT_R16G16B16A16_UINT,
+ [REND_FORMAT_R8G8B8A8_UINT] = VK_FORMAT_R8G8B8A8_UINT,
+ [REND_FORMAT_D32_SFLOAT] = VK_FORMAT_D32_SFLOAT,
+ [REND_FORMAT_D24_UNORM_S8_UINT] = VK_FORMAT_D24_UNORM_S8_UINT,
+ [REND_FORMAT_D32_SFLOAT_S8_UINT] = VK_FORMAT_D32_SFLOAT_S8_UINT,
+};
+
+static VkPrimitiveTopology vk_topology[] = {
+ [REND_TOPOLOGY_TRIANGLE_LIST] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
+ [REND_TOPOLOGY_TRIANGLE_STRIP] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
+ [REND_TOPOLOGY_LINE_LIST] = VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
+ [REND_TOPOLOGY_LINE_STRIP] = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
+ [REND_TOPOLOGY_POINT_LIST] = VK_PRIMITIVE_TOPOLOGY_POINT_LIST
+};
+
+static VkPolygonMode vk_polymode[] = {
+ [REND_POLYGON_MODE_FILL] = VK_POLYGON_MODE_FILL,
+ [REND_POLYGON_MODE_LINE] = VK_POLYGON_MODE_LINE,
+ [REND_POLYGON_MODE_POINT] = VK_POLYGON_MODE_POINT,
+};
+
+static VkCullModeFlags vk_cullflags[] = {
+ [REND_CULL_MODE_NONE] = VK_CULL_MODE_NONE,
+ [REND_CULL_MODE_FRONT] = VK_CULL_MODE_FRONT_BIT,
+ [REND_CULL_MODE_BACK] = VK_CULL_MODE_BACK_BIT,
+ [REND_CULL_MODE_FRONT_AND_BACK] = VK_CULL_MODE_FRONT_AND_BACK,
+};
+
+static const VkBufferUsageFlags vk_buffer_usage[] = {
+ [REND_BUFFER_VERTEX] = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ [REND_BUFFER_INDEX] = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ [REND_BUFFER_UNIFORM] = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+ [REND_BUFFER_STORAGE] = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ [REND_BUFFER_TRANSFER] = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
+};
+
+static const VkShaderStageFlagBits vk_pipeline_stages[][3] = {
+ [REND__PIPELINE_GRAPHICS] = { VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT },
+ [REND__PIPELINE_COMPUTE] = { VK_SHADER_STAGE_COMPUTE_BIT },
+ [REND__PIPELINE_MESH] = { VK_SHADER_STAGE_MESH_BIT_EXT, VK_SHADER_STAGE_FRAGMENT_BIT },
+};
+
+/* --- host allocator --- */
+
+static bool
+rend_vk_allocator_is_power_of_two(uintptr_t x)
+{
+ return (x & (x - 1)) == 0;
+}
+
+static uintptr_t
+rend_vk_allocator_align_forward(uintptr_t ptr, size_t align)
+{
+ uintptr_t p, a, modulo;
+
+ assert(rend_vk_allocator_is_power_of_two(align));
+
+ p = ptr;
+ a = (uintptr_t)align;
+ modulo = p & (a - 1);
+
+ if (modulo != 0) {
+ p += a - modulo;
+ }
+ return p;
+}
+
+static void *
+rend_vk_allocator_alloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+ size_t total_size;
+ uint8_t *raw_ptr;
+ uintptr_t unaligned_addr;
+ uint8_t *aligned_ptr;
+ RendVkAllocatorHeader *header;
+
+ (void)pUserData;
+ total_size = size + alignment + sizeof(RendVkAllocatorHeader);
+ raw_ptr = malloc(total_size);
+ unaligned_addr = (uintptr_t)(raw_ptr + sizeof(RendVkAllocatorHeader));
+
+ aligned_ptr = (uint8_t *)rend_vk_allocator_align_forward(unaligned_addr, alignment);
+ header = (RendVkAllocatorHeader *)aligned_ptr - 1;
+ header->offset = aligned_ptr - raw_ptr;
+
+ PDEBUG("[VK_ALLOC] %p - bytes %lu with alignment %lu - scope %s",
+ aligned_ptr, size, alignment, rend_vk_allocator_scope_name[allocationScope]);
+
+ return (void *)aligned_ptr;
+}
+
+static void *
+rend_vk_allocator_realloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope)
+{
+ void *new_ptr;
+
+ if (!pOriginal)
+ return rend_vk_allocator_alloc(pUserData, size, alignment, allocationScope);
+ if (size == 0) {
+ rend_vk_allocator_free(pUserData, pOriginal);
+ return NULL;
+ }
+ new_ptr = rend_vk_allocator_alloc(pUserData, size, alignment, allocationScope);
+ if (!new_ptr)
+ return NULL;
+ memcpy(new_ptr, pOriginal, size);
+ rend_vk_allocator_free(pUserData, pOriginal);
+ return new_ptr;
+}
+
+static void
+rend_vk_allocator_free(void *pUserData, void *pMemory)
+{
+ RendVkAllocatorHeader *ptr;
+ uint8_t *raw_ptr;
+
+ (void)pUserData;
+ if (!pMemory)
+ return;
+ ptr = pMemory;
+ raw_ptr = pMemory;
+ raw_ptr -= (ptr - 1)->offset;
+
+ PDEBUG("[VK_FREE] %p", ptr);
+ free(raw_ptr);
+}
+
+static void
+rend_vk_allocator_internal_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+ (void)pUserData;
+ (void)allocationType;
+ PDEBUG("[VK_ALLOC_INTERNAL] bytes %lu - scope %s",
+ size, rend_vk_allocator_scope_name[allocationScope]);
+}
+
+static void
+rend_vk_allocator_free_notification(void *pUserData, size_t size, VkInternalAllocationType allocationType, VkSystemAllocationScope allocationScope)
+{
+ (void)pUserData;
+ (void)allocationType;
+ PDEBUG("[VK_FREE_INTERNAL] bytes %lu - scope %s",
+ size, rend_vk_allocator_scope_name[allocationScope]);
+}
+
+/* --- device --- */
+
+static void
+rend_vk_device_query_swapchain_support(RendVkDevice *device)
+{
+ RASSERT(device->physical_device, "Invalid device pointer.");
+
+ CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device->physical_device, device->surface, &device->swapchain_support.capabilities));
+
+ CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(device->physical_device, device->surface, &device->swapchain_support.format_count, VK_NULL_HANDLE));
+ if (device->swapchain_support.format_count != 0) {
+ if (!device->swapchain_support.format) {
+ device->swapchain_support.format = rmalloc(device->swapchain_support.format_count * sizeof(*device->swapchain_support.format));
+ }
+ CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(device->physical_device, device->surface, &device->swapchain_support.format_count, device->swapchain_support.format));
+ }
+
+ CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(device->physical_device, device->surface, &device->swapchain_support.present_mode_count, VK_NULL_HANDLE));
+ if (device->swapchain_support.present_mode_count != 0) {
+ if (!device->swapchain_support.present_modes) {
+ device->swapchain_support.present_modes = rmalloc(device->swapchain_support.present_mode_count * sizeof(*device->swapchain_support.present_modes));
+ }
+ CHECK_VK_RESULT(vkGetPhysicalDeviceSurfacePresentModesKHR(device->physical_device, device->surface, &device->swapchain_support.present_mode_count, device->swapchain_support.present_modes));
+ }
+}
+
+static uint32_t
+rend_vk_device_score_default(RendVkDevice *device, RendSpecs minimum_specs, const char **required_extensions, uint32_t required_extension_count)
+{
+ uint32_t score;
+ uint32_t q_family_count;
+ uint8_t min_transfer_score;
+ uint32_t i;
+
+ score = 0;
+
+ device->graphics_family_index = UINT32_MAX;
+ device->present_family_index = UINT32_MAX;
+ device->compute_family_index = UINT32_MAX;
+ device->transfer_family_index = UINT32_MAX;
+
+ if (minimum_specs.discrete_gpu) {
+ if (device->properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
+ return 0;
+ }
+ }
+
+ q_family_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(device->physical_device, &q_family_count, VK_NULL_HANDLE);
+
+ {
+ VkQueueFamilyProperties q_family[q_family_count];
+
+ vkGetPhysicalDeviceQueueFamilyProperties(device->physical_device, &q_family_count, q_family);
+
+ min_transfer_score = 255;
+ for (i = 0; i < q_family_count; i++) {
+ uint8_t transfer_score;
+ VkBool32 supports_present;
+
+ transfer_score = 0;
+ if (q_family[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ device->graphics_family_index = i;
+ transfer_score++;
+ }
+
+ if (q_family[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
+ device->compute_family_index = i;
+ transfer_score++;
+ }
+
+ if (q_family[i].queueFlags & VK_QUEUE_TRANSFER_BIT) {
+ if (transfer_score <= min_transfer_score) {
+ min_transfer_score = transfer_score;
+ device->transfer_family_index = i;
+ }
+ }
+
+ supports_present = VK_FALSE;
+ CHECK_VK_RESULT(vkGetPhysicalDeviceSurfaceSupportKHR(device->physical_device, i, device->surface, &supports_present));
+ if (supports_present) {
+ device->present_family_index = i;
+ }
+ }
+ }
+
+ if ((minimum_specs.graphics && device->graphics_family_index == UINT32_MAX) ||
+ (minimum_specs.present && device->present_family_index == UINT32_MAX) ||
+ (minimum_specs.compute && device->compute_family_index == UINT32_MAX) ||
+ (minimum_specs.transfer && device->transfer_family_index == UINT32_MAX)) {
+ return 0;
+ }
+
+ rend_vk_device_query_swapchain_support(device);
+
+ if (device->swapchain_support.format_count < 1 || device->swapchain_support.present_mode_count < 1) {
+ return 0;
+ }
+
+ if (required_extensions) {
+ uint32_t available_extentions_count;
+ VkExtensionProperties *available_extentions;
+
+ available_extentions_count = 0;
+ available_extentions = NULL;
+ CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, VK_NULL_HANDLE));
+
+ if (available_extentions_count != 0) {
+ bool overall_found;
+ uint32_t j;
+
+ available_extentions = rmalloc(available_extentions_count * sizeof(*available_extentions));
+ CHECK_VK_RESULT(vkEnumerateDeviceExtensionProperties(device->physical_device, VK_NULL_HANDLE, &available_extentions_count, available_extentions));
+
+ overall_found = true;
+ for (i = 0; i < required_extension_count; ++i) {
+ bool found;
+
+ found = false;
+ for (j = 0; j < available_extentions_count; ++j) {
+ if (strcmp(required_extensions[i], available_extentions[j].extensionName) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ overall_found = false;
+ break;
+ }
+ }
+
+ rfree(available_extentions);
+ if (!overall_found) {
+ return 0;
+ }
+ }
+ }
+
+ if (minimum_specs.sampler_anisotropy && !device->features.samplerAnisotropy) {
+ return 0;
+ }
+
+ score = 10;
+ if (device->properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
+ score += 1000;
+ }
+
+ return score;
+}
+
+static bool
+rend_vk_device_create(VkSurfaceKHR surface, RendSpecs specs, RendVkDevice *out_device)
+{
+ uint32_t device_count;
+ const char *extension_names[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
+ uint32_t best_score;
+ RendVkDevice best_device;
+ uint32_t i;
+ bool present_shares_graphics_q;
+ bool transfer_shares_graphics_q;
+ uint32_t index_count;
+ uint8_t index;
+ float queue_priority[2];
+ VkPhysicalDeviceFeatures device_features;
+ VkPhysicalDeviceVulkan13Features vk13_features;
+ VkPhysicalDeviceVulkan12Features vk12_features;
+ VkPhysicalDeviceVulkan11Features vk11_features;
+ VkDeviceCreateInfo device_create_info;
+ const char *extention_names;
+ VkPhysicalDeviceMemoryProperties mem_props;
+ VkMemoryPropertyFlags host_flags;
+
+ assert(out_device);
+
+ device_count = 0;
+ CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, VK_NULL_HANDLE));
+ if (device_count == 0) {
+ PFATAL("No GPU with Vulkan support found!");
+ return false;
+ }
+
+ {
+ VkPhysicalDevice physical_devices[device_count];
+
+ CHECK_VK_RESULT(vkEnumeratePhysicalDevices(vk_instance, &device_count, physical_devices));
+
+ best_score = 1;
+ best_device = (RendVkDevice){0};
+
+ PDEBUG("DEVICE SCORE");
+ for (i = 0; i < device_count; i++) {
+ RendVkDevice scoring;
+ uint32_t dev_score;
+
+ scoring = (RendVkDevice){0};
+ scoring.surface = surface;
+ scoring.physical_device = physical_devices[i];
+ vkGetPhysicalDeviceProperties(physical_devices[i], &scoring.properties);
+ vkGetPhysicalDeviceFeatures(physical_devices[i], &scoring.features);
+ vkGetPhysicalDeviceMemoryProperties(physical_devices[i], &scoring.memory);
+
+ dev_score = rend_vk_device_score_default(&scoring, specs, extension_names, 1);
+ PDEBUG("%-20.20s %5d", scoring.properties.deviceName, dev_score);
+ if (dev_score >= best_score) {
+ if (best_device.swapchain_support.format) {
+ rfree(best_device.swapchain_support.format);
+ }
+ if (best_device.swapchain_support.present_modes) {
+ rfree(best_device.swapchain_support.present_modes);
+ }
+ best_score = dev_score;
+ best_device = scoring;
+ } else {
+ if (scoring.swapchain_support.format) {
+ rfree(scoring.swapchain_support.format);
+ }
+ if (scoring.swapchain_support.present_modes) {
+ rfree(scoring.swapchain_support.present_modes);
+ }
+ }
+ }
+ }
+
+ if (best_score > 1) {
+ PDEBUG("Driver version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.driverVersion), VK_VERSION_MINOR(best_device.properties.driverVersion), VK_VERSION_PATCH(best_device.properties.driverVersion));
+ PDEBUG("Vulkan API version %d.%d.%d", VK_VERSION_MAJOR(best_device.properties.apiVersion), VK_VERSION_MINOR(best_device.properties.apiVersion), VK_VERSION_PATCH(best_device.properties.apiVersion));
+ }
+
+ if (!best_device.physical_device) {
+ PERROR("No physical devices were found that meet specs!");
+ return false;
+ }
+
+ *out_device = best_device;
+
+ PDEBUG("Graphics Family Index: %u", out_device->graphics_family_index);
+ PDEBUG("Present Family Index: %u", out_device->present_family_index);
+ PDEBUG("Compute Family Index: %u", out_device->compute_family_index);
+ PDEBUG("Transfer Family Index: %u", out_device->transfer_family_index);
+
+ present_shares_graphics_q = out_device->present_family_index == out_device->graphics_family_index;
+ transfer_shares_graphics_q = out_device->transfer_family_index == out_device->graphics_family_index;
+
+ index_count = 1;
+ if (!present_shares_graphics_q)
+ index_count++;
+ if (!transfer_shares_graphics_q)
+ index_count++;
+
+ {
+ uint32_t indices[index_count];
+ VkDeviceQueueCreateInfo q_create_info[index_count];
+
+ index = 0;
+ indices[index++] = out_device->graphics_family_index;
+ if (!present_shares_graphics_q) {
+ indices[index++] = out_device->present_family_index;
+ }
+ if (!transfer_shares_graphics_q) {
+ indices[index++] = out_device->transfer_family_index;
+ }
+
+ queue_priority[0] = 1.0f;
+ queue_priority[1] = 1.0f;
+ for (i = 0; i < index_count; i++) {
+ q_create_info[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
+ q_create_info[i].queueFamilyIndex = indices[i];
+ q_create_info[i].queueCount = 1;
+ q_create_info[i].flags = 0;
+ q_create_info[i].pNext = 0;
+ q_create_info[i].pQueuePriorities = queue_priority;
+ }
+
+ device_features = (VkPhysicalDeviceFeatures){0};
+ device_features.samplerAnisotropy = specs.sampler_anisotropy;
+ device_features.fillModeNonSolid = VK_TRUE;
+ device_features.shaderInt64 = VK_TRUE;
+
+ vk13_features = (VkPhysicalDeviceVulkan13Features){0};
+ vk13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
+ vk13_features.dynamicRendering = VK_TRUE;
+ vk13_features.synchronization2 = VK_TRUE;
+
+ vk12_features = (VkPhysicalDeviceVulkan12Features){0};
+ vk12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
+ vk12_features.timelineSemaphore = VK_TRUE;
+ vk12_features.descriptorBindingPartiallyBound = VK_TRUE;
+ vk12_features.bufferDeviceAddress = VK_TRUE;
+ vk12_features.scalarBlockLayout = VK_TRUE;
+ vk12_features.pNext = &vk13_features;
+
+ vk11_features = (VkPhysicalDeviceVulkan11Features){0};
+ vk11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;
+ vk11_features.shaderDrawParameters = VK_TRUE;
+ vk11_features.pNext = &vk12_features;
+
+ device_create_info = (VkDeviceCreateInfo){ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
+ device_create_info.pNext = &vk11_features;
+ device_create_info.queueCreateInfoCount = index_count;
+ device_create_info.pQueueCreateInfos = q_create_info;
+ device_create_info.pEnabledFeatures = &device_features;
+ device_create_info.enabledExtensionCount = 1;
+
+ extention_names = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
+ device_create_info.ppEnabledExtensionNames = &extention_names;
+
+ device_create_info.enabledLayerCount = 0;
+ device_create_info.ppEnabledLayerNames = 0;
+
+ CHECK_VK_RESULT(vkCreateDevice(out_device->physical_device, &device_create_info, vk_allocator, &out_device->logical_device));
+ }
+
+ vkGetDeviceQueue(out_device->logical_device, out_device->graphics_family_index, 0, &out_device->graphics_queue);
+ vkGetDeviceQueue(out_device->logical_device, out_device->present_family_index, 0, &out_device->present_queue);
+ vkGetDeviceQueue(out_device->logical_device, out_device->transfer_family_index, 0, &out_device->transfer_queue);
+
+ PDEBUG("GRAPHICS | PRESENT | COMPUTE | TRANSFER | DEVICE");
+ PDEBUG(" %02d | %02d | %02d | %02d | %s",
+ out_device->graphics_family_index != UINT32_MAX,
+ out_device->present_family_index != UINT32_MAX,
+ out_device->compute_family_index != UINT32_MAX,
+ out_device->transfer_family_index != UINT32_MAX,
+ out_device->properties.deviceName);
+
+ mem_props = out_device->memory;
+ out_device->device_index = UINT32_MAX;
+ for (i = 0; i < mem_props.memoryTypeCount; i++) {
+ if ((mem_props.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
+ out_device->device_index = i;
+ break;
+ }
+ }
+
+ host_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ out_device->host_index = UINT32_MAX;
+ for (i = 0; i < mem_props.memoryTypeCount; i++) {
+ if ((mem_props.memoryTypes[i].propertyFlags & host_flags) == host_flags) {
+ out_device->host_index = i;
+ break;
+ }
+ }
+
+ PDEBUG("Device local heap: %2u", out_device->device_index);
+ PDEBUG("Host mapped heap: %2u", out_device->host_index);
+ return true;
+}
+
+static void
+rend_vk_device_destroy(RendVkDevice *device)
+{
+ RASSERT(device && device->logical_device, "Invalid or uninitialized device.");
+
+ vkDeviceWaitIdle(device->logical_device);
+ vkDestroyDevice(device->logical_device, vk_allocator);
+ if (device->swapchain_support.format)
+ rfree(vk_device.swapchain_support.format);
+ if (device->swapchain_support.present_modes)
+ rfree(vk_device.swapchain_support.present_modes);
+
+ *device = (RendVkDevice){0};
+}
+
+static bool
+rend_vk_device_detect_depth_format(RendVkDevice *device)
+{
+ const uint64_t candidate_count = 3;
+ VkFormat candidates[] = {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT};
+ uint32_t flags;
+ uint32_t i;
+ VkFormatProperties properties;
+
+ flags = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
+ for (i = 0; i < candidate_count; i++) {
+ vkGetPhysicalDeviceFormatProperties(device->physical_device, candidates[i], &properties);
+ if ((properties.linearTilingFeatures & flags) == flags) {
+ device->depth_format = candidates[i];
+ return true;
+ } else if ((properties.optimalTilingFeatures & flags) == flags) {
+ device->depth_format = candidates[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* --- arena --- */
+
+static RendVkArenaAllocator
+rend_vk_arena_create(VkDevice logical_device, VkPhysicalDevice physical_device, VkPhysicalDeviceLimits device_limits, VkAllocationCallbacks *allocator)
+{
+ RendVkArenaAllocator arena;
+ VkPhysicalDeviceMemoryProperties mem_properties;
+ VkDeviceSize alignment;
+ uint32_t count;
+ uint32_t u;
+
+ arena = (RendVkArenaAllocator){
+ .allocator = allocator,
+ .logical_device = logical_device,
+ .physical_device = physical_device,
+ .gpu_alignment = 0,
+ .block_min_size = 0,
+ .heap_index_count = 0,
+ .mem_arenas = 0,
+ };
+
+ vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_properties);
+ arena.properties = mem_properties;
+
+ count = mem_properties.memoryTypeCount;
+ arena.heap_index_count = count;
+
+ arena.mem_arenas = rmalloc(count * sizeof(*arena.mem_arenas));
+ for (u = 0; u < count; ++u) {
+ arena.mem_arenas[u].capacity = 2;
+ arena.mem_arenas[u].elements = 0;
+ arena.mem_arenas[u].page_darr = rmalloc(2 * sizeof(*arena.mem_arenas[u].page_darr));
+ memset(arena.mem_arenas[u].page_darr, 0, 2 * sizeof(*arena.mem_arenas[u].page_darr));
+ }
+
+ alignment = device_limits.bufferImageGranularity;
+ if (device_limits.nonCoherentAtomSize > alignment) {
+ alignment = device_limits.nonCoherentAtomSize;
+ }
+
+ arena.gpu_alignment = alignment;
+ arena.block_min_size = arena.gpu_alignment * 10;
+
+ return arena;
+}
+
+static uint32_t
+rend_vk_arena_add_page(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index, bool fit_to_alloc)
+{
+ VkDeviceSize new_arena_size;
+ VkMemoryPropertyFlags properties;
+ RendVkPage page;
+ VkMemoryAllocateFlagsInfo flags_info;
+ VkMemoryAllocateInfo alloc_info;
+ VkResult res;
+ RendVkPagedArena *mem_arena;
+ uint32_t page_index;
+ uint32_t new_capacity;
+ void *new_darr;
+
+ new_arena_size = fit_to_alloc ? size : (size * 2);
+ new_arena_size = (new_arena_size < arena->block_min_size) ? arena->block_min_size : new_arena_size;
+
+ properties = arena->properties.memoryTypes[heap_index].propertyFlags;
+
+ page = (RendVkPage){0};
+ page.head = 0;
+
+ flags_info = (VkMemoryAllocateFlagsInfo){
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
+ .pNext = NULL,
+ .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT,
+ .deviceMask = 0
+ };
+
+ alloc_info = (VkMemoryAllocateInfo){
+ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ .allocationSize = new_arena_size,
+ .pNext = &flags_info,
+ .memoryTypeIndex = heap_index
+ };
+
+ page.memory = (RendMemory){0};
+ page.memory.offset = 0;
+ page.memory.size = new_arena_size;
+
+ res = vkAllocateMemory(arena->logical_device, &alloc_info, arena->allocator, (VkDeviceMemory *)&page.memory.device_memory);
+ if (res != VK_SUCCESS) {
+ return UINT32_MAX;
+ }
+
+ if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ vkMapMemory(
+ arena->logical_device,
+ (VkDeviceMemory)page.memory.device_memory,
+ 0,
+ new_arena_size,
+ 0,
+ &page.memory.host_mapped_memory
+ );
+ }
+
+ mem_arena = &arena->mem_arenas[heap_index];
+
+ if (mem_arena->elements + 1 >= mem_arena->capacity) {
+ new_capacity = mem_arena->capacity * 2;
+ new_darr = rrealloc(mem_arena->page_darr, new_capacity * sizeof(*mem_arena->page_darr));
+ if (!new_darr) {
+ vkFreeMemory(arena->logical_device, (VkDeviceMemory)page.memory.device_memory, arena->allocator);
+ return UINT32_MAX;
+ }
+ mem_arena->page_darr = new_darr;
+ mem_arena->capacity = new_capacity;
+ }
+
+ page_index = mem_arena->elements;
+ mem_arena->page_darr[mem_arena->elements++] = page;
+
+ return page_index;
+}
+
+static RendMemory
+rend_vk_arena_alloc(RendVkArenaAllocator *arena, VkDeviceSize size, uint32_t heap_index)
+{
+ RendVkPagedArena *mem_arena;
+ VkMemoryPropertyFlags properties;
+ VkDeviceSize align;
+ VkDeviceSize aligned_size;
+ int whole_page;
+ uint32_t page_idx;
+ uint32_t u;
+ RendVkPage page;
+ int valid;
+ RendMemory memory;
+
+ assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?");
+ mem_arena = &arena->mem_arenas[heap_index];
+
+ properties = arena->properties.memoryTypes[heap_index].propertyFlags;
+
+ align = arena->gpu_alignment;
+ aligned_size = (size + align - 1) & ~(align - 1);
+
+ whole_page = !(properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ page_idx = UINT32_MAX;
+ for (u = 0; u < mem_arena->elements; ++u) {
+ page = mem_arena->page_darr[u];
+ valid = (whole_page) ? (page.head == 0) : 1;
+ if ((page.head + aligned_size <= page.memory.size) && valid) {
+ page_idx = u;
+ break;
+ }
+ }
+
+ if (page_idx == UINT32_MAX) {
+ page_idx = rend_vk_arena_add_page(arena, aligned_size, heap_index, whole_page);
+ if (page_idx == UINT32_MAX) {
+ REND__CRASH("[REND_VK] Arena page allocation failed!");
+ }
+ }
+
+ mem_arena->page_darr[page_idx].reserved = whole_page;
+
+ memory = (RendMemory){
+ .device_memory = mem_arena->page_darr[page_idx].memory.device_memory,
+ .size = aligned_size,
+ .offset = mem_arena->page_darr[page_idx].head,
+ .host_mapped_memory = 0,
+ .heap_index = heap_index,
+ .id = page_idx,
+ };
+
+ if (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ memory.host_mapped_memory = mem_arena->page_darr[page_idx].memory.host_mapped_memory + memory.offset;
+ }
+
+ mem_arena->page_darr[page_idx].head += aligned_size;
+ return memory;
+}
+
+static void
+rend_vk_arena_clear(RendVkArenaAllocator *arena, uint32_t heap_index)
+{
+ RendVkPagedArena mem_arena;
+ uint32_t u;
+
+ assert(heap_index < 32 && "Unusual heap index. Did you pass the memory type instead?");
+ mem_arena = arena->mem_arenas[heap_index];
+ if (mem_arena.page_darr) {
+ for (u = 0; u < mem_arena.elements; ++u) {
+ mem_arena.page_darr[u].head = 0;
+ mem_arena.page_darr[u].reserved = 0;
+ }
+ }
+}
+
+static void
+rend_vk_arena_clear_all(RendVkArenaAllocator *arena)
+{
+ uint32_t u;
+
+ for (u = 0; u < arena->properties.memoryTypeCount; ++u) {
+ rend_vk_arena_clear(arena, u);
+ }
+}
+
+static void
+rend_vk_arena_destroy(RendVkArenaAllocator *arena)
+{
+ size_t u;
+ uint32_t p;
+ RendVkPagedArena *mem_arena;
+ RendMemory memory;
+
+ if (arena->mem_arenas) {
+ for (u = 0; u < arena->heap_index_count; ++u) {
+ mem_arena = &arena->mem_arenas[u];
+ for (p = 0; p < mem_arena->elements; ++p) {
+ memory = mem_arena->page_darr[p].memory;
+ if (memory.offset == 0) {
+ if (memory.host_mapped_memory) {
+ vkUnmapMemory(arena->logical_device, (VkDeviceMemory)memory.device_memory);
+ }
+ vkFreeMemory(arena->logical_device, (VkDeviceMemory)memory.device_memory, arena->allocator);
+ } else {
+ PWARN("[REND_VK] Attempted to free memory with an offset!");
+ }
+ }
+ if (mem_arena->page_darr) {
+ rfree(mem_arena->page_darr);
+ mem_arena->page_darr = 0;
+ }
+ }
+ rfree(arena->mem_arenas);
+ arena->mem_arenas = 0;
+ }
+ memset(arena, 0, sizeof(*arena));
+}
+
+/* --- image --- */
+
+static RendVkImage
+rend_vk_image_create(VkDevice logical_device, VkImageType img_type, uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling,
+ VkImageUsageFlags usage, uint32_t depth, uint32_t mip_levels, uint32_t layers,
+ VkSampleCountFlags sample_count_flags, VkSharingMode sharing_mode)
+{
+ RendVkImage image = {
+ .handle = VK_NULL_HANDLE,
+ .memory = VK_NULL_HANDLE,
+ .logical_device = logical_device,
+ .img_type = img_type,
+ .width = width,
+ .height = height,
+ .format = format,
+ .tiling = tiling,
+ .usage = usage,
+ .depth = depth,
+ .mip_levels = mip_levels,
+ .layers = layers,
+ .sample_count_flags = sample_count_flags,
+ .sharing_mode = sharing_mode
+ };
+
+ VkImageCreateInfo img_create_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
+ img_create_info.imageType = img_type;
+ img_create_info.extent.width = width;
+ img_create_info.extent.height = height;
+ img_create_info.extent.depth = depth;
+ img_create_info.mipLevels = mip_levels;
+ img_create_info.arrayLayers = layers;
+ img_create_info.format = format;
+ img_create_info.tiling = tiling;
+ img_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ img_create_info.usage = usage;
+ img_create_info.samples = sample_count_flags;
+ img_create_info.sharingMode = sharing_mode;
+
+ if (vkCreateImage(logical_device, &img_create_info, vk_allocator, &image.handle) != VK_SUCCESS) {
+ return image;
+ }
+
+ return image;
+}
+
+static uint32_t
+rend_vk_image_required_memory_type(RendVkImage *img)
+{
+ VkMemoryRequirements memory_requirements;
+
+ assert(img->memory == NULL && "Image already bound to memory");
+ memory_requirements = (VkMemoryRequirements){0};
+ vkGetImageMemoryRequirements(img->logical_device, img->handle, &memory_requirements);
+ img->requirements = memory_requirements;
+ return memory_requirements.memoryTypeBits;
+}
+
+static void
+rend_vk_image_bind_memory(RendVkImage *img, RendMemory *memory)
+{
+ assert(img->memory == NULL && "Image already bound to memory");
+ vkBindImageMemory(img->logical_device, img->handle, (VkDeviceMemory) memory->device_memory, memory->offset);
+}
+
+static void
+rend_vk_image_destroy(RendVkImage *img)
+{
+ if (img->view) {
+ vkDestroyImageView(img->logical_device, img->view, vk_allocator);
+ img->view = 0;
+ }
+
+ if (img->handle) {
+ vkDestroyImage(img->logical_device, img->handle, vk_allocator);
+ img->handle = 0;
+ }
+}
+
+static void
+rend_vk_image_view_create(RendVkImage *image, VkImageViewType view_type, VkImageAspectFlags view_aspect_flags)
+{
+ VkImageViewCreateInfo view_create_info = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+ .image = image->handle,
+ .format = image->format,
+ .viewType = view_type,
+ .subresourceRange.aspectMask = view_aspect_flags,
+ .subresourceRange.baseMipLevel = 0,
+ .subresourceRange.levelCount = image->mip_levels,
+ .subresourceRange.baseArrayLayer = 0,
+ .subresourceRange.layerCount = image->layers,
+ };
+
+ vkCreateImageView(image->logical_device, &view_create_info, vk_allocator, &image->view);
+}