From e0904e84c4d860c67fec6da720c02c3f1f3303a9 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Tue, 30 Jul 2024 21:57:46 +0200 Subject: [PATCH] feat(renderer): Initialize vulkan --- src/launcher/launcher.c | 2 +- src/renderer/backend_api.h | 25 +- src/renderer/dx11/device.cpp | 2 +- src/renderer/dx11/device.hpp | 2 +- src/renderer/dx11/init.cpp | 9 +- src/renderer/init.c | 5 +- src/renderer/renderer.h | 4 +- src/renderer/vk/device.c | 665 +++++++++++++++++++++++++++++++++++ src/renderer/vk/device.h | 79 +++++ src/renderer/vk/init.c | 45 ++- src/renderer/vk/meson.build | 18 +- 11 files changed, 837 insertions(+), 19 deletions(-) create mode 100644 src/renderer/vk/device.c create mode 100644 src/renderer/vk/device.h diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index 6348221..0265408 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -339,7 +339,7 @@ static int Entry(int argc, char **argv) { } /* Initialize the renderer */ - rt_renderer_init_info renderer_init_info; + rt_renderer_window_info renderer_init_info; #ifdef _WIN32 renderer_init_info.hWnd = glfwGetWin32Window(window); renderer_init_info.hInstance = _hInstance; diff --git a/src/renderer/backend_api.h b/src/renderer/backend_api.h index 63c7484..4dc775e 100644 --- a/src/renderer/backend_api.h +++ b/src/renderer/backend_api.h @@ -4,8 +4,31 @@ #include "runtime/runtime.h" #include "renderer.h" +typedef struct rt_physical_resource_manager_i rt_physical_resource_manager_i; + +typedef rt_physical_resource_manager_i *rt_render_device_get_physical_resource_manager_fn(void *o); + +/* Interface for the render device. + * The device is responsible for executing command lists. */ +typedef struct { + void *o; + rt_render_device_get_physical_resource_manager_fn *GetPhysicalResourceManager; +} rt_render_device_i; + +/* Interface for the physical resource manager. + * The physical resource manager maps render resources to actual gpu memory (=api objects like VkImage) + */ +struct rt_physical_resource_manager_i { + void *o; +}; + +typedef struct { + rt_result result; + rt_render_device_i device; +} rt_render_backend_init_result; + typedef void rt_render_backend_register_cvars_fn(void); -typedef rt_result rt_render_backend_init_fn(const rt_renderer_init_info *info); +typedef rt_render_backend_init_result rt_render_backend_init_fn(const rt_renderer_window_info *info); typedef void rt_render_backend_shutdown_fn(void); /* Public renderer interface */ diff --git a/src/renderer/dx11/device.cpp b/src/renderer/dx11/device.cpp index 12e35db..6da9345 100644 --- a/src/renderer/dx11/device.cpp +++ b/src/renderer/dx11/device.cpp @@ -10,7 +10,7 @@ rt_dx11_device::rt_dx11_device() : m_is_initialized(false) { rt_dx11_device::~rt_dx11_device() { } -rt_result rt_dx11_device::Initialize(const rt_renderer_init_info *info) { +rt_result rt_dx11_device::Initialize(const rt_renderer_window_info *info *info) { // Create the necessary objects (Device, SwapChain, Immediate Context) HRESULT hr = S_OK; ComPtr factory; diff --git a/src/renderer/dx11/device.hpp b/src/renderer/dx11/device.hpp index 091b6c8..4332f08 100644 --- a/src/renderer/dx11/device.hpp +++ b/src/renderer/dx11/device.hpp @@ -22,7 +22,7 @@ class rt_dx11_device { return &dev; } - rt_result Initialize(const rt_renderer_init_info *info); + rt_result Initialize(const rt_renderer_window_info *info *info); void Shutdown(void); private: diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp index 7e29676..3e94c6a 100644 --- a/src/renderer/dx11/init.cpp +++ b/src/renderer/dx11/init.cpp @@ -5,11 +5,14 @@ void Dx11RegisterCVARs(void) { } -rt_result Dx11Init(const rt_renderer_init_info *info) { +rt_render_backend_init_result Dx11Init(const rt_renderer_window_info *info *info) { rt_result res = rt_dx11_device::GetInstance()->Initialize(info); + + rt_render_device_i iface = {reinterpret_cast(rt_dx11_device::GetInstance()), nullptr}; + rt_render_backend_init_result result = {.result = res, .device = iface}; if (res != RT_SUCCESS) - return res; - return res; + return result; + return result; } void Dx11Shutdown(void) { diff --git a/src/renderer/init.c b/src/renderer/init.c index f571f18..1f407c8 100644 --- a/src/renderer/init.c +++ b/src/renderer/init.c @@ -14,8 +14,9 @@ RT_DLLEXPORT void rtRegisterRenderBackendCVARs(void) { } -RT_DLLEXPORT rt_result rtInitRenderer(const rt_renderer_init_info *info) { - return g_render_backend.Init(info); +RT_DLLEXPORT rt_result rtInitRenderer(const rt_renderer_window_info *info) { + rt_render_backend_init_result backend_res = g_render_backend.Init(info); + return backend_res.result; } RT_DLLEXPORT void rtShutdownRenderer(void) { diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h index 1080fbf..df2129a 100644 --- a/src/renderer/renderer.h +++ b/src/renderer/renderer.h @@ -23,7 +23,7 @@ typedef struct { unsigned int width; unsigned int height; int is_fullscreen; -} rt_renderer_init_info; +} rt_renderer_window_info; typedef enum { RT_SHADER_STAGE_VERTEX, @@ -43,7 +43,7 @@ RT_DLLEXPORT rt_result rtLoadRenderBackend(void); RT_DLLEXPORT void rtRegisterRenderBackendCVARs(void); -RT_DLLEXPORT rt_result rtInitRenderer(const rt_renderer_init_info *info); +RT_DLLEXPORT rt_result rtInitRenderer(const rt_renderer_window_info *window); RT_DLLEXPORT void rtShutdownRenderer(void); diff --git a/src/renderer/vk/device.c b/src/renderer/vk/device.c new file mode 100644 index 0000000..7d98008 --- /dev/null +++ b/src/renderer/vk/device.c @@ -0,0 +1,665 @@ +#include +#include +#include +#include + +#include +#include + +#include "device.h" + +#define TARGET_API_VERSION VK_API_VERSION_1_3 + +RT_CVAR_I(r_VkEnableAPIAllocTracking, + "Enable tracking of allocations done by the vulkan api. [0/1] Default: 0", + 0); + +RT_CVAR_S(r_VkPhysDeviceName, "Name of the selected physical device. Default: \"\"", ""); + +RT_CVAR_I(r_VkMaxFramesInFlight, "Maximum number of frames in flight. [2/3] Default: 2", 2); + +static VkAllocationCallbacks _tracking_alloc_cbs; + +static const char *AllocationScopeToString(VkSystemAllocationScope scope) { + switch (scope) { + case VK_SYSTEM_ALLOCATION_SCOPE_COMMAND: + return "COMMAND"; + case VK_SYSTEM_ALLOCATION_SCOPE_OBJECT: + return "OBJECT"; + case VK_SYSTEM_ALLOCATION_SCOPE_CACHE: + return "CACHE"; + case VK_SYSTEM_ALLOCATION_SCOPE_DEVICE: + return "DEVICE"; + case VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE: + return "INSTANCE"; + default: + return "UNKNOWN"; + } +} + +static void * +TrackAllocation(void *userData, size_t size, size_t alignment, VkSystemAllocationScope scope) { + rtLog("VK", + "Allocation. Size: %zu, Alignment: %zu, Scope: %s", + size, + alignment, + AllocationScopeToString(scope)); +#ifdef _WIN32 + return _aligned_malloc(size, alignment); +#else + return aligned_alloc(alignment, size); +#endif +} + +static void *TrackReallocation(void *userData, + void *original, + size_t size, + size_t alignment, + VkSystemAllocationScope scope) { + rtLog("VK", + "Reallocation. Size: %zu, Alignment: %zu, Scope: %s", + size, + alignment, + AllocationScopeToString(scope)); + return realloc(original, size); +} + +static void TrackFree(void *userData, void *memory) { + free(memory); +} + +static VkBool32 VKAPI_PTR +DebugUtilsMessengerCb(VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT types, + const VkDebugUtilsMessengerCallbackDataEXT *callbackData, + void *userData) { + if (severity < VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + return VK_FALSE; + + const char *severity_str = ""; + if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + severity_str = "WARNING"; + else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + severity_str = "ERROR"; + rtLog("VK", "[%s] %s", severity_str, callbackData->pMessage); + if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + RT_DEBUGBREAK; + return VK_FALSE; +} + +static rt_result CreateInstance(rt_vk_device *dev) { + VkResult result = volkInitialize(); + if (result != VK_SUCCESS) { + rtReportError("vk", "Initialization failed: volkInitialize()"); + return 1; + } + + VkApplicationInfo app_info = { + .apiVersion = TARGET_API_VERSION, + .applicationVersion = 0x00001000, + .engineVersion = 0x00001000, + .pEngineName = "voyageEngine", + .pApplicationName = "Voyage", + }; + + const char *extensions[] = { + VK_KHR_SURFACE_EXTENSION_NAME, +#ifdef _WIN32 + "VK_KHR_win32_surface", +#elif defined(RT_USE_XLIB) + "VK_KHR_xlib_surface", +#endif + +#ifdef RT_DEBUG + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, +#endif + }; + + const char *layers[1]; + unsigned int layer_count = 0; +#ifdef RT_DEBUG + /* Search for layers we want to enable */ + uint32_t available_layer_count = 0; + result = vkEnumerateInstanceLayerProperties(&available_layer_count, NULL); + if (result == VK_SUCCESS) { + VkLayerProperties *props = calloc(available_layer_count, sizeof(VkLayerProperties)); + if (props) { + vkEnumerateInstanceLayerProperties(&available_layer_count, props); + for (uint32_t i = 0; i < available_layer_count; ++i) { + if (strcmp(props[i].layerName, "VK_LAYER_KHRONOS_validation") == 0) { + layers[0] = "VK_LAYER_KHRONOS_validation"; + layer_count = 1; + break; + } + } + free(props); + } else { + rtLog("VK", "Failed to allocate storage for instance layer properties."); + } + } else { + rtLog("VK", "vkEnumerateInstanceLayerProperties failed."); + } +#endif + + VkInstanceCreateInfo instance_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pApplicationInfo = &app_info, + .ppEnabledExtensionNames = extensions, + .enabledExtensionCount = RT_ARRAY_COUNT(extensions), + .ppEnabledLayerNames = layers, + .enabledLayerCount = layer_count, + }; + result = vkCreateInstance(&instance_info, dev->alloc_cb, &dev->instance); + if (result != VK_SUCCESS) { + rtReportError("VK", "Failed to create the vulkan instance."); + return 1; + } + volkLoadInstance(dev->instance); + +#ifdef RT_DEBUG + /* Create the debug utils messenger */ + VkDebugUtilsMessengerCreateInfoEXT messenger_info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + .pfnUserCallback = DebugUtilsMessengerCb, + }; + vkCreateDebugUtilsMessengerEXT(dev->instance, + &messenger_info, + dev->alloc_cb, + &dev->messenger); +#endif + return RT_SUCCESS; +} + +static rt_result CreateSurface(const rt_renderer_window_info *info, rt_vk_device *dev) { +#ifdef _WIN32 + dev->native_window.hInstance = info->hInstance; + dev->native_window.hWnd = info->hWnd; + VkWin32SurfaceCreateInfoKHR surface_info = { + .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + .hinstance = info->hInstance, + .hwnd = info->hWnd, + }; + if (vkCreateWin32SurfaceKHR(dev->instance, &surface_info, dev->alloc_cb, &dev->surface) == + VK_SUCCESS) + return RT_SUCCESS; + else + return RT_UNKNOWN_ERROR; +#elif defined(RT_USE_XLIB) + dev->native_window.display = info->display; + dev->native_window.window = info->window; + VkXlibSurfaceCreateInfoKHR surface_info = { + .sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + .dpy = info->display, + .window = info->window, + }; + if (vkCreateXlibSurfaceKHR(dev->instance, &surface_info, dev->alloc_cb, &dev->surface) == VK_SUCCESS) + return RT_SUCCESS; + else + return RT_UNKNOWN_ERROR; +#endif +} + +typedef struct { + uint32_t graphics; + uint32_t compute; + uint32_t present; + uint32_t transfer; +} rt_queue_indices; + +static rt_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, VkSurfaceKHR surface) { + rt_queue_indices indices = {.graphics = UINT32_MAX, + .compute = UINT32_MAX, + .present = UINT32_MAX, + .transfer = UINT32_MAX}; + + uint32_t count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, NULL); + VkQueueFamilyProperties *props = calloc(count, sizeof(VkQueueFamilyProperties)); + if (!props) { + return indices; + } + vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, props); + for (uint32_t i = 0; i < count; ++i) { + if (props[i].queueCount == 0) + continue; + if ((props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) + indices.graphics = i; + if ((props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0) + indices.compute = i; + if ((props[i].queueFlags & VK_QUEUE_TRANSFER_BIT) != 0) + indices.transfer = i; + + VkBool32 present_supported = VK_FALSE; + vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev, i, surface, &present_supported); + if (present_supported) + indices.present = i; + } + + if (indices.transfer == UINT32_MAX && indices.graphics != UINT32_MAX) + indices.transfer = indices.graphics; + else if (indices.transfer == UINT32_MAX && indices.compute != UINT32_MAX) + indices.transfer = indices.compute; + + free(props); + return indices; +} + +static bool CheckDeviceExtensionSupported(VkPhysicalDevice phys_dev) { + const char *required_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + uint32_t extension_count; + vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, NULL); + + VkExtensionProperties *supported_extensions = + calloc(extension_count, sizeof(VkExtensionProperties)); + if (!supported_extensions) + return false; + vkEnumerateDeviceExtensionProperties(phys_dev, NULL, &extension_count, supported_extensions); + + bool supported = true; + for (uint32_t i = 0; i < RT_ARRAY_COUNT(required_extensions); ++i) { + bool found = false; + for (uint32_t j = 0; j < extension_count; ++j) { + if (strncmp(supported_extensions[j].extensionName, + required_extensions[i], + VK_MAX_EXTENSION_NAME_SIZE) == 0) { + found = true; + break; + } + } + if (!found) { + supported = false; + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(phys_dev, &props); + rtLog("VK", "Device %s does not support the required extension %s", + props.deviceName, + required_extensions[i]); + goto out; + } + } + +out: + free(supported_extensions); + return supported; +} + +static rt_result ChoosePhysicalDevice(rt_vk_device *dev) { + + dev->phys_device = VK_NULL_HANDLE; + uint32_t phys_device_count = 0; + VkResult result = vkEnumeratePhysicalDevices(dev->instance, &phys_device_count, NULL); + if (result != VK_SUCCESS) { + rtReportError("VK", "Failed to enumerate the physical devices."); + return 2; + } + VkPhysicalDevice *phys_devices = calloc(phys_device_count, sizeof(VkPhysicalDevice)); + if (!phys_devices) { + rtReportError("VK", "Failed to enumerate the physical devices: Out of memory."); + return 2; + } + vkEnumeratePhysicalDevices(dev->instance, &phys_device_count, phys_devices); + + uint32_t highscore = 0; + uint32_t best_index = phys_device_count; + for (uint32_t i = 0; i < phys_device_count; ++i) { + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + }; + VkPhysicalDeviceSynchronization2Features synchronization2_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + .pNext = &timeline_semaphore_features, + }; + VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, + .pNext = &synchronization2_features, + }; + VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + .pNext = &dynamic_rendering_features, + }; + VkPhysicalDeviceFeatures2 features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &descriptor_indexing_features, + }; + vkGetPhysicalDeviceFeatures2(phys_devices[i], &features); + + VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, + .pNext = NULL, + }; + VkPhysicalDeviceProperties2 props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &descriptor_indexing_props, + }; + vkGetPhysicalDeviceProperties2(phys_devices[i], &props); + + if (!CheckDeviceExtensionSupported(phys_devices[i])) + continue; + + rt_queue_indices indices = RetrieveQueueIndices(phys_devices[i], dev->surface); + if (indices.compute == UINT32_MAX || indices.present == UINT32_MAX || + indices.graphics == UINT32_MAX) + continue; + + if (!synchronization2_features.synchronization2 || + !dynamic_rendering_features.dynamicRendering || + !timeline_semaphore_features.timelineSemaphore) + continue; + + /* Check for bindless support */ + if (!descriptor_indexing_features.runtimeDescriptorArray || + !descriptor_indexing_features.descriptorBindingPartiallyBound) + continue; + + uint32_t score = 0; + + if (props.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + score += 100; + + score += (props.properties.limits.maxFramebufferWidth / 100) * + (props.properties.limits.maxFramebufferHeight / 100); + + score += + (descriptor_indexing_props.shaderStorageBufferArrayNonUniformIndexingNative) ? 100 : 0; + score += + (descriptor_indexing_props.shaderSampledImageArrayNonUniformIndexingNative) ? 100 : 0; + + if (score > highscore) { + highscore = score; + best_index = i; + } + + if (strncmp(props.properties.deviceName, + r_VkPhysDeviceName.s, + VK_MAX_PHYSICAL_DEVICE_NAME_SIZE) == 0) { + best_index = i; + break; + } + } + if (best_index < phys_device_count) { + dev->phys_device = phys_devices[best_index]; + + VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES, + .pNext = NULL, + }; + VkPhysicalDeviceProperties2 props = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &descriptor_indexing_props, + }; + VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + }; + VkPhysicalDeviceFeatures2 features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &descriptor_indexing_features, + }; + vkGetPhysicalDeviceFeatures2(phys_devices[best_index], &features); + vkGetPhysicalDeviceProperties2(phys_devices[best_index], &props); + + dev->phys_device_props = props.properties; + dev->descriptor_indexing_props = descriptor_indexing_props; + dev->phys_device_features = features.features; + dev->descriptor_indexing_features = descriptor_indexing_features; + } + free(phys_devices); + + if (dev->phys_device == VK_NULL_HANDLE) { + rtReportError("vk", "Failed to find a suitable physical device."); + return 3; + } + return RT_SUCCESS; +} + +static rt_result CreateDevice(rt_vk_device *dev) { + const char *extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + rt_queue_indices queue_indices = RetrieveQueueIndices(dev->phys_device, dev->surface); + + dev->compute_family = queue_indices.compute; + dev->graphics_family = queue_indices.graphics; + dev->present_family = queue_indices.present; + dev->transfer_family = queue_indices.transfer; + + float priority = 1.f; + + uint32_t distinct_queue_count = 1; + VkDeviceQueueCreateInfo queue_info[4]; + queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[0].pNext = NULL; + queue_info[0].flags = 0; + queue_info[0].queueCount = 1; + queue_info[0].queueFamilyIndex = queue_indices.graphics; + queue_info[0].pQueuePriorities = &priority; + if (queue_indices.compute != queue_indices.graphics) { + queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[1].pNext = NULL; + queue_info[1].flags = 0; + queue_info[1].queueCount = 1; + queue_info[1].queueFamilyIndex = queue_indices.compute; + queue_info[1].pQueuePriorities = &priority; + ++distinct_queue_count; + } + if (queue_indices.present != queue_indices.graphics && + queue_indices.present != queue_indices.compute) { + queue_info[distinct_queue_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[distinct_queue_count].pNext = NULL; + queue_info[distinct_queue_count].flags = 0; + queue_info[distinct_queue_count].queueCount = 1; + queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.present; + queue_info[distinct_queue_count].pQueuePriorities = &priority; + ++distinct_queue_count; + } + if (queue_indices.transfer != queue_indices.graphics && + queue_indices.transfer != queue_indices.compute) { + queue_info[distinct_queue_count].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_info[distinct_queue_count].pNext = NULL; + queue_info[distinct_queue_count].flags = 0; + queue_info[distinct_queue_count].queueCount = 1; + queue_info[distinct_queue_count].queueFamilyIndex = queue_indices.transfer; + queue_info[distinct_queue_count].pQueuePriorities = &priority; + ++distinct_queue_count; + } + VkPhysicalDeviceTimelineSemaphoreFeatures timeline_semaphore_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + }; + VkPhysicalDeviceSynchronization2Features synchronization2_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + .pNext = &timeline_semaphore_features, + }; + VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, + .pNext = &synchronization2_features, + }; + VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + .pNext = &dynamic_rendering_features, + }; + VkPhysicalDeviceFeatures2 features = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &indexing_features}; + vkGetPhysicalDeviceFeatures2(dev->phys_device, &features); + + RT_ASSERT(indexing_features.runtimeDescriptorArray && + indexing_features.descriptorBindingPartiallyBound, + "We require a device that supports bindless vulkan."); + + VkDeviceCreateInfo device_info = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = &features, + .enabledExtensionCount = RT_ARRAY_COUNT(extensions), + .ppEnabledExtensionNames = extensions, + .pQueueCreateInfos = queue_info, + .queueCreateInfoCount = distinct_queue_count, + }; + if (vkCreateDevice(dev->phys_device, &device_info, dev->alloc_cb, &dev->device) != + VK_SUCCESS) { + rtReportError("VK", "Device creation failed."); + return 10; + } + + vkGetDeviceQueue(dev->device, queue_indices.graphics, 0, &dev->graphics_queue); + vkGetDeviceQueue(dev->device, queue_indices.compute, 0, &dev->compute_queue); + vkGetDeviceQueue(dev->device, queue_indices.present, 0, &dev->present_queue); + vkGetDeviceQueue(dev->device, queue_indices.transfer, 0, &dev->transfer_queue); + + return RT_SUCCESS; +} + +#if 0 +static rt_result CreateAllocator(rt_vk_device *dev) { +#define SET_FNC(name) fncs.name = name +#define SET_KHR_FNC(name) (fncs).name##KHR = name + VmaVulkanFunctions fncs = {NULL}; + SET_FNC(vkGetInstanceProcAddr); + SET_FNC(vkGetDeviceProcAddr); + SET_FNC(vkGetPhysicalDeviceProperties); + SET_FNC(vkGetPhysicalDeviceMemoryProperties); + SET_FNC(vkAllocateMemory); + SET_FNC(vkFreeMemory); + SET_FNC(vkMapMemory); + SET_FNC(vkUnmapMemory); + SET_FNC(vkFlushMappedMemoryRanges); + SET_FNC(vkInvalidateMappedMemoryRanges); + SET_FNC(vkBindBufferMemory); + SET_FNC(vkBindImageMemory); + SET_FNC(vkGetBufferMemoryRequirements); + SET_FNC(vkGetImageMemoryRequirements); + SET_FNC(vkCreateBuffer); + SET_FNC(vkDestroyBuffer); + SET_FNC(vkCreateImage); + SET_FNC(vkDestroyImage); + SET_FNC(vkCmdCopyBuffer); + SET_KHR_FNC(vkGetBufferMemoryRequirements2); + SET_KHR_FNC(vkGetImageMemoryRequirements2); + SET_KHR_FNC(vkBindBufferMemory2); + SET_KHR_FNC(vkBindImageMemory2); + SET_KHR_FNC(vkGetPhysicalDeviceMemoryProperties2); + SET_FNC(vkGetDeviceBufferMemoryRequirements); + SET_FNC(vkGetDeviceImageMemoryRequirements); +#undef SET_FNC +#undef SET_KHR_FNC + + VmaAllocatorCreateInfo allocator_info = { + .instance = dev->instance, + .physicalDevice = dev->phys_device, + .device = dev->device, + .pAllocationCallbacks = dev->alloc_cb, + .vulkanApiVersion = TARGET_API_VERSION, + .pVulkanFunctions = &fncs, + }; + + return vmaCreateAllocator(&allocator_info, &dev->allocator) == VK_SUCCESS ? RT_SUCCESS + : RT_UNKNOWN_ERROR; +} + +static void DestroyAllocator(void) { + vmaDestroyAllocator(dev->allocator); +} +#endif + +static rt_result CreatePerFrameObjects(rt_vk_device *dev) { + for (unsigned int i = 0; i < dev->max_frames_in_flight; ++i) { + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + if (vkCreateSemaphore(dev->device, + &semaphore_info, + dev->alloc_cb, + &dev->frames[i].render_finished) != VK_SUCCESS) { + return RT_UNKNOWN_ERROR; + } + if (vkCreateSemaphore(dev->device, + &semaphore_info, + dev->alloc_cb, + &dev->frames[i].image_available) != VK_SUCCESS) { + return RT_UNKNOWN_ERROR; + } + if (vkCreateSemaphore(dev->device, + &semaphore_info, + dev->alloc_cb, + &dev->frames[i].swapchain_transitioned) != VK_SUCCESS) { + return RT_UNKNOWN_ERROR; + } +#ifdef RT_DEBUG + char name[128]; + rtSPrint(name, 128, "Render Finished Semaphore (%u)", i); + VkDebugUtilsObjectNameInfoEXT name_info = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, + .objectHandle = (uint64_t)dev->frames[i].render_finished, + .objectType = VK_OBJECT_TYPE_SEMAPHORE, + .pObjectName = name, + }; + vkSetDebugUtilsObjectNameEXT(dev->device, &name_info); + + rtSPrint(name, 128, "Image Available Semaphore (%u)", i); + name_info.objectHandle = (uint64_t)dev->frames[i].image_available; + vkSetDebugUtilsObjectNameEXT(dev->device, &name_info); + + rtSPrint(name, 128, "Swapchain Transitioned Semaphore (%u)", i); + name_info.objectHandle = (uint64_t)dev->frames[i].swapchain_transitioned; + vkSetDebugUtilsObjectNameEXT(dev->device, &name_info); +#endif + } + return RT_SUCCESS; +} + +void DestroyPerFrameObjects(rt_vk_device *dev) { + for (unsigned int i = 0; i < dev->max_frames_in_flight; ++i) { + vkDestroySemaphore(dev->device, dev->frames[i].image_available, dev->alloc_cb); + vkDestroySemaphore(dev->device, dev->frames[i].render_finished, dev->alloc_cb); + vkDestroySemaphore(dev->device, dev->frames[i].swapchain_transitioned, dev->alloc_cb); + } +} + +rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) { + rtLog("vk", "Init"); + + rt_vk_device dev; + + _tracking_alloc_cbs.pUserData = NULL; + _tracking_alloc_cbs.pfnAllocation = TrackAllocation; + _tracking_alloc_cbs.pfnReallocation = TrackReallocation; + _tracking_alloc_cbs.pfnFree = TrackFree; + + if (r_VkEnableAPIAllocTracking.i) { + dev.alloc_cb = &_tracking_alloc_cbs; + } else { + dev.alloc_cb = NULL; + } + + rt_result res; + if ((res = CreateInstance(&dev)) != RT_SUCCESS) + goto out; + if ((res = CreateSurface(info, &dev)) != RT_SUCCESS) + goto out; + if ((res = ChoosePhysicalDevice(&dev)) != RT_SUCCESS) + goto out; + if ((res = CreateDevice(&dev)) != RT_SUCCESS) + goto out; + if ((res = CreatePerFrameObjects(&dev)) != RT_SUCCESS) + goto out; + +out: + return (rt_create_vk_device_result) {.result = res, .device = dev}; +} + +void rtDestroyVkDevice(rt_vk_device *dev) { + rtLog("VK", "Shutdown"); + vkDeviceWaitIdle(dev->device); + DestroyPerFrameObjects(dev); + // DestroyAllocator(); + vkDestroyDevice(dev->device, dev->alloc_cb); + vkDestroySurfaceKHR(dev->instance, dev->surface, dev->alloc_cb); +#ifdef RT_DEBUG + vkDestroyDebugUtilsMessengerEXT(dev->instance, dev->messenger, dev->alloc_cb); +#endif + vkDestroyInstance(dev->instance, dev->alloc_cb); +} diff --git a/src/renderer/vk/device.h b/src/renderer/vk/device.h new file mode 100644 index 0000000..992a480 --- /dev/null +++ b/src/renderer/vk/device.h @@ -0,0 +1,79 @@ +#ifndef RT_VK_DEVICE_H +#define RT_VK_DEVICE_H + +#include +#include +#include + +#ifdef _WIN32 +struct HINSTANCE__; +struct HWND__; +#elif defined(RT_USE_XLIB) +struct _XDisplay; +#endif + +typedef struct { +#ifdef _WIN32 + struct HINSTANCE__ *hInstance; + struct HWND__ *hWnd; +#elif defined(RT_USE_XLIB) + struct _XDisplay *display; + unsigned long window; +#endif +} rt_vk_native_window; + + +typedef struct { + VkSemaphore render_finished; + VkSemaphore image_available; + VkSemaphore swapchain_transitioned; +} rt_vk_frame_data; + +typedef struct { + /* *** Vulkan objects *** */ + VkInstance instance; + VkDevice device; + VkPhysicalDevice phys_device; + VkAllocationCallbacks *alloc_cb; + VkSurfaceKHR surface; + + /* *** Queues *** */ + uint32_t graphics_family; + uint32_t compute_family; + uint32_t transfer_family; + uint32_t present_family; + VkQueue graphics_queue; + VkQueue compute_queue; + VkQueue transfer_queue; + VkQueue present_queue; + + /* *** Properties and features *** */ + VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props; + VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features; + VkPhysicalDeviceFeatures phys_device_features; + VkPhysicalDeviceProperties phys_device_props; + + /* *** Per frame data *** */ + uint32_t max_frames_in_flight; + rt_vk_frame_data frames[3]; + + /* *** Windowing system *** */ + rt_vk_native_window native_window; + + /* *** Debug utils *** */ +#ifdef RT_DEBUG + VkDebugUtilsMessengerEXT messenger; +#endif +} rt_vk_device; + +typedef struct { + rt_result result; + rt_vk_device device; +} rt_create_vk_device_result; + +rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info); + +void rtDestroyVkDevice(rt_vk_device *dev); + +#endif + diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index c4a2178..aaa7d9b 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -1,14 +1,51 @@ -#include "renderer/backend_api.h" +#include +#include +#include + +#include "device.h" + +extern rt_cvar r_VkEnableAPIAllocTracking; +extern rt_cvar r_VkPhysDeviceName; +extern rt_cvar r_VkMaxFramesInFlight; + +#if 0 +extern rt_cvar r_VkPreferredSwapchainImages; +extern rt_cvar r_VkPreferMailboxMode; +#endif void VkRegisterCVARs(void) { - + rtRegisterCVAR(&r_VkEnableAPIAllocTracking); + rtRegisterCVAR(&r_VkPhysDeviceName); + rtRegisterCVAR(&r_VkMaxFramesInFlight); +#if 0 + rtRegisterCVAR(&r_VkPreferredSwapchainImages); + rtRegisterCVAR(&r_VkPreferMailboxMode); +#endif } -rt_result VkInit(const rt_renderer_init_info *info) { - return RT_SUCCESS; +static rt_vk_device _device; + +rt_render_backend_init_result VkInit(const rt_renderer_window_info *info) { + rt_render_backend_init_result res = {.result = RT_SUCCESS}; + + rt_create_vk_device_result device_result = rtCreateVkDevice(info); + if (device_result.result != RT_SUCCESS) { + res.result = device_result.result; + return res; + } + /* Populate the device interface */ + _device = device_result.device; + rt_render_device_i device_i = { + .o = &_device, + .GetPhysicalResourceManager = NULL + }; + res.device = device_i; + + return res; } void VkShutdown(void) { + rtDestroyVkDevice(&_device); } // Called by the application to retrieve the renderer api diff --git a/src/renderer/vk/meson.build b/src/renderer/vk/meson.build index c270d9f..e5c3d11 100644 --- a/src/renderer/vk/meson.build +++ b/src/renderer/vk/meson.build @@ -3,19 +3,29 @@ if get_option('build_vk') vma_dep = vma_proj.get_variable('vma_allocator_dep') vk_inc_proj = subproject('vulkan-headers') - vk_inc_dep = vk_inc_proj.get_variable('vulkan_headers_dep') + vk_inc_dep = vk_inc_proj.get_variable('vulkan_headers_dep') + + platform_defs = [] + if get_option('use_xlib') + platform_defs = ['-DVK_USE_PLATFORM_XLIB_KHR'] + elif host_machine.system() == 'windows' + platform_defs = ['-DVK_USE_PLATFORM_WIN32_KHR'] + endif vk_renderer_lib = library('rtvk', + 'device.h', + + 'device.c', 'init.c', + '../../../contrib/volk/volk.c', '../../../contrib/volk/volk.h', - dependencies: [m_dep, vk_inc_dep, vma_dep, thread_dep], include_directories: [engine_incdir, contrib_incdir], link_with: [runtime_lib], - cpp_pch: 'pch/vk_pch.h', - override_options: ['b_sanitize=none'], + c_pch: 'pch/vk_pch.h', + c_args: platform_defs, install: true) engine_libs += vk_renderer_lib