From cc3429220b19df8c5ca6abf28a02cd38d08de32d Mon Sep 17 00:00:00 2001 From: Vasco Date: Mon, 24 Aug 2026 17:47:30 +0100 Subject: Cool 0.0.0. Rend 1.0.4. --- Rend/rend_vk_internal.c | 1014 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1014 insertions(+) create mode 100644 Rend/rend_vk_internal.c (limited to 'Rend/rend_vk_internal.c') 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 +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.3.1