#include "rendering.h" #include "tier0/platform.h" #include "tier1/utlvector.h" #include "console.h" #include "tier1/commandline.h" #include "input.h" #include "vulkan/vulkan.h" #include "SDL3/SDL.h" #include "vulkan/vulkan_core.h" #include #define SDL_MAIN_HANDLED #include "SDL3/SDL_main.h" #include "SDL3/SDL_vulkan.h" #include "SDL3/SDL_events.h" #define VULKAN_RENDERING_IMPL #include "vk_video.h" #define VMA_VULKAN_VERSION 1004000 #define VMA_IMPLEMENTATION #include "tier0/minmax_off.h" #include "vk_mem_alloc.h" #include "tier0/minmax.h" SDL_Window *g_window; VkInstance g_vkInstance = NULL; VkPhysicalDevice g_vkPhysicalDevice = NULL; VkDevice g_vkDevice = NULL; uint32_t g_drawfamily = 0; VkQueue g_drawQueue; uint32_t g_presentfamily = 0; VkQueue g_presentQueue; VmaAllocator g_allocator = NULL; VkSurfaceKHR g_surface; VkSwapchainKHR g_swapchain; ConVar vulkan_gpu("vk_gpu", "0", FCVAR_ARCHIVE); VkCommandPool g_vkCommandPool; /* more efficient */ CUtlVector g_commandBuffers; VkCommandBuffer g_vkCommandBuffer; CUtlVector g_frameData; CUtlVector g_swapchainImages; CUtlVector g_swapchainImageViews; VkImageView g_swapchainImageView; VkImage g_swapchainImage; VkFormat g_swapchainFormat; uint32_t g_nNumSwapchainImages = 0; char g_bConfigNotify = 0; uint32_t g_nWindowWidth = 1280; uint32_t g_nWindowHeight = 720; #define VK_DEVICE_FUNCTION(name) PFN_##name _##name #include "vk_external_functions.cpp" #undef VK_DEVICE_FUNCTION void IVideo_SwapchainInit() { /* swapchain */ SDL_Vulkan_CreateSurface(g_window, g_vkInstance, NULL, &g_surface); VkSurfaceCapabilitiesKHR surfaceCapatibilities = {}; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_vkPhysicalDevice, g_surface, &surfaceCapatibilities); const VkFormat preferedSurfaceFormats[] = { VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8A8_UNORM, }; uint32_t numSurfaceFormats = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(g_vkPhysicalDevice, g_surface, &numSurfaceFormats, NULL); CUtlVector surfaceFormats(numSurfaceFormats); vkGetPhysicalDeviceSurfaceFormatsKHR(g_vkPhysicalDevice, g_surface, &numSurfaceFormats, surfaceFormats.GetData()); VkSurfaceFormatKHR selectedFormat = surfaceFormats[0]; for (auto &format: surfaceFormats) { for (int i = 0; i < sizeof(preferedSurfaceFormats)/sizeof(VkFormat); i++) { selectedFormat = surfaceFormats[i]; } } uint32_t numSurfacePresentModes = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(g_vkPhysicalDevice, g_surface, &numSurfacePresentModes, NULL); CUtlVector surfacePresentModes(numSurfacePresentModes); vkGetPhysicalDeviceSurfacePresentModesKHR(g_vkPhysicalDevice, g_surface, &numSurfacePresentModes, surfacePresentModes.GetData()); VkSwapchainCreateInfoKHR swapchainCreateInfo = {}; swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; swapchainCreateInfo.surface = g_surface; swapchainCreateInfo.imageFormat = selectedFormat.format; swapchainCreateInfo.imageColorSpace = selectedFormat.colorSpace; swapchainCreateInfo.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_STORAGE_BIT; swapchainCreateInfo.preTransform = surfaceCapatibilities.currentTransform; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageExtent = surfaceCapatibilities.minImageExtent; swapchainCreateInfo.minImageCount = surfaceCapatibilities.minImageCount; vkCreateSwapchainKHR(g_vkDevice, &swapchainCreateInfo, NULL, &g_swapchain); vkGetSwapchainImagesKHR(g_vkDevice, g_swapchain, &g_nNumSwapchainImages, NULL); g_swapchainImages = CUtlVector(g_nNumSwapchainImages); g_swapchainImageViews = CUtlVector(g_nNumSwapchainImages); g_commandBuffers = CUtlVector(g_nNumSwapchainImages); g_frameData = CUtlVector(g_nNumSwapchainImages); vkGetSwapchainImagesKHR(g_vkDevice, g_swapchain, &g_nNumSwapchainImages, g_swapchainImages.GetData()); for (int i = 0; i < g_nNumSwapchainImages; i++) { VkImageViewCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; createInfo.image = g_swapchainImages[i]; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.format = selectedFormat.format; createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; createInfo.subresourceRange.baseMipLevel = 0; createInfo.subresourceRange.levelCount = 1; createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; vkCreateImageView(g_vkDevice, &createInfo, NULL, &g_swapchainImageViews[i]); } g_swapchainFormat = selectedFormat.format; /* command buffers */ VkCommandPoolCreateInfo commandPoolCreateInfo = {}; commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; commandPoolCreateInfo.queueFamilyIndex = g_drawfamily; commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; vkCreateCommandPool(g_vkDevice, &commandPoolCreateInfo, NULL, &g_vkCommandPool); VkCommandBufferAllocateInfo commandBufferAllocInfo = {}; commandBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBufferAllocInfo.commandPool = g_vkCommandPool; commandBufferAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; commandBufferAllocInfo.commandBufferCount = g_nNumSwapchainImages; vkAllocateCommandBuffers(g_vkDevice, &commandBufferAllocInfo, g_commandBuffers.GetData()); /* sync */ VkFenceCreateInfo fenceCreateInfo = {}; fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; VkSemaphoreCreateInfo semaphoreCreateInfo = {}; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; for (int i = 0; i < g_nNumSwapchainImages; i++) { vkCreateFence(g_vkDevice,&fenceCreateInfo, NULL, &g_frameData[i].fence); vkCreateSemaphore(g_vkDevice, &semaphoreCreateInfo, NULL, &g_frameData[i].draw); vkCreateSemaphore(g_vkDevice, &semaphoreCreateInfo, NULL, &g_frameData[i].present); } }; void IVideo_SwapchainDestroy() { vkDestroySwapchainKHR(g_vkDevice, g_swapchain, NULL); vkDestroySurfaceKHR(g_vkInstance, g_surface, NULL); for (int i = 0; i < g_nNumSwapchainImages; i++) { vkDestroyImageView(g_vkDevice, g_swapchainImageViews[i], NULL); vkDestroySemaphore(g_vkDevice, g_frameData[i].draw, NULL); vkDestroySemaphore(g_vkDevice, g_frameData[i].present, NULL); vkDestroyFence(g_vkDevice, g_frameData[i].fence, NULL); } }; EInputKey ISDL_KeyName(SDL_Keycode key) { switch(key) { case SDLK_1: return KEY_1; case SDLK_2: return KEY_2; case SDLK_3: return KEY_3; case SDLK_4: return KEY_4; case SDLK_5: return KEY_5; case SDLK_6: return KEY_6; case SDLK_7: return KEY_7; case SDLK_8: return KEY_8; case SDLK_9: return KEY_9; case SDLK_0: return KEY_0; case SDLK_A: return KEY_A; case SDLK_B: return KEY_B; case SDLK_C: return KEY_C; case SDLK_D: return KEY_D; case SDLK_E: return KEY_E; case SDLK_F: return KEY_F; case SDLK_G: return KEY_G; case SDLK_H: return KEY_H; case SDLK_I: return KEY_I; case SDLK_J: return KEY_J; case SDLK_K: return KEY_K; case SDLK_L: return KEY_L; case SDLK_M: return KEY_M; case SDLK_N: return KEY_N; case SDLK_O: return KEY_O; case SDLK_P: return KEY_P; case SDLK_Q: return KEY_Q; case SDLK_R: return KEY_R; case SDLK_S: return KEY_S; case SDLK_T: return KEY_T; case SDLK_U: return KEY_U; case SDLK_V: return KEY_V; case SDLK_W: return KEY_W; case SDLK_X: return KEY_X; case SDLK_Y: return KEY_Y; case SDLK_Z: return KEY_Z; } return KEY_NONE; }; void IVideo::Init() { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); g_window = SDL_CreateWindow("rtt", 1280, 720, SDL_WINDOW_VULKAN); unsigned int nExtensionCount = 0; const char* const* szExtensions = SDL_Vulkan_GetInstanceExtensions(&nExtensionCount); vulkan_gpu.SetValue(ICommandLine::ParamValue("-gpu")); VkResult r = VK_SUCCESS; uint32_t i = 0; /* Instance */ VkApplicationInfo applicationInfo = {}; applicationInfo.apiVersion = VK_API_VERSION_1_4; VkInstanceCreateInfo instanceCreateInfo = {}; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pApplicationInfo = &applicationInfo; instanceCreateInfo.enabledExtensionCount = nExtensionCount; instanceCreateInfo.ppEnabledExtensionNames = szExtensions; r = vkCreateInstance(&instanceCreateInfo, NULL, &g_vkInstance); if ( r != VK_SUCCESS ) Plat_FatalErrorFunc("Failed to create VkInstance!"); /* Physical Devices */ uint32_t nNumPhysicalDevices = 0; vkEnumeratePhysicalDevices(g_vkInstance, &nNumPhysicalDevices, NULL); if ( nNumPhysicalDevices == 0 ) Plat_FatalErrorFunc("No GPU drivers available!"); CUtlVector PhysicalDevices(nNumPhysicalDevices); vkEnumeratePhysicalDevices(g_vkInstance, &nNumPhysicalDevices, PhysicalDevices.GetData()); /* enumerate them for the user */ for (auto &device: PhysicalDevices) { VkPhysicalDeviceProperties Properties = {}; vkGetPhysicalDeviceProperties(device, &Properties); V_printf("GPU%i available: %s\n", i, Properties.deviceName); i++; } /* select one in vk_gpu */ g_vkPhysicalDevice = PhysicalDevices[vulkan_gpu.GetInt()]; VkPhysicalDeviceProperties Properties = {}; vkGetPhysicalDeviceProperties(g_vkPhysicalDevice, &Properties); V_printf("Using %s\n", Properties.deviceName); /* queue family */ uint32_t nNumQueueFamilies = 0; vkGetPhysicalDeviceQueueFamilyProperties(g_vkPhysicalDevice, &nNumQueueFamilies, NULL); CUtlVector queueFamilyProperties(nNumQueueFamilies); vkGetPhysicalDeviceQueueFamilyProperties(g_vkPhysicalDevice, &nNumQueueFamilies, queueFamilyProperties.GetData()); i = 0; for (auto &family: queueFamilyProperties) { if (family.queueFlags & VK_QUEUE_GRAPHICS_BIT) { g_drawfamily = i; g_presentfamily = i; } i++; } /* create device */ float queuePriority = 1.0f; VkDeviceQueueCreateInfo queueCreateInfo = {}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = g_drawfamily; queueCreateInfo.queueCount = 1; queueCreateInfo.pQueuePriorities = &queuePriority; VkPhysicalDeviceVertexInputDynamicStateFeaturesEXT pdvidsfe = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_INPUT_DYNAMIC_STATE_FEATURES_EXT, .vertexInputDynamicState = VK_TRUE, }; VkPhysicalDeviceExtendedDynamicState3FeaturesEXT pdeds3fe = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT, .pNext = &pdvidsfe, .extendedDynamicState3DepthClampEnable = VK_TRUE, .extendedDynamicState3PolygonMode = VK_TRUE, .extendedDynamicState3RasterizationSamples = VK_TRUE, .extendedDynamicState3SampleMask = VK_TRUE, .extendedDynamicState3AlphaToCoverageEnable = VK_TRUE, .extendedDynamicState3LogicOpEnable = VK_TRUE, .extendedDynamicState3ColorBlendEnable = VK_TRUE, .extendedDynamicState3ColorBlendEquation = VK_TRUE, .extendedDynamicState3ColorWriteMask = VK_TRUE, }; VkPhysicalDeviceVulkan13Features pdv13f = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, .pNext = &pdeds3fe, .synchronization2 = VK_TRUE, .dynamicRendering = VK_TRUE, }; VkPhysicalDeviceVulkan12Features pdv12f = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .pNext = &pdv13f, .runtimeDescriptorArray = VK_TRUE, .bufferDeviceAddress = VK_TRUE, }; VkPhysicalDeviceVulkan11Features pdv11f = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES, .pNext = &pdv12f, }; const char *szEnabledGPUExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME, }; VkDeviceCreateInfo deviceCreateInfo = {}; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; deviceCreateInfo.queueCreateInfoCount = 1; deviceCreateInfo.enabledExtensionCount = sizeof(szEnabledGPUExtensions)/(sizeof(const char*)); deviceCreateInfo.ppEnabledExtensionNames = szEnabledGPUExtensions; deviceCreateInfo.pNext = &pdv11f; r = vkCreateDevice(g_vkPhysicalDevice, &deviceCreateInfo, NULL, &g_vkDevice); if ( r != VK_SUCCESS ) Plat_FatalErrorFunc("Failed to create VkDevice!"); vkGetDeviceQueue(g_vkDevice, g_drawfamily, 0, &g_drawQueue); vkGetDeviceQueue(g_vkDevice, g_presentfamily, 0, &g_presentQueue); VmaVulkanFunctions vulkanFunctions = {}; vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; VmaAllocatorCreateInfo allocatorCreateInfo = {}; allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_4; allocatorCreateInfo.physicalDevice = g_vkPhysicalDevice; allocatorCreateInfo.device = g_vkDevice; allocatorCreateInfo.instance = g_vkInstance; vmaCreateAllocator(&allocatorCreateInfo, &g_allocator); #define VK_DEVICE_FUNCTION(name) _##name = (PFN_##name)vkGetDeviceProcAddr(g_vkDevice, #name) #include "vk_external_functions.cpp" #undef VK_DEVICE_FUNCTION IVideo_SwapchainInit(); IVulkan::Init(); } void IVideo_HandleEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { SDL_KeyboardEvent *key = &event.key; switch (event.type) { case SDL_EVENT_WINDOW_RESIZED: g_nWindowWidth = event.window.data1; g_nWindowHeight = event.window.data2; g_bConfigNotify = 2; break; case SDL_EVENT_KEY_DOWN: if (!key->repeat) IInput::KeyEvent(ISDL_KeyName(key->key),KEY_EVENT_TYPE_UP); break; case SDL_EVENT_KEY_UP: key = &event.key; SDL_Log("Key Up: %s", SDL_GetKeyName(key->key)); break; } }; }; void IVideo::Frame( float fDelta ) { static uint32_t s_frameID = 0; IVideo_HandleEvents(); vk_framedata_t frame = g_frameData[s_frameID]; vkDeviceWaitIdle(g_vkDevice); vkWaitForFences(g_vkDevice, 1, &frame.fence, VK_TRUE, UINT64_MAX); uint32_t imageIndex = 0; VkResult r = vkAcquireNextImageKHR(g_vkDevice, g_swapchain, UINT64_MAX, frame.draw, VK_NULL_HANDLE, &imageIndex); if (r == VK_ERROR_OUT_OF_DATE_KHR || r == VK_SUBOPTIMAL_KHR || g_bConfigNotify == 2) { g_bConfigNotify=1; vkDeviceWaitIdle(g_vkDevice); IVideo_SwapchainDestroy(); IVideo_SwapchainInit(); return; } vk_framedata_t frameindex = g_frameData[imageIndex]; vkResetFences(g_vkDevice, 1, &frame.fence); g_vkCommandBuffer = g_commandBuffers[s_frameID]; g_swapchainImageView = g_swapchainImageViews[imageIndex]; g_swapchainImage = g_swapchainImages[imageIndex]; vkResetCommandBuffer(g_vkCommandBuffer, 0); VkCommandBufferBeginInfo beginInfo = {}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkBeginCommandBuffer(g_vkCommandBuffer, &beginInfo); IVulkan::Frame(); vkEndCommandBuffer(g_vkCommandBuffer); VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSubmitInfo submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = &frame.draw; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &g_vkCommandBuffer; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &frameindex.present; vkQueueSubmit(g_drawQueue, 1, &submitInfo, frame.fence); VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = &frameindex.present; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &g_swapchain; presentInfo.pImageIndices = &imageIndex; r = vkQueuePresentKHR(g_presentQueue, &presentInfo); if (r == VK_ERROR_OUT_OF_DATE_KHR || r == VK_SUBOPTIMAL_KHR) { vkDeviceWaitIdle(g_vkDevice); IVideo_SwapchainDestroy(); IVideo_SwapchainInit(); return; } g_bConfigNotify = 0; s_frameID=(s_frameID+1)%g_nNumSwapchainImages; };