#include "SDL3/SDL_vulkan.h" #include "materialsystem/materialsystem.h" #include "tier0/platform.h" #include "tier1/utlstring.h" #include "tier1/utlvector.h" #define VK_NO_PROTOTYPES #include "vulkan/vulkan_core.h" #include "vulkan_state.h" #include "igamewindow.h" #define REQUIRED_EXTENSION(ext) ext##_EXTENSION_NAME, #define OPTIONAL_EXTENSION(ext) ext##_EXTENSION_NAME, const char *g_vkDeviceExtensions[] = { #include "device_extensions.h" }; #undef REQUIRED_EXTENSION #undef OPTIONAL_EXTENSION class CVkRenderContext: public IRenderContext { public: virtual void Init() override; virtual void Frame( float fDeltaTime ) override; virtual void Shutdown() override; virtual IVertexBuffer *CreateVertexBuffer( uint32_t nSize ) override; virtual IIndexBuffer *CreateIndexBuffer( uint32_t nSize ) override; virtual IImage *CreateRenderTarget( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) override; virtual IImage *CreateStorageImage( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) override; virtual void DestroyBuffer( IBuffer *pBuffer ) override; virtual void DestroyImage( IImage *pImage ) override; private: IBuffer *CreateBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage ); IBuffer *CreateBufferAligned( uint32_t nSize, uint32_t nAlignment, VkBufferUsageFlags2 eUsage ); VkPhysicalDevice SelectPhysicalDevice( CUtlVector physicalDevices ); void CreateSwapchain(); void DestroySwapchain(); }; //----------------------------------------------------------------------------- // Creates vertex buffer. Wrapper over CreateBuffer //----------------------------------------------------------------------------- IVertexBuffer *CVkRenderContext::CreateVertexBuffer( uint32_t nSize ) { return (IVertexBuffer*)CreateBuffer(nSize, VK_BUFFER_USAGE_2_VERTEX_BUFFER_BIT); } //----------------------------------------------------------------------------- // Creates index buffer. Wrapper over CreateBuffer //----------------------------------------------------------------------------- IIndexBuffer *CVkRenderContext::CreateIndexBuffer( uint32_t nSize ) { return (IIndexBuffer*)CreateBuffer(nSize, VK_BUFFER_USAGE_2_INDEX_BUFFER_BIT); } //----------------------------------------------------------------------------- // Creates basic vulkan buffer //----------------------------------------------------------------------------- IBuffer *CreateBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage ) { } //----------------------------------------------------------------------------- // Creates vulkan buffer aligned to the nAlignment // Useful for everything eg: ray tracing, which requires them to be aligned // to the groupBaseAlignment. //----------------------------------------------------------------------------- IBuffer *CreateBufferAligned( uint32_t nSize, uint32_t nAlignment, VkBufferUsageFlags2 eUsage ) { } IImage *CVkRenderContext::CreateRenderTarget( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) { } IImage *CVkRenderContext::CreateStorageImage( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) { } void CVkRenderContext::DestroyBuffer( IBuffer *pBuffer ) { } void CVkRenderContext::DestroyImage( IImage *pImage ) { } CVkRenderContext s_vkRenderContext; IRenderContext *g_pVkRenderContext = &s_vkRenderContext; uint32_t g_iDrawFamily; uint32_t g_iPresentFamily; VkInstance g_vkInstance; VkPhysicalDevice g_vkPhysicalDevice; VkDevice g_vkDevice; VkSwapchainKHR g_vkSwapchain; void CVkRenderContext::Init() { VkResult r; int nExtensionCount; CUtlVector extensions = {}; uint32_t nPhysicalDevicesCount; CUtlVector physicalDevices; uint32_t nNumQueueFamilies = 0; CUtlVector queueFamilyProperties; VkApplicationInfo stApplicationInfo = {}; VkInstanceCreateInfo stInstanceCreateInfo = {}; VkDeviceQueueCreateInfo stDeviceQueueCreateInfo = {}; VkDeviceCreateInfo stDeviceCreateInfo = {}; float fPriority = 1.0; r = volkInitialize(); VULKAN_RESULT_PRINT(r, volkInitialize) // Get extensions required by game window nExtensionCount = gamewindow->GetVulkanInstanceExtensionCount(); extensions.Resize(nExtensionCount); V_memcpy(extensions.GetData(), gamewindow->GetVulkanInstanceExtensions(), extensions.GetSize()*sizeof(const char*)); // Create instance stApplicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; stApplicationInfo.apiVersion = VK_API_VERSION_1_4; stApplicationInfo.pApplicationName = "funny"; stApplicationInfo.pEngineName = "funny"; stInstanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; stInstanceCreateInfo.pApplicationInfo = &stApplicationInfo; stInstanceCreateInfo.enabledExtensionCount = extensions.GetSize(); stInstanceCreateInfo.ppEnabledExtensionNames = extensions.GetData(); r = vkCreateInstance(&stInstanceCreateInfo, NULL, &g_vkInstance); VULKAN_RESULT_PRINT(r, vkCreateInstance) // volk requires to load instance this way volkLoadInstance(g_vkInstance); // Get amount of physical devices r = vkEnumeratePhysicalDevices(g_vkInstance, &nPhysicalDevicesCount, NULL); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices) physicalDevices.Resize(nPhysicalDevicesCount); // Read all physical devices r = vkEnumeratePhysicalDevices(g_vkInstance, &nPhysicalDevicesCount, physicalDevices.GetData()); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices) g_vkPhysicalDevice = SelectPhysicalDevice(physicalDevices); // Get all queues vkGetPhysicalDeviceQueueFamilyProperties(g_vkPhysicalDevice, &nNumQueueFamilies, NULL); queueFamilyProperties.Resize(nNumQueueFamilies); uint32_t i = 0; vkGetPhysicalDeviceQueueFamilyProperties(g_vkPhysicalDevice, &nNumQueueFamilies, queueFamilyProperties.GetData()); for (auto &family: queueFamilyProperties) { if (family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { g_iDrawFamily = i; g_iPresentFamily = i; } i++; } // Create device stDeviceQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; stDeviceQueueCreateInfo.queueCount = 1; stDeviceQueueCreateInfo.pQueuePriorities = &fPriority; stDeviceQueueCreateInfo.queueFamilyIndex = g_iDrawFamily; stDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; stDeviceCreateInfo.queueCreateInfoCount = 1; stDeviceCreateInfo.pQueueCreateInfos = &stDeviceQueueCreateInfo; vkCreateDevice(g_vkPhysicalDevice, &stDeviceCreateInfo, NULL, &g_vkDevice); CreateSwapchain(); } void CVkRenderContext::Frame( float fDeltaTime ) { } void CVkRenderContext::CreateSwapchain() { CUtlVector surfaceFormats; gamewindow->CreateVulkanSurface(g_vkInstance); VkSwapchainCreateInfoKHR stSwapchainCreateInfo = {}; stSwapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; stSwapchainCreateInfo.surface = (VkSurfaceKHR)gamewindow->GetVulkanSurface(); vkCreateSwapchainKHR(g_vkDevice, &stSwapchainCreateInfo, NULL, &g_vkSwapchain); } void CVkRenderContext::DestroySwapchain() { vkDestroySwapchainKHR(g_vkDevice, g_vkSwapchain, NULL); gamewindow->DestroyVulkanSurface(g_vkInstance); } //----------------------------------------------------------------------------- // TODO: Move to the rendering context //----------------------------------------------------------------------------- void CVkRenderContext::Shutdown() { vkDestroyInstance(g_vkInstance, NULL); } //----------------------------------------------------------------------------- // Selects best device to be used in rendering. // We want to get the best perfomance out of the GPU, so we need to select the // best driver. //----------------------------------------------------------------------------- VkPhysicalDevice CVkRenderContext::SelectPhysicalDevice( CUtlVector physicalDevices ) { uint32_t uMaxScore = 0; VkPhysicalDevice selectedDevice = 0; VkResult r; for (auto &device: physicalDevices) { uint32_t nExtensionCount; CUtlVector extensions; VkPhysicalDeviceFeatures stPhysicalDeviceFeatures; VkPhysicalDeviceProperties stPhysicalDeviceProperties; uint32_t uScore = 0; r = vkEnumerateDeviceExtensionProperties(device, NULL, &nExtensionCount, NULL); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices) extensions.Resize(nExtensionCount); r = vkEnumerateDeviceExtensionProperties(device, NULL, &nExtensionCount, extensions.GetData()); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices) vkGetPhysicalDeviceProperties(device, &stPhysicalDeviceProperties); vkGetPhysicalDeviceFeatures(device, &stPhysicalDeviceFeatures); if (stPhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) uScore += 1000; if (stPhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) uScore += 500; if (uScore >= uMaxScore) { selectedDevice = device; uMaxScore = uScore; } } return selectedDevice; }