#include "commands.h" #include "materialsystem/imaterialsystem.h" #include "shadercompiler/icompiler.h" #include "tier0/lib.h" #include "tier0/platform.h" #include "tier1/interface.h" #include "tier1/utlstring.h" #include "tier1/utlvector.h" #include "tier2/ifilesystem.h" #define VK_NO_PROTOTYPES #include "vulkan/vulkan_core.h" #include "vulkan_state.h" #include "materialsystem/igamewindow.h" #include "libraries.h" #include "stb/stb_image.h" #define REQUIRED_EXTENSION(ext) ext##_EXTENSION_NAME, #define OPTIONAL_EXTENSION(ext) ext##_EXTENSION_NAME, static const char *s_vkDeviceExtensions[] = { #include "device_extensions.h" }; #undef REQUIRED_EXTENSION #undef OPTIONAL_EXTENSION SupportedVulkanExtensions_t g_vkAvailableExtensions = {}; uint32_t g_iDrawFamily; uint32_t g_iPresentFamily; VkQueue s_vkDrawQueue; VkQueue s_vkPresentQueue; VkInstance s_vkInstance; VmaAllocator s_vkAllocator; VkPhysicalDevice s_vkPhysicalDevice; VkDevice s_vkDevice; VkSwapchainKHR s_vkSwapchain; struct VulkanWindow_t { IGameWindow *m_pWindow; VkSurfaceKHR m_surface; VkSwapchainKHR m_swapchain; VkFormat m_eFormat; VkFence m_fences[FRAMES_IN_FLIGHT]; VkSemaphore m_imageAvailable[FRAMES_IN_FLIGHT]; VkSemaphore m_renderFinished[FRAMES_IN_FLIGHT]; CUtlVector m_images; uint32_t m_uCurrentFrame; }; 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; if (eFormat == IMAGE_FORMAT_D32_SFLOAT) m_ePreferredLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL; else 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_RGB8_UINT: return VK_FORMAT_R8G8B8_UINT; case IMAGE_FORMAT_RGB8_SINT: return VK_FORMAT_R8G8B8_SINT; case IMAGE_FORMAT_RGB8_UNORM: return VK_FORMAT_R8G8B8_UNORM; case IMAGE_FORMAT_RGB8_SNORM: return VK_FORMAT_R8G8B8_SNORM; case IMAGE_FORMAT_RGB16_UINT: return VK_FORMAT_R16G16B16_UINT; case IMAGE_FORMAT_RGB16_SINT: return VK_FORMAT_R16G16B16_SINT; case IMAGE_FORMAT_RGB16_UNORM: return VK_FORMAT_R16G16B16_UNORM; case IMAGE_FORMAT_RGB16_SNORM: return VK_FORMAT_R16G16B16_SNORM; case IMAGE_FORMAT_RGB16_SFLOAT: return VK_FORMAT_R16G16B16_SFLOAT; case IMAGE_FORMAT_RGB32_SFLOAT: return VK_FORMAT_R32G32B32_SFLOAT; 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_UINT: return VK_FORMAT_R16G16B16A16_UINT; case IMAGE_FORMAT_RGBA16_SINT: return VK_FORMAT_R16G16B16A16_SINT; case IMAGE_FORMAT_RGBA16_UNORM: return VK_FORMAT_R16G16B16A16_UNORM; case IMAGE_FORMAT_RGBA16_SNORM: return VK_FORMAT_R16G16B16A16_SNORM; case IMAGE_FORMAT_RGBA16_SFLOAT: return VK_FORMAT_R16G16B16A16_SFLOAT; case IMAGE_FORMAT_RGBA32_SFLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT; case IMAGE_FORMAT_BC1: return VK_FORMAT_BC1_RGB_UNORM_BLOCK; case IMAGE_FORMAT_BC3: return VK_FORMAT_BC3_UNORM_BLOCK; case IMAGE_FORMAT_BC7: return VK_FORMAT_BC7_UNORM_BLOCK; case IMAGE_FORMAT_D32_SFLOAT: return VK_FORMAT_D32_SFLOAT; } return VK_FORMAT_UNDEFINED; } VkSampleCountFlagBits CVkImage::GetMultisampling( enum EMultisampleType eImageFormat ) { switch ( eImageFormat ) { case MULTISAMPLE_TYPE_1_SAMPLES: return VK_SAMPLE_COUNT_1_BIT; case MULTISAMPLE_TYPE_2_SAMPLES: return VK_SAMPLE_COUNT_2_BIT; case MULTISAMPLE_TYPE_4_SAMPLES: return VK_SAMPLE_COUNT_4_BIT; case MULTISAMPLE_TYPE_8_SAMPLES: return VK_SAMPLE_COUNT_8_BIT; } } 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 = GetMultisampling(eMultisampleType); stAlloc.usage = VMA_MEMORY_USAGE_AUTO; vmaCreateImage(s_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(s_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 | VK_BUFFER_USAGE_2_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_2_TRANSFER_DST_BIT; if (eUsage & VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT) { m_eDescriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; } if (eUsage & VK_BUFFER_USAGE_2_UNIFORM_BUFFER_BIT) { m_eDescriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; } 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; VkResult r = vmaCreateBuffer(s_vkAllocator, &stBufferInfo, &stAllocInfo, &m_buffer, &m_allocation, NULL); stAddress.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; stAddress.buffer = m_buffer; m_address = vkGetBufferDeviceAddress(s_vkDevice, &stAddress); m_nSize = nSize; } CVkBuffer::~CVkBuffer() { vmaDestroyBuffer(s_vkAllocator, m_buffer, m_allocation); } void CVkBuffer::SetDebugName( const char *szName ) { } void CVkBuffer::Lock() { } void CVkBuffer::Unlock() { } void *CVkBuffer::Map() { void *pData; pData = NULL; VkResult r = vmaMapMemory(s_vkAllocator, m_allocation, &pData); VULKAN_RESULT_PRINT(r, vmaMapMemory); return pData; } void CVkBuffer::Unmap() { vmaUnmapMemory(s_vkAllocator, m_allocation); } uint32_t CVkBuffer::GetSize() { return m_nSize; } CVkTextureArray::~CVkTextureArray() { } CVkTextureArray::CVkTextureArray() { for (uint32_t i = 0; i < 128; i++ ) m_pImages[i] = NULL; } void CVkTextureArray::Build() { for (uint32_t i = 0; i < 128; i++ ) m_pImages[i] = NULL; VkSamplerCreateInfo samplerInfo = {}; samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerInfo.magFilter = VK_FILTER_LINEAR; samplerInfo.minFilter = VK_FILTER_LINEAR; samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; samplerInfo.unnormalizedCoordinates = VK_FALSE; samplerInfo.compareEnable = VK_FALSE; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = VK_LOD_CLAMP_NONE; vkCreateSampler(m_hDevice, &samplerInfo, nullptr, &m_hSampler); LoadTexture("game/core/textures/error.png"); } void CVkTextureArray::SetDebugName( const char *szPath ) { } uint32_t CVkTextureArray::LoadTexture( const char *szPath ) { for (uint32_t i = 0; i < 128; i++ ) { if (m_pImages[i] == NULL) return CreateTexture(i, szPath); } return 0; } extern IFileSystem *filesystem; uint32_t CVkTextureArray::CreateTexture( uint32_t i, const char *szPath ) { if ( filesystem == NULL ) { CreateInterfaceFn pFilesystemFactory = Sys_GetFactory("filesystem_std"); filesystem = (IFileSystem*)pFilesystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL); } IFileHandle *pHandle = filesystem->Open(szPath, FILEMODE_READ); CUtlBuffer data = filesystem->Size(pHandle); filesystem->Read(pHandle, data.GetMemory(), data.GetSize()); filesystem->Close(pHandle); int uWidth = 0; int uHeight = 0; int uChannels = 0; unsigned char *pImg = stbi_load_from_memory(data.GetMemory(), data.GetSize(), &uWidth, &uHeight, &uChannels, 4); IBuffer *pBuffer = m_pRenderContext->CreateStorageBuffer(uWidth*uHeight*4); pBuffer->Lock(); void *pData = pBuffer->Map(); V_memcpy(pData, pImg, uWidth*uHeight*4); pBuffer->Unmap(); pBuffer->Unlock(); vkDeviceWaitIdle(s_vkDevice); IImage *pImage = m_pRenderContext->CreateTexture(uWidth, uHeight, IMAGE_FORMAT_RGBA8_UNORM, MULTISAMPLE_TYPE_NONE); m_pImages[i] = (CVkImage*)pImage; stbi_image_free(pImg); CVkCopyBufferToImageCommand *pCopyCommand = NULL; IVkCommandBuffer *pCommandBuffer = m_pCommandBufferManager->CreateCommandBuffer(); pCommandBuffer->Reset(); pCopyCommand = CREATE_COMMAND(m_pCommandBufferManager, CopyBufferToImage); pCopyCommand->AddDependency((IRenderingObject*)pBuffer, DEPENDENCY_MODE_BUFFER_SOURCE); pCopyCommand->AddDependency((IRenderingObject*)pImage, DEPENDENCY_MODE_IMAGE_DESTINATION); pCopyCommand->pBuffer = pBuffer; pCopyCommand->stOutputImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; pCopyCommand->stOutputImage.m_pSingle = pImage; pCopyCommand->iImageX = uWidth; pCopyCommand->iImageY = uWidth; pCommandBuffer->AddCommand(pCopyCommand); pCommandBuffer->Render(); pCommandBuffer->Submit(); return i; } uint32_t CVkTextureArray::GetTextureID( const char *szPath ) { } void CVkTextureArray::UnloadTexture( uint32_t uTextureID ) { } void CVkTextureArray::Frame() { } 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 *CreateTexture( 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 IRayTracingShader *CreateRayShader( const char *szName ) override; virtual IComputeShader *CreateComputeShader( const char *szName ) override; virtual void DestroyShader( IBaseShader *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; virtual ITextureArray *CreateTextureArray() override; virtual void DestroyTextureArray() override; private: VkPhysicalDevice SelectPhysicalDevice( CUtlVector physicalDevices ); CUtlVector GetDeviceExtensions(); VkCommandBuffer GetCommandBuffer(); VulkanWindow_t CreateSwapchain( IGameWindow *pWindow ); void DestroySwapchain( uint32_t uIndex ); IGameWindowManager *m_pWindowManager; IVkCommandBufferManager *m_pCommandBufferManager; IVkCommandBufferManager *m_pAssetsCommandBufferManager; CUtlVector m_renderWindows; CUtlVector m_pMaterials; CUtlVector m_pTextureArrays; CUtlVector m_scheduledRemovalLists; CUtlVector m_scheduledRemovalTextureArrays; CUtlVector m_scheduledRemovalBuffers; CUtlVector m_scheduledRemovalImages; }; 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::CreateTexture( uint32_t x, uint32_t y, EImageFormat eFormat, EMultisampleType eMultisampleType ) { CVkImage *pImage = new CVkImage(x, y, 1, eFormat, eMultisampleType, IMAGE_TYPE_2D, VK_IMAGE_USAGE_SAMPLED_BIT); pImage->m_ePreferredLayout = VK_IMAGE_LAYOUT_GENERAL; return pImage; } 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 ) { m_scheduledRemovalBuffers.AppendTail((CVkBuffer*)pBuffer); } void CVkRenderContext::DestroyImage( IImage *pImage ) { } IShader *CVkRenderContext::CreateShader( const char *szName ) { CVkShader *pShader = new CVkShader(); VkGraphicsPipelineCreateInfo stPipelineCreateInfo = {}; ICompiledShaderManager *pCompiledShaderManager = (ICompiledShaderManager*)CreateInterface(COMPILED_SHADER_MANAGER_INTERFACE_VERSION, NULL); pCompiledShaderManager->ReadFromFile(&pShader->m_shader, szName); pShader->m_hDevice = s_vkDevice; return pShader; } IRayTracingShader *CVkRenderContext::CreateRayShader( const char *szName ) { CVkRayTracingShader *pShader = new CVkRayTracingShader(); VkGraphicsPipelineCreateInfo stPipelineCreateInfo = {}; ICompiledShaderManager *pCompiledShaderManager = (ICompiledShaderManager*)CreateInterface(COMPILED_SHADER_MANAGER_INTERFACE_VERSION, NULL); pCompiledShaderManager->ReadFromFile(&pShader->m_shader, szName); pShader->m_hDevice = s_vkDevice; return pShader; } IComputeShader *CVkRenderContext::CreateComputeShader( const char *szName ) { } void CVkRenderContext::DestroyShader( IBaseShader *pShader ) { delete pShader; } IMaterial *CVkRenderContext::CreateMaterial( IShader *pShader ) { CVkMaterial *pMaterial = new CVkMaterial(pShader); m_pMaterials.AppendTail(pMaterial); return pMaterial; } void CVkRenderContext::DestroyMaterial( IMaterial *pMaterial ) { delete pMaterial; } IRenderCommandList *CVkRenderContext::CreateCommandList() { CVkRenderCommandList *pList = new CVkRenderCommandList; pList->m_pCommandBufferManager = m_pCommandBufferManager; return pList; } void CVkRenderContext::DestroyCommandList( IRenderCommandList *pCommandList ) { m_scheduledRemovalLists.AppendTail((CVkRenderCommandList*)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 ) { VulkanWindow_t window = CreateSwapchain(pWindow); m_renderWindows.AppendTail(window); } void CVkRenderContext::UnregisterGameWindow( IGameWindow *pWindow ) { } ITextureArray *CVkRenderContext::CreateTextureArray() { CVkTextureArray *pArray = new CVkTextureArray(); pArray->m_hDevice = s_vkDevice; pArray->m_pRenderContext = this; pArray->m_pCommandBufferManager = m_pCommandBufferManager; pArray->Build(); return pArray; } void CVkRenderContext::DestroyTextureArray() { } 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, &s_vkInstance); VULKAN_RESULT_PRINT(r, vkCreateInstance); // volk requires to load instance this way volkLoadInstance(s_vkInstance); // Get amount of physical devices r = vkEnumeratePhysicalDevices(s_vkInstance, &nPhysicalDevicesCount, NULL); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); physicalDevices.Resize(nPhysicalDevicesCount); // Read all physical devices r = vkEnumeratePhysicalDevices(s_vkInstance, &nPhysicalDevicesCount, physicalDevices.GetData()); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); s_vkPhysicalDevice = SelectPhysicalDevice(physicalDevices); enabledDeviceExtensions = GetDeviceExtensions(); // Get all queues vkGetPhysicalDeviceQueueFamilyProperties(s_vkPhysicalDevice, &nNumQueueFamilies, NULL); queueFamilyProperties.Resize(nNumQueueFamilies); uint32_t i = 0; vkGetPhysicalDeviceQueueFamilyProperties(s_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; vk12Features.runtimeDescriptorArray = VK_TRUE; vk12Features.descriptorBindingPartiallyBound = VK_TRUE; vk12Features.descriptorBindingVariableDescriptorCount = VK_TRUE; VkPhysicalDeviceVulkan11Features vk11Features = {}; vk11Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; vk11Features.pNext = &vk12Features; vk11Features.shaderDrawParameters = 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 = &vk11Features; r = vkCreateDevice(s_vkPhysicalDevice, &stDeviceCreateInfo, NULL, &s_vkDevice); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); for (auto &extension: enabledDeviceExtensions) V_printf("%s\n", extension); vkGetDeviceQueue(s_vkDevice, g_iDrawFamily, 0, &s_vkDrawQueue); vkGetDeviceQueue(s_vkDevice, g_iPresentFamily, 0, &s_vkPresentQueue); volkLoadDevice(s_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 = s_vkPhysicalDevice; stAllocatorInfo.device = s_vkDevice; stAllocatorInfo.instance = s_vkInstance; VmaVulkanFunctions vulkanFunctions; vmaImportVulkanFunctionsFromVolk(&stAllocatorInfo, &vulkanFunctions); stAllocatorInfo.pVulkanFunctions = &vulkanFunctions; vmaCreateAllocator(&stAllocatorInfo, &s_vkAllocator); VkPipelineLayoutCreateInfo stPipelineLayout = {}; stPipelineLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; stPipelineLayout.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT; vkCreatePipelineLayout(s_vkDevice, &stPipelineLayout, NULL, &g_pLibraryEmptyLayout); m_pCommandBufferManager = (IVkCommandBufferManager*)CreateInterface(VULKAN_COMMAND_BUFFER_MANAGER_INTERFACE_NAME, NULL); m_pCommandBufferManager->SetVulkanHandlers(s_vkInstance, s_vkDevice); m_pCommandBufferManager->Init(); } void CVkRenderContext::Frame( float fDeltaTime ) { uint32_t i; CUtlVector fences = {}; CUtlVector imageReady = {}; CUtlVector renderFinish = {}; CUtlVector swapchains = {}; CUtlVector uImageIndexes = {}; CUtlVector uSwapchainImageIndexes = {}; CUtlVector recreatedWindows = {}; for ( auto &m: m_pMaterials) { m->Frame(); } i = 0; for ( auto &s: m_renderWindows) { if ( !s.m_pWindow->BRenderSizeUpdated() ) { i++; continue; } DestroySwapchain(i); m_renderWindows.RemoveAt(i); recreatedWindows.AppendTail(CreateSwapchain(s.m_pWindow)); } for ( auto &s: recreatedWindows) { m_renderWindows.AppendTail(s); } for ( auto &s: m_renderWindows) { fences.AppendTail(s.m_fences[s.m_uCurrentFrame]); imageReady.AppendTail(s.m_imageAvailable[s.m_uCurrentFrame]); renderFinish.AppendTail(s.m_renderFinished[s.m_uCurrentFrame]); swapchains.AppendTail(s.m_swapchain); uImageIndexes.AppendTail(s.m_uCurrentFrame); } uSwapchainImageIndexes.Resize(m_renderWindows.GetSize()); vkWaitForFences(s_vkDevice, fences.GetSize(), fences.GetData(), VK_TRUE, UINT64_MAX); vkResetFences(s_vkDevice, fences.GetSize(), fences.GetData()); i = 0; for ( auto &s: m_renderWindows ) { VkResult r = vkAcquireNextImageKHR(s_vkDevice, s.m_swapchain, UINT64_MAX, s.m_imageAvailable[s.m_uCurrentFrame], NULL, &uSwapchainImageIndexes[i]); i++; } s_pPresentCommandBuffer = m_pCommandBufferManager->CreateCommandBuffer(); s_pPresentCommandBuffer->Reset(); double a = Plat_GetTime(); i = 0; for ( auto &s: m_renderWindows ) { CVkBlitCommand *pBlitCommand = NULL; if (s.m_pWindow->GetOutputImage()) { pBlitCommand = CREATE_COMMAND(m_pCommandBufferManager, Blit); pBlitCommand->AddDependency(s.m_pWindow->GetOutputImage(), DEPENDENCY_MODE_BLIT_IMAGE_SOURCE); pBlitCommand->AddDependency((IRenderingObject*)s.m_images[uSwapchainImageIndexes[i]], DEPENDENCY_MODE_BLIT_IMAGE_DESTINATION); pBlitCommand->stInputImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; pBlitCommand->stInputImage.m_pSingle = s.m_pWindow->GetOutputImage(); pBlitCommand->stOutputImage.m_eObjectType = FRAME_OBJECT_TYPE_SINGLE; pBlitCommand->stOutputImage.m_pSingle = (IRenderingObject*)s.m_images[uSwapchainImageIndexes[i]]; pBlitCommand->iSrcMax[0] = s.m_pWindow->GetOutputImage()->GetImageWidth(); pBlitCommand->iSrcMax[1] = s.m_pWindow->GetOutputImage()->GetImageHeight(); pBlitCommand->iSrcMax[2] = 1; pBlitCommand->iDstMax[0] = s.m_pWindow->GetRenderWidth(); pBlitCommand->iDstMax[1] = s.m_pWindow->GetRenderHeight(); pBlitCommand->iDstMax[2] = 1; } if (pBlitCommand != NULL) s_pPresentCommandBuffer->AddCommand(pBlitCommand); i++; } s_pPresentCommandBuffer->Render(); s_pPresentCommandBuffer->Submit(0); VkPipelineStageFlags uPipelineStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo stSubmitInfo = {}; stSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; stSubmitInfo.waitSemaphoreCount = imageReady.GetSize(); stSubmitInfo.pWaitSemaphores = imageReady.GetData(); stSubmitInfo.pWaitDstStageMask = &uPipelineStageFlags; stSubmitInfo.commandBufferCount = m_pCommandBufferManager->GetVulkanCommands().GetSize(); stSubmitInfo.pCommandBuffers = m_pCommandBufferManager->GetVulkanCommands().GetData(); stSubmitInfo.signalSemaphoreCount = renderFinish.GetSize(); stSubmitInfo.pSignalSemaphores = renderFinish.GetData(); vkQueueSubmit(s_vkDrawQueue, 1, &stSubmitInfo, fences[0]); VkPresentInfoKHR stPresentInfo = {}; stPresentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; stPresentInfo.waitSemaphoreCount = renderFinish.GetSize(); stPresentInfo.pWaitSemaphores = renderFinish.GetData(); stPresentInfo.swapchainCount = swapchains.GetSize(); stPresentInfo.pSwapchains = swapchains.GetData(); stPresentInfo.pImageIndices = uImageIndexes.GetData(); vkQueuePresentKHR(s_vkPresentQueue, &stPresentInfo); vkDeviceWaitIdle(s_vkDevice); for ( auto &s: m_renderWindows ) { s.m_uCurrentFrame = (s.m_uCurrentFrame + 1) % FRAMES_IN_FLIGHT; } m_pCommandBufferManager->RenderingFinished(); for (auto &a: m_scheduledRemovalBuffers) { delete a; } for (auto &a: m_scheduledRemovalLists) { delete a; } m_scheduledRemovalLists = {}; m_scheduledRemovalBuffers = {}; m_pCommandBufferManager->FreeCommandBufferWithCommands(s_pPresentCommandBuffer); } VulkanWindow_t 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; VulkanWindow_t window = {}; window.m_surface = (VkSurfaceKHR)pWindow->CreateVulkanSurface(s_vkInstance); vkGetPhysicalDeviceSurfaceCapabilitiesKHR(s_vkPhysicalDevice, (VkSurfaceKHR)window.m_surface, &surfaceCapatibilities); vkGetPhysicalDeviceSurfaceFormatsKHR(s_vkPhysicalDevice, (VkSurfaceKHR)window.m_surface, &numSurfaceFormats, NULL); surfaceFormats.Resize(numSurfaceFormats); vkGetPhysicalDeviceSurfaceFormatsKHR(s_vkPhysicalDevice, (VkSurfaceKHR)window.m_surface, &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(s_vkPhysicalDevice, window.m_surface, &nSurfacePresentModes, NULL); surfacePresentModes.Resize(nSurfacePresentModes); vkGetPhysicalDeviceSurfacePresentModesKHR(s_vkPhysicalDevice, window.m_surface, &nSurfacePresentModes, surfacePresentModes.GetData()); stSwapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; stSwapchainCreateInfo.surface = window.m_surface; 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 = {pWindow->GetRenderWidth(), pWindow->GetRenderHeight()}; if (stSwapchainCreateInfo.imageExtent.width == 0) stSwapchainCreateInfo.imageExtent.width = 1; if (stSwapchainCreateInfo.imageExtent.height == 0) stSwapchainCreateInfo.imageExtent.height = 1; stSwapchainCreateInfo.minImageCount = surfaceCapatibilities.minImageCount; vkCreateSwapchainKHR(s_vkDevice, &stSwapchainCreateInfo, NULL, &window.m_swapchain); window.m_eFormat = stSwapchainCreateInfo.imageFormat; vkGetSwapchainImagesKHR(s_vkDevice, window.m_swapchain, &nSwapchainImages, NULL); window.m_images.Resize(nSwapchainImages); swapchainImages.Resize(nSwapchainImages); vkGetSwapchainImagesKHR(s_vkDevice, window.m_swapchain, &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_BGRA8_UNORM; pImage->m_nHeight = 1280; pImage->m_nWidth = 720; pImage->m_ePreferredLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; pImage->CreateImageView(); window.m_images[i] = pImage; } for ( int i = 0; i < FRAMES_IN_FLIGHT; i++ ) { stFenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; stFenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; vkCreateFence(s_vkDevice, &stFenceCreateInfo, NULL, &window.m_fences[i]); stSemaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; vkCreateSemaphore(s_vkDevice, &stSemaphoreCreateInfo, NULL, &window.m_imageAvailable[i]); vkCreateSemaphore(s_vkDevice, &stSemaphoreCreateInfo, NULL, &window.m_renderFinished[i]); } window.m_pWindow = pWindow; return window; } void CVkRenderContext::DestroySwapchain( uint32_t uIndex ) { for ( auto i: m_renderWindows[uIndex].m_images ) { CVkImage *pImage = (CVkImage*)i; vkDestroyImageView(s_vkDevice, pImage->m_imageView, NULL); } vkDestroySwapchainKHR(s_vkDevice, m_renderWindows[uIndex].m_swapchain, NULL); m_renderWindows[uIndex].m_pWindow->DestroyVulkanSurface(s_vkInstance); for ( int i = 0; i < FRAMES_IN_FLIGHT; i++ ) { vkDestroyFence(s_vkDevice, m_renderWindows[uIndex].m_fences[i], NULL); vkDestroySemaphore(s_vkDevice, m_renderWindows[uIndex].m_imageAvailable[i], NULL); vkDestroySemaphore(s_vkDevice, m_renderWindows[uIndex].m_renderFinished[i], NULL); } } //----------------------------------------------------------------------------- // TODO: Move to the rendering context //----------------------------------------------------------------------------- void CVkRenderContext::Shutdown() { vkDestroyInstance(s_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(s_vkPhysicalDevice, NULL, &nExtensionCount, NULL); VULKAN_RESULT_PRINT(r, vkEnumeratePhysicalDevices); extensions.Resize(nExtensionCount); r = vkEnumerateDeviceExtensionProperties(s_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; }