feat(renderer): Initialize vulkan
This commit is contained in:
parent
3059fce861
commit
e0904e84c4
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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<IDXGIFactory1> factory;
|
||||
|
@ -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:
|
||||
|
@ -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<void*>(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) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
665
src/renderer/vk/device.c
Normal file
665
src/renderer/vk/device.c
Normal file
@ -0,0 +1,665 @@
|
||||
#include <malloc.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <runtime/runtime.h>
|
||||
#include <runtime/config.h>
|
||||
|
||||
#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 = "<UNKNOWN>";
|
||||
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);
|
||||
}
|
79
src/renderer/vk/device.h
Normal file
79
src/renderer/vk/device.h
Normal file
@ -0,0 +1,79 @@
|
||||
#ifndef RT_VK_DEVICE_H
|
||||
#define RT_VK_DEVICE_H
|
||||
|
||||
#include <volk/volk.h>
|
||||
#include <runtime/runtime.h>
|
||||
#include <renderer/renderer.h>
|
||||
|
||||
#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
|
||||
|
@ -1,14 +1,51 @@
|
||||
#include "renderer/backend_api.h"
|
||||
#include <renderer/backend_api.h>
|
||||
#include <runtime/runtime.h>
|
||||
#include <runtime/config.h>
|
||||
|
||||
#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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user