#include "commands.h" #include "materialsystem/imaterialsystem.h" #include "tier0/lib.h" #include "tier0/platform.h" #include "tier1/interface.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" #include "libraries.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 SupportedVulkanExtensions_t g_vkAvailableExtensions; uint32_t g_iDrawFamily; uint32_t g_iPresentFamily; VkQueue g_vkDrawQueue; VkQueue g_vkPresentQueue; VkInstance g_vkInstance; VmaAllocator g_vkAllocator; VkPhysicalDevice g_vkPhysicalDevice; VkDevice g_vkDevice; VkSwapchainKHR g_vkSwapchain; CUtlVector g_vkFences = {}; CUtlVector g_vkGraphicsSemaphores = {}; CUtlVector g_vkPresentSemaphores = {}; CUtlVector g_vkCommandPools = {}; CUtlVector g_vkSwapchainImages = {}; VkFormat g_vkWindowImageFormat; CVkImage::CVkImage() { } CVkImage::CVkImage( uint32_t nWidth, uint32_t nHeight, uint32_t nDepth, EImageFormat eFormat, EMultisampleType eMultisampleType, EImageType eImageType, VkImageUsageFlagBits eUsage ) { m_nWidth = nWidth; m_nHeight = nHeight; m_eMultisampleType = eMultisampleType; m_eImageType = eImageType; m_eFormat = eFormat; m_ePreferredLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; CreateImage(nWidth, nHeight, eFormat, eMultisampleType, eUsage); CreateImageView(); } CVkImage::~CVkImage() { } VkImageViewType CVkImage::GetImageViewType( enum EImageType eImageType ) { switch ( eImageType ) { case IMAGE_TYPE_1D: return VK_IMAGE_VIEW_TYPE_1D; case IMAGE_TYPE_2D: return VK_IMAGE_VIEW_TYPE_2D; case IMAGE_TYPE_3D: return VK_IMAGE_VIEW_TYPE_3D; case IMAGE_TYPE_CUBE: return VK_IMAGE_VIEW_TYPE_CUBE; case IMAGE_TYPE_1D_ARRAY: return VK_IMAGE_VIEW_TYPE_1D_ARRAY; case IMAGE_TYPE_2D_ARRAY: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; case IMAGE_TYPE_CUBE_ARRAY: return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; } } VkFormat CVkImage::GetImageFormat( enum EImageFormat eImageFormat ) { switch ( eImageFormat ) { case IMAGE_FORMAT_R8_UINT: return VK_FORMAT_R8_UINT; case IMAGE_FORMAT_RGBA8_UNORM: return VK_FORMAT_R8G8B8A8_UNORM; case IMAGE_FORMAT_BGRA8_UNORM: return VK_FORMAT_B8G8R8A8_UNORM; case IMAGE_FORMAT_RGBA8_UINT: return VK_FORMAT_R8G8B8A8_UINT; case IMAGE_FORMAT_RGBA8_SINT: return VK_FORMAT_R8G8B8A8_SINT; case IMAGE_FORMAT_RGBA16_SFLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT; case IMAGE_FORMAT_RGBA32_SFLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT; case IMAGE_FORMAT_D32_SFLOAT: return VK_FORMAT_D32_SFLOAT; case IMAGE_FORMAT_WINDOW: return g_vkWindowImageFormat; } } void CVkImage::CreateImage( uint32_t nWidth, uint32_t nHeight, EImageFormat eFormat, EMultisampleType eMultisampleType, VkImageUsageFlagBits eUsage ) { VkImageCreateInfo stCreateInfo = {}; VmaAllocationCreateInfo stAlloc = {}; stCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; stCreateInfo.imageType = VK_IMAGE_TYPE_2D; stCreateInfo.extent.width = nWidth; stCreateInfo.extent.height = nHeight; stCreateInfo.extent.depth = 1; stCreateInfo.mipLevels = 1; stCreateInfo.arrayLayers = 1; stCreateInfo.usage = eUsage | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; stCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; stCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; stCreateInfo.format = GetImageFormat(eFormat); stCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; stAlloc.usage = VMA_MEMORY_USAGE_AUTO; vmaCreateImage(g_vkAllocator, &stCreateInfo, &stAlloc, &m_image, &m_allocation, NULL); } void CVkImage::CreateImageView() { VkImageViewCreateInfo stImageViewCreateInfo = {}; VkImageViewType eImageViewType; stImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; stImageViewCreateInfo.image = m_image; stImageViewCreateInfo.viewType = GetImageViewType(m_eImageType); stImageViewCreateInfo.format = GetImageFormat(m_eFormat); if (m_eFormat == IMAGE_FORMAT_D32_SFLOAT) stImageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; else stImageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; stImageViewCreateInfo.subresourceRange.layerCount = 1; stImageViewCreateInfo.subresourceRange.levelCount = 1; m_range = stImageViewCreateInfo.subresourceRange; vkCreateImageView(g_vkDevice, &stImageViewCreateInfo, NULL, &m_imageView); } void CVkImage::SetDebugName( const char *szName ) { } uint32_t CVkImage::GetImageWidth() { return m_nWidth; } uint32_t CVkImage::GetImageHeight() { return m_nHeight; } EImageFormat CVkImage::GetImageFormat() { return m_eFormat; } EMultisampleType CVkImage::GetMultisampleType() { return m_eMultisampleType; } CVkBuffer::CVkBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage, uint32_t nAlignment ) { VkBufferUsageFlags2CreateInfo stUsage = {}; VkBufferCreateInfo stBufferInfo = {}; VmaAllocationCreateInfo stAllocInfo = {}; VkBufferDeviceAddressInfo stAddress = {}; stUsage.sType = VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO; stUsage.usage = eUsage | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT; stBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; stBufferInfo.size = nSize; stBufferInfo.pNext = &stUsage; stAllocInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; stAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; vmaCreateBuffer(g_vkAllocator, &stBufferInfo, &stAllocInfo, &m_buffer, &m_allocation, NULL); stAddress.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; stAddress.buffer = m_buffer; m_address = vkGetBufferDeviceAddress(g_vkDevice, &stAddress); m_nSize = nSize; } CVkBuffer::~CVkBuffer() { } void CVkBuffer::SetDebugName( const char *szName ) { } void CVkBuffer::Lock() { } void CVkBuffer::Unlock() { } void *CVkBuffer::Map() { void *pData; pData = NULL; vmaMapMemory(g_vkAllocator, m_allocation, &pData); return pData; } void CVkBuffer::Unmap() { vmaUnmapMemory(g_vkAllocator, m_allocation); } uint32_t CVkBuffer::GetSize() { return m_nSize; } 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 IBuffer *CreateConstantBuffer( uint32_t nSize ) override; virtual IBuffer *CreateStorageBuffer( uint32_t nSize ) override; virtual void DestroyBuffer( IBuffer *pBuffer ) 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 DestroyImage( IImage *pImage ) override; IBuffer *CreateBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage ); IBuffer *CreateBufferAligned( uint32_t nSize, uint32_t nAlignment, VkBufferUsageFlags2 eUsage ); virtual IShader *CreateShader( const char *szName ) override; virtual void DestroyShader( IShader *pShader ) override; virtual IMaterial *CreateMaterial( IShader *pShader ) override; virtual void DestroyMaterial( IMaterial *pMaterial ) override; virtual IRenderCommandList *CreateCommandList() override; virtual void DestroyCommandList( IRenderCommandList *pCommandList ) override; virtual void SubmitCommandList(IRenderCommandList *pList) override; virtual void SetMainWindowManager( IGameWindowManager *pWindowManager ) override; virtual void RenderGameWindow( IGameWindow *pWindow ) override; virtual void RegisterGameWindow( IGameWindow *pWindow ) override; virtual void UnregisterGameWindow( IGameWindow *pWindow ) override; private: VkPhysicalDevice SelectPhysicalDevice( CUtlVector physicalDevices ); CUtlVector GetDeviceExtensions(); VkCommandBuffer GetCommandBuffer(); void CreateSwapchain( IGameWindow *pWindow ); void DestroySwapchain( IGameWindow *pWindow ); IGameWindowManager *m_pWindowManager; }; EXPOSE_INTERFACE(CVkRenderContext, IRenderContext, RENDER_CONTEXT_INTERFACE_VERSION); //----------------------------------------------------------------------------- // Creates vertex buffer. Wrapper over CreateBuffer //----------------------------------------------------------------------------- IVertexBuffer *CVkRenderContext::CreateVertexBuffer( uint32_t nSize ) { return (IVertexBuffer*)CreateBuffer(nSize, VK_BUFFER_USAGE_2_VERTEX_BUFFER_BIT |VK_BUFFER_USAGE_2_STORAGE_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 | VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT); } //----------------------------------------------------------------------------- // Creates storage buffer. Wrapper over CreateBuffer //----------------------------------------------------------------------------- IBuffer *CVkRenderContext::CreateStorageBuffer( uint32_t nSize ) { return (IVertexBuffer*)CreateBuffer(nSize, VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT); } //----------------------------------------------------------------------------- // Creates constant buffer. Wrapper over CreateBuffer //----------------------------------------------------------------------------- IBuffer *CVkRenderContext::CreateConstantBuffer( uint32_t nSize ) { return (IIndexBuffer*)CreateBuffer(nSize, VK_BUFFER_USAGE_2_UNIFORM_BUFFER_BIT); } //----------------------------------------------------------------------------- // Creates basic vulkan buffer //----------------------------------------------------------------------------- IBuffer *CVkRenderContext::CreateBuffer( uint32_t nSize, VkBufferUsageFlags2 eUsage ) { CVkBuffer *pBuffer = new CVkBuffer(nSize, eUsage, 0); return pBuffer; } //----------------------------------------------------------------------------- // Creates vulkan buffer aligned to the nAlignment // Useful for everything eg: ray tracing, which requires them to be aligned // to the groupBaseAlignment. //----------------------------------------------------------------------------- IBuffer *CVkRenderContext::CreateBufferAligned( uint32_t nSize, uint32_t nAlignment, VkBufferUsageFlags2 eUsage ) { } IImage *CVkRenderContext::CreateRenderTarget( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) { if (eFormat == IMAGE_FORMAT_D32_SFLOAT) return new CVkImage(x, y, 1, eFormat, eMultisampleType, IMAGE_TYPE_2D, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); else return new CVkImage(x, y, 1, eFormat, eMultisampleType, IMAGE_TYPE_2D, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); } IImage *CVkRenderContext::CreateStorageImage( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) { return new CVkImage(x, y, 1, eFormat, eMultisampleType, IMAGE_TYPE_2D, VK_IMAGE_USAGE_STORAGE_BIT); } void CVkRenderContext::DestroyBuffer( IBuffer *pBuffer ) { delete (CVkBuffer*)pBuffer; } void CVkRenderContext::DestroyImage( IImage *pImage ) { } IShader *CVkRenderContext::CreateShader( const char *szName ) { CCompiledShader stShader = {}; CompiledShaderManager()->ReadFromFile(&stShader, szName); CVkVertexDescriptionPipelineLibrary vertexDescription = {}; vertexDescription.AddAttribute(0, 0, VERTEX_FORMAT_XYZ32_SFLOAT, 0); vertexDescription.AddLayout(0, 12); vertexDescription.SetTopology(TOPOLOGY_MODE_TRIANGLE_LIST); vertexDescription.Build(); CVkVertexTransformPipelineLibrary vertexTransform = {}; vertexTransform.SetShader(&stShader); vertexTransform.Build(); CVkPixelShaderPipelineLibrary pixelShader = {}; pixelShader.SetShader(&stShader); pixelShader.Build(); CVkPixelOutputPipelineLibrary pixelOutput = {}; pixelOutput.AddAttachment(IMAGE_FORMAT_RGBA8_UNORM); pixelOutput.Build(); CVkShader *pShader = new CVkShader(); pShader->AddShaderLibrary(&vertexDescription); pShader->AddShaderLibrary(&vertexTransform); pShader->AddShaderLibrary(&pixelShader); pShader->AddShaderLibrary(&pixelOutput); pShader->Build(); return pShader; } void CVkRenderContext::DestroyShader( IShader *pShader ) { delete pShader; } IMaterial *CVkRenderContext::CreateMaterial( IShader *pShader ) { return new CVkMaterial(pShader); } void CVkRenderContext::DestroyMaterial( IMaterial *pMaterial ) { delete pMaterial; } IRenderCommandList *CVkRenderContext::CreateCommandList() { return new CVkRenderCommandList; } void CVkRenderContext::DestroyCommandList( IRenderCommandList *pCommandList ) { } void CVkRenderContext::SubmitCommandList(IRenderCommandList *pList) { CVkRenderCommandList *pVkList = (CVkRenderCommandList*)pList; pVkList->Submit(); } void CVkRenderContext::RenderGameWindow( IGameWindow *pWindow ) { } void CVkRenderContext::SetMainWindowManager( IGameWindowManager *pWindowManager ) { m_pWindowManager = pWindowManager; } void CVkRenderContext::RegisterGameWindow( IGameWindow *pWindow ) { } void CVkRenderContext::UnregisterGameWindow( IGameWindow *pWindow ) { } VkPipelineLayout g_pLibraryEmptyLayout; static IVkCommandBuffer *s_pPresentCommandBuffer; void CVkRenderContext::Init() { VkResult r; int nExtensionCount; CUtlVector enabledInstanceExtensions; CUtlVector enabledDeviceExtensions; 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); if (m_pWindowManager) { nExtensionCount = m_pWindowManager->GetVulkanInstanceExtensionCount(); enabledInstanceExtensions.Resize(nExtensionCount); V_memcpy( enabledInstanceExtensions.GetData(), m_pWindowManager->GetVulkanInstanceExtensions(), nExtensionCount*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 = enabledInstanceExtensions.GetSize(); stInstanceCreateInfo.ppEnabledExtensionNames = enabledInstanceExtensions.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); enabledDeviceExtensions = GetDeviceExtensions(); // 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; VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT gplFeatures = {}; gplFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GRAPHICS_PIPELINE_LIBRARY_FEATURES_EXT; gplFeatures.graphicsPipelineLibrary = VK_TRUE; VkPhysicalDeviceVulkan13Features vk13Features = {}; vk13Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; vk13Features.pNext = &gplFeatures; vk13Features.synchronization2 = VK_TRUE; vk13Features.dynamicRendering = VK_TRUE; VkPhysicalDeviceVulkan12Features vk12Features = {}; vk12Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; vk12Features.pNext = &vk13Features; vk12Features.bufferDeviceAddress = VK_TRUE; stDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; stDeviceCreateInfo.queueCreateInfoCount = 1; stDeviceCreateInfo.pQueueCreateInfos = &stDeviceQueueCreateInfo; stDeviceCreateInfo.enabledExtensionCount = enabledDeviceExtensions.GetSize(); stDeviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions.GetData(); stDeviceCreateInfo.pNext = &vk12Features; r = vkCreateDevice(g_vkPhysicalDevice, &stDeviceCreateInfo, NULL, &g_vkDevice); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); for (auto &extension: enabledDeviceExtensions) V_printf("%s\n", extension); vkGetDeviceQueue(g_vkDevice, g_iDrawFamily, 0, &g_vkDrawQueue); vkGetDeviceQueue(g_vkDevice, g_iPresentFamily, 0, &g_vkPresentQueue); volkLoadDevice(g_vkDevice); VmaAllocatorCreateInfo stAllocatorInfo = {}; stAllocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT | VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT ; stAllocatorInfo.vulkanApiVersion = VK_API_VERSION_1_4; stAllocatorInfo.physicalDevice = g_vkPhysicalDevice; stAllocatorInfo.device = g_vkDevice; stAllocatorInfo.instance = g_vkInstance; VmaVulkanFunctions vulkanFunctions; vmaImportVulkanFunctionsFromVolk(&stAllocatorInfo, &vulkanFunctions); stAllocatorInfo.pVulkanFunctions = &vulkanFunctions; vmaCreateAllocator(&stAllocatorInfo, &g_vkAllocator); VkPipelineLayoutCreateInfo stPipelineLayout = {}; stPipelineLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; stPipelineLayout.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT; vkCreatePipelineLayout(g_vkDevice, &stPipelineLayout, NULL, &g_pLibraryEmptyLayout); g_vkCommandPools.Resize(g_vkSwapchainImages.GetSize()); for (auto &pool: g_vkCommandPools) { VkCommandPoolCreateInfo commandPoolCreateInfo = {}; commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; commandPoolCreateInfo.queueFamilyIndex = g_iDrawFamily; commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; vkCreateCommandPool(g_vkDevice, &commandPoolCreateInfo, NULL, &pool); } g_pCommandBufferManager = (IVkCommandBufferManager*)CreateInterface(VULKAN_COMMAND_BUFFER_MANAGER_INTERFACE_NAME, NULL); g_pCommandBufferManager->Init(); } void CVkRenderContext::Frame( float fDeltaTime ) { /* CVkBlitCommand *pBlitCommand = NULL; if (m_pOutputImage) { pBlitCommand = CREATE_COMMAND(Blit); pBlitCommand->AddDependency(m_pOutputImage, DEPENDENCY_MODE_BLIT_IMAGE_SOURCE); pBlitCommand->AddSwapchainDependency((IRenderingObject**)g_vkSwapchainImages.GetData(), DEPENDENCY_MODE_BLIT_IMAGE_DESTINATION); pBlitCommand->stInputImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; pBlitCommand->stInputImage.m_pSingle = m_pOutputImage; pBlitCommand->stOutputImage.m_eObjectType = FRAME_OBJECT_TYPE_SWAPPED; pBlitCommand->stOutputImage.m_ppSwapped = (IRenderingObject**)g_vkSwapchainImages.GetData(); pBlitCommand->iSrcMax[0] = 1280; pBlitCommand->iSrcMax[1] = 720; pBlitCommand->iSrcMax[2] = 1; pBlitCommand->iDstMax[0] = 1280; pBlitCommand->iDstMax[1] = 720; pBlitCommand->iDstMax[2] = 1; } s_pPresentCommandBuffer = g_pCommandBufferManager->CreateCommandBuffer(); s_pPresentCommandBuffer->Reset(); if (pBlitCommand != NULL) s_pPresentCommandBuffer->AddCommand(pBlitCommand); s_pPresentCommandBuffer->Render(); vkDeviceWaitIdle(g_vkDevice); static uint32_t s_nImageIndex = 0; uint32_t nImageIndex = 0; vkWaitForFences(g_vkDevice, 1, &g_vkFences[s_nImageIndex], VK_TRUE, UINT64_MAX); VkResult r = vkAcquireNextImageKHR(g_vkDevice, g_vkSwapchain, UINT64_MAX, g_vkGraphicsSemaphores[s_nImageIndex], NULL, &nImageIndex); vkResetFences(g_vkDevice, 1, &g_vkFences[s_nImageIndex]); s_pPresentCommandBuffer->Submit(nImageIndex); VkPipelineStageFlags uPipelineStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo stSubmitInfo = {}; stSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; stSubmitInfo.waitSemaphoreCount = 1; stSubmitInfo.pWaitSemaphores = &g_vkGraphicsSemaphores[s_nImageIndex]; stSubmitInfo.pWaitDstStageMask = &uPipelineStageFlags; stSubmitInfo.commandBufferCount = g_vkCommandBuffers.GetSize(); stSubmitInfo.pCommandBuffers = g_vkCommandBuffers.GetData(); stSubmitInfo.signalSemaphoreCount = 1; stSubmitInfo.pSignalSemaphores = &g_vkPresentSemaphores[nImageIndex]; vkQueueSubmit(g_vkDrawQueue, 1, &stSubmitInfo, g_vkFences[s_nImageIndex]); VkPresentInfoKHR stPresentInfo = {}; stPresentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; stPresentInfo.waitSemaphoreCount = 1; stPresentInfo.pWaitSemaphores = &g_vkPresentSemaphores[nImageIndex]; stPresentInfo.swapchainCount = 1; stPresentInfo.pSwapchains = &g_vkSwapchain; stPresentInfo.pImageIndices = &nImageIndex; vkQueuePresentKHR(g_vkPresentQueue, &stPresentInfo); s_nImageIndex = (s_nImageIndex + 1) % g_vkSwapchainImages.GetSize(); g_vkCommandBuffers = {}; */ } void CVkRenderContext::CreateSwapchain( IGameWindow *pWindow ) { uint32_t numSurfaceFormats = 0; CUtlVector surfaceFormats; VkSurfaceCapabilitiesKHR surfaceCapatibilities = {}; uint32_t nSurfacePresentModes = 0; CUtlVector surfacePresentModes; const VkFormat preferedSurfaceFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, }; VkSurfaceFormatKHR stSelectedFormat; VkSwapchainCreateInfoKHR stSwapchainCreateInfo = {}; VkFenceCreateInfo stFenceCreateInfo = {}; VkSemaphoreCreateInfo stSemaphoreCreateInfo = {}; uint32_t nSwapchainImages; CUtlVector swapchainImages; VkSurfaceKHR hSurface; hSurface = (VkSurfaceKHR)pWindow->CreateVulkanSurface(g_vkInstance); vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_vkPhysicalDevice, (VkSurfaceKHR)hSurface, &surfaceCapatibilities); vkGetPhysicalDeviceSurfaceFormatsKHR(g_vkPhysicalDevice, (VkSurfaceKHR)hSurface, &numSurfaceFormats, NULL); surfaceFormats.Resize(numSurfaceFormats); vkGetPhysicalDeviceSurfaceFormatsKHR(g_vkPhysicalDevice, (VkSurfaceKHR)hSurface, &numSurfaceFormats, surfaceFormats.GetData()); stSelectedFormat = surfaceFormats[0]; for (auto &format: surfaceFormats) { for (int i = 0; i < sizeof(preferedSurfaceFormats)/sizeof(VkFormat); i++) { if (format.format == preferedSurfaceFormats[i]) { stSelectedFormat = format; goto formatPicked; } } } formatPicked: vkGetPhysicalDeviceSurfacePresentModesKHR(g_vkPhysicalDevice, hSurface, &nSurfacePresentModes, NULL); surfacePresentModes.Resize(nSurfacePresentModes); vkGetPhysicalDeviceSurfacePresentModesKHR(g_vkPhysicalDevice, hSurface, &nSurfacePresentModes, surfacePresentModes.GetData()); stSwapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; stSwapchainCreateInfo.surface = hSurface; stSwapchainCreateInfo.imageFormat = stSelectedFormat.format; stSwapchainCreateInfo.imageColorSpace = stSelectedFormat.colorSpace; stSwapchainCreateInfo.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; stSwapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; stSwapchainCreateInfo.preTransform = surfaceCapatibilities.currentTransform; stSwapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; stSwapchainCreateInfo.imageArrayLayers = 1; stSwapchainCreateInfo.imageExtent = surfaceCapatibilities.minImageExtent; stSwapchainCreateInfo.minImageCount = surfaceCapatibilities.minImageCount; vkCreateSwapchainKHR(g_vkDevice, &stSwapchainCreateInfo, NULL, &g_vkSwapchain); g_vkWindowImageFormat = stSwapchainCreateInfo.imageFormat; vkGetSwapchainImagesKHR(g_vkDevice, g_vkSwapchain, &nSwapchainImages, NULL); g_vkSwapchainImages.Resize(nSwapchainImages); swapchainImages.Resize(nSwapchainImages); g_vkFences.Resize(nSwapchainImages); g_vkGraphicsSemaphores.Resize(nSwapchainImages); g_vkPresentSemaphores.Resize(nSwapchainImages); vkGetSwapchainImagesKHR(g_vkDevice, g_vkSwapchain, &nSwapchainImages, swapchainImages.GetData()); for ( int i = 0; i < swapchainImages.GetSize(); i++ ) { CVkImage *pImage; pImage = new CVkImage; pImage->m_image = swapchainImages[i]; pImage->m_eImageType = IMAGE_TYPE_2D; pImage->m_eMultisampleType = MULTISAMPLE_TYPE_NONE; pImage->m_eFormat = IMAGE_FORMAT_WINDOW; pImage->m_nHeight = 1280; pImage->m_nWidth = 720; pImage->m_ePreferredLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; pImage->CreateImageView(); g_vkSwapchainImages[i] = pImage; stFenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; stFenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; vkCreateFence(g_vkDevice, &stFenceCreateInfo, NULL, &g_vkFences[i]); stSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; vkCreateSemaphore(g_vkDevice, &stSemaphoreCreateInfo, NULL, &g_vkGraphicsSemaphores[i]); vkCreateSemaphore(g_vkDevice, &stSemaphoreCreateInfo, NULL, &g_vkPresentSemaphores[i]); } } void CVkRenderContext::DestroySwapchain( IGameWindow *pWindow ) { vkDestroySwapchainKHR(g_vkDevice, g_vkSwapchain, NULL); pWindow->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; } CUtlVector CVkRenderContext::GetDeviceExtensions() { VkResult r; int i; uint32_t nExtensionCount; CUtlVector extensions; CUtlVector enabledExtensions; const char *szExtensionName; r = vkEnumerateDeviceExtensionProperties(g_vkPhysicalDevice, NULL, &nExtensionCount, NULL); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); extensions.Resize(nExtensionCount); r = vkEnumerateDeviceExtensionProperties(g_vkPhysicalDevice, NULL, &nExtensionCount, extensions.GetData()); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); #define REQUIRED_EXTENSION(ext) if (!V_strcmp(extension.extensionName, ext##_EXTENSION_NAME)) { g_vkAvailableExtensions.bIsSupported_##ext = true; enabledExtensions.AppendTail(ext##_EXTENSION_NAME); continue; }; #define OPTIONAL_EXTENSION(ext) REQUIRED_EXTENSION(ext); for ( auto extension: extensions ) { #include "device_extensions.h" } #undef REQUIRED_EXTENSION #undef OPTIONAL_EXTENSION return enabledExtensions; }