从2014年开始,DX12、Metal、Vulkan就陆续作为新一代的 API 走上 GPU的大舞台上,虽然Vulkan在推广过程比Metal(毕竟是苹果自家API)慢,但是从华为P30、小米9系列开始,Vulkan的地位已经慢慢超过OpenGL ES,所以,我跳过了OpenGL ES 3.0、3.1、3.2 spec的翻译,直接进入Vulkan时代


Spec是枯燥的,读spec前看几篇文章:https://www.zhihu.com/question/424430509/answer/1632072443、https://glumes.com/tags/vulkan/

PFN_vkVoidFunction vkGetInstanceProcAddr( VkInstance instance, const char* pName);

在CTS,该函数是用于获取 vkGetPhysicalDeviceProperties2KHR 等函数的函数handle,比如:PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR = reinterpret_cast<.PFN_vkGetPhysicalDeviceProperties2KHR>(vkGetInstanceProcAddr(instance->get_handle(), "vkGetPhysicalDeviceProperties2KHR"));

PFN_vkVoidFunction vkGetDeviceProcAddr( VkDevice device, const char* pName);

在CTS,该函数是用于获取 vkCmdPushDescriptorSetKHR 等函数的函数handle,比如:vkCmdPushDescriptorSetKHR = (PFN_vkCmdPushDescriptorSetKHR) vkGetDeviceProcAddr(get_device().get_handle(), "vkCmdPushDescriptorSetKHR");

VkResult vkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);

Vulkan中没有global state,每个应用程序的所有状态都存储在VkInstance中

第一个输入参数为创建instance所用到到的信息。

typedef struct VkInstanceCreateInfo { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO const void* pNext; //NULL,或者扩展该结构体的另外一个结构体,必须是NULL VkInstanceCreateFlags flags; //reserved for future,必须是0 const VkApplicationInfo* pApplicationInfo; //NULL,或者一个包含应用程序信息的结构体,用于告诉instance应用程序信息。如果使用NULL,或者使用一个0版本的apiVersion,则相当于使用了VK_MAKE_API_VERSION(0,1,0,0).的apiVersion uint32_t enabledLayerCount; //启用的global layer的数量 const char* const* ppEnabledLayerNames; //启用的global layer的名字,第一个是最靠近应用程序的layer,最后一个是最靠近驱动的layer uint32_t enabledExtensionCount; //启用的global extension的数量 const char* const* ppEnabledExtensionNames; //启用的extension的名字,这里包含了所有的extension } VkInstanceCreateInfo;

typedef struct VkApplicationInfo { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_APPLICATION_INFO const void* pNext; //NULL,或者扩展该结构体的另外一个结构体,必须是NULL const char* pApplicationName; //NULL,或者应用程序名字 uint32_t applicationVersion; //应用程序版本 const char* pEngineName; //NULL,或者创建该应用程序的引擎名字 uint32_t engineVersion; //引擎版本 uint32_t apiVersion; //应用程序所支持的Vulkan的最高版本。当使用vulkan1.0的时候,这里如果写超过1.0的数字,会出现 VK_ERROR_INCOMPATIBLE_DRIVER 的错误,因此,在创建之前,需要通过 vkGetInstanceProcAddr 获取 vkEnumerateInstanceVersion 的信息,如果 vkEnumerateInstanceVersion 为 NULL,则 为vulkan 1.0,否则 vkEnumerateInstanceVersion 即为 Vulkan版本。而vulkan1.1及以上版本永远不会出现这个错误。需要注意的是:validation layer会根据这个版本,来判断,如果使用了高版本的特性,则会报validation error。比如,如果instance支持1.1,3个physical devices支持1.0、1.1、1.2,如果应用程序将该值设置为1.2。则应用程序中,instance和所有的physical devices都可以使用1.0,instance和1.1和1.2的physical devices可以使用1.1,1.2的physical device可以使用1.2。如果应用程序设置为1.1,则应用程序即使在支持1.2的physical deevice上,也无法使用1.2。这个版本影响了layer的使用。除非是0,否则必须大于等于VK_API_VERSION_1_0 } VkApplicationInfo;

第二个输入参数,用于内存分配。

typedef struct VkAllocationCallbacks { void* pUserData; PFN_vkAllocationFunction pfnAllocation; PFN_vkReallocationFunction pfnReallocation; PFN_vkFreeFunction pfnFree; PFN_vkInternalAllocationNotification pfnInternalAllocation; PFN_vkInternalFreeNotification pfnInternalFree; } VkAllocationCallbacks;

第三个输入参数,用于获取生成的instance

vkCreateInstance 会验证 request layer是否存在,如果不存在,则返回 VK_ERROR_LAYER_NOT_PRESENT

vkCreateInstance 会验证 request extension是否支持(在当前instance或者所有开启的instance),如果有extension不支持,则返回 VK_ERROR_EXTENSION_NOT_PRESENT。

如果有layer和extension必须同时存在,则在这里就需要同时存在。

可能会出现的错误:VK_ERROR_OUT_OF_HOST_MEMORY、VK_ERROR_OUT_OF_DEVICE_MEMORY、VK_ERROR_INITIALIZATION_FAILED、VK_ERROR_LAYER_NOT_PRESENT、VK_ERROR_EXTENSION_NOT_PRESENT、VK_ERROR_INCOMPATIBLE_DRIVER

在CTS中的使用:result = vkCreateInstance(&instance_info, nullptr, &handle);

在UE中的使用:VkResult Result = VulkanRHI::vkCreateInstance(&InstInfo, VULKAN_CPU_ALLOCATOR, &Instance);//FVulkanDynamicRHI::CreateInstance()

void vkDestroyInstance( VkInstance instance, const VkAllocationCallbacks* pAllocator);

与vkCreateInstance对应,删除instance

删除instance之前,需要删除所有通过instance创建的物件

如果vkCreateInstance的时候使用 VkAllocationCallbacks 了,那么删除的时候也需要提供一个与之兼容的 VkAllocationCallbacks

如果vkCreateInstance的时候没有使用 VkAllocationCallbacks ,那么删除的时候 VkAllocationCallbacks 也必须是NULL

第一个输入参数 可以是NULL,或者一个合法的 VkInstance

第二个输入参数如果不是NULL,则必须是一个合法的 VkAllocationCallbacks 结构体

typedef struct VkAllocationCallbacks { void* pUserData; PFN_vkAllocationFunction pfnAllocation; PFN_vkReallocationFunction pfnReallocation; PFN_vkFreeFunction pfnFree; PFN_vkInternalAllocationNotification pfnInternalAllocation; PFN_vkInternalFreeNotification pfnInternalFree; } VkAllocationCallbacks;

在CTS中的使用:vkDestroyInstance(handle, nullptr);

在UE中的使用:VulkanRHI::vkDestroyInstance(Instance, VULKAN_CPU_ALLOCATOR);//FVulkanDynamicRHI::Shutdown()

		Host access to instance must be externally synchronized
		Host access to all VkPhysicalDevice objects enumerated from instance must be externally synchronized
	
VkResult vkEnumeratePhysicalDevices( VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices);

该API用于获取物理设备的信息(比如多个GPU的情况)

第一个输入参数为使用 vkCreateInstance 创建的 instance 。

第二个输入参数用于获取 avaliable或者queried的physical devices 的数量。

第三个输入参数可以是NULL,或者一个指向 VkPhysicalDevice handle 数组的指针。

如果 pPhysicalDevices 为NULL,则可用的 physical devices 的数量通过 pPhysicalDeviceCount 返回。否则, pPhysicalDeviceCount 必须和 pPhysicalDevices 的元素数量匹配,执行完毕后, pPhysicalDeviceCount 将被赋值为真实获取到的 pPhysicalDevices的元素数量。假如 pPhysicalDeviceCount 小于可用的 physical devices 数量,那么最多可以获取到 pPhysicalDeviceCount 个physical devices,并且返回 VK_INCOMPLETE (而非 VK_SUCCESS,但是也算是成功了),说明并非所有可用的physical devices都被获取到了。

可能会出现的错误:VK_ERROR_OUT_OF_HOST_MEMORY、VK_ERROR_OUT_OF_DEVICE_MEMORY、VK_ERROR_INITIALIZATION_FAILED

在CTS中的使用:vkEnumeratePhysicalDevices(context.instance, &gpu_count, nullptr)获取physical devices的数量,vkEnumeratePhysicalDevices(context.instance, &gpu_count, gpus.data())获取physical devices的list

在UE中的使用:VkResult Result = VulkanRHI::vkEnumeratePhysicalDevices(Instance, &GpuCount, nullptr);VERIFYVULKANRESULT_EXPANDED(VulkanRHI::vkEnumeratePhysicalDevices(Instance, &GpuCount, PhysicalDevices.GetData()));//FVulkanDynamicRHI::SelectAndInitDevice()

void vkGetPhysicalDeviceProperties( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);

用于获取通过 vkEnumeratePhysicalDevices 得到的physical device的属性

第一个输入参数为被查询的 physicalDevice。

第二个输入参数为一个指向 VkPhysicalDeviceProperties 的指针,用于获取被查询出来的数据

typedef struct VkPhysicalDeviceProperties { uint32_t apiVersion; //当前device支持的Vulkan版本,需要注意的是,这里的apiVersion可能会和 vkEnumerateInstanceVersion 获取到的不同,可能高或者低。这种情况,应用程序不能使用超过given object相关的vulkan 版本的特性。vkEnumerateInstanceVersion 返回的 pApiVersion ,为instance及其children相关的版本,而非 VkPhysicalDevice 以及其 children。 VkPhysicalDeviceProperties::apiVersion 为 VkPhysicalDevice 及其 children 相关的版本。 uint32_t driverVersion; //vendor公司设定的驱动版本,driverVersion 的编码格式为 implementation-defined,可能与apiVersion的编码格式不同。应用程序需要根据vendor提供的信息,来从driverVersion 获取版本信息 uint32_t vendorID; //当前physical device对应的vendor公司的标识 uint32_t deviceID; //当前physical device的标识符 VkPhysicalDeviceType deviceType; //当前device的类型 char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; //一个包含 VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 元素的数组,包含了physical device的name uint8_t pipelineCacheUUID[VK_UUID_SIZE]; //一个包含 VK_UUID_SIZE 元素的数组,表示当前设备中的一个唯一标识符 VkPhysicalDeviceLimits limits; //当前physical device的限制 VkPhysicalDeviceSparseProperties sparseProperties; //当前physical device的various sparse 相关属性 } VkPhysicalDeviceProperties;

typedef enum VkPhysicalDeviceType { VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, //当前device不符合任何avaliable类型 VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, //当前device为嵌入式或者与主机密切耦合的设备 VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, //当前device为通过interlink与主机关联的独立处理器 VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, //当前device为虚拟机 VK_PHYSICAL_DEVICE_TYPE_CPU = 4, //当前device运行在与主机相同的处理器上 } VkPhysicalDeviceType;

physical device type不会影响操作。然而可能会与系统的其他公开属性或者功能相关,比如有多少内存。

typedef struct VkPhysicalDeviceLimits { uint32_t maxImageDimension1D; uint32_t maxImageDimension2D; uint32_t maxImageDimension3D; uint32_t maxImageDimensionCube; uint32_t maxImageArrayLayers; uint32_t maxTexelBufferElements; uint32_t maxUniformBufferRange; uint32_t maxStorageBufferRange; uint32_t maxPushConstantsSize; uint32_t maxMemoryAllocationCount; uint32_t maxSamplerAllocationCount; VkDeviceSize bufferImageGranularity; VkDeviceSize sparseAddressSpaceSize; uint32_t maxBoundDescriptorSets; uint32_t maxPerStageDescriptorSamplers; uint32_t maxPerStageDescriptorUniformBuffers; uint32_t maxPerStageDescriptorStorageBuffers; uint32_t maxPerStageDescriptorSampledImages; uint32_t maxPerStageDescriptorStorageImages; uint32_t maxPerStageDescriptorInputAttachments; uint32_t maxPerStageResources; uint32_t maxDescriptorSetSamplers; uint32_t maxDescriptorSetUniformBuffers; uint32_t maxDescriptorSetUniformBuffersDynamic; uint32_t maxDescriptorSetStorageBuffers; uint32_t maxDescriptorSetStorageBuffersDynamic; uint32_t maxDescriptorSetSampledImages; uint32_t maxDescriptorSetStorageImages; uint32_t maxDescriptorSetInputAttachments; uint32_t maxVertexInputAttributes; uint32_t maxVertexInputBindings; uint32_t maxVertexInputAttributeOffset; uint32_t maxVertexInputBindingStride; uint32_t maxVertexOutputComponents; uint32_t maxTessellationGenerationLevel; uint32_t maxTessellationPatchSize; uint32_t maxTessellationControlPerVertexInputComponents; uint32_t maxTessellationControlPerVertexOutputComponents; uint32_t maxTessellationControlPerPatchOutputComponents; uint32_t maxTessellationControlTotalOutputComponents; uint32_t maxTessellationEvaluationInputComponents; uint32_t maxTessellationEvaluationOutputComponents; uint32_t maxGeometryShaderInvocations; uint32_t maxGeometryInputComponents; uint32_t maxGeometryOutputComponents; uint32_t maxGeometryOutputVertices; uint32_t maxGeometryTotalOutputComponents; uint32_t maxFragmentInputComponents; uint32_t maxFragmentOutputAttachments; uint32_t maxFragmentDualSrcAttachments; uint32_t maxFragmentCombinedOutputResources; uint32_t maxComputeSharedMemorySize; uint32_t maxComputeWorkGroupCount[3]; uint32_t maxComputeWorkGroupInvocations; uint32_t maxComputeWorkGroupSize[3]; uint32_t subPixelPrecisionBits; uint32_t subTexelPrecisionBits; uint32_t mipmapPrecisionBits; uint32_t maxDrawIndexedIndexValue; uint32_t maxDrawIndirectCount; float maxSamplerLodBias; float maxSamplerAnisotropy; uint32_t maxViewports; uint32_t maxViewportDimensions[2]; float viewportBoundsRange[2]; uint32_t viewportSubPixelBits; size_t minMemoryMapAlignment; VkDeviceSize minTexelBufferOffsetAlignment; VkDeviceSize minUniformBufferOffsetAlignment; VkDeviceSize minStorageBufferOffsetAlignment; int32_t minTexelOffset; uint32_t maxTexelOffset; int32_t minTexelGatherOffset; uint32_t maxTexelGatherOffset; float minInterpolationOffset; float maxInterpolationOffset; uint32_t subPixelInterpolationOffsetBits; uint32_t maxFramebufferWidth; uint32_t maxFramebufferHeight; uint32_t maxFramebufferLayers; VkSampleCountFlags framebufferColorSampleCounts; VkSampleCountFlags framebufferDepthSampleCounts; VkSampleCountFlags framebufferStencilSampleCounts; VkSampleCountFlags framebufferNoAttachmentsSampleCounts; uint32_t maxColorAttachments; VkSampleCountFlags sampledImageColorSampleCounts; VkSampleCountFlags sampledImageIntegerSampleCounts; VkSampleCountFlags sampledImageDepthSampleCounts; VkSampleCountFlags sampledImageStencilSampleCounts; VkSampleCountFlags storageImageSampleCounts; uint32_t maxSampleMaskWords; VkBool32 timestampComputeAndGraphics; float timestampPeriod; uint32_t maxClipDistances; uint32_t maxCullDistances; uint32_t maxCombinedClipAndCullDistances; uint32_t discreteQueuePriorities; float pointSizeRange[2]; float lineWidthRange[2]; float pointSizeGranularity; float lineWidthGranularity; VkBool32 strictLines; VkBool32 standardSampleLocations; VkDeviceSize optimalBufferCopyOffsetAlignment; VkDeviceSize optimalBufferCopyRowPitchAlignment; VkDeviceSize nonCoherentAtomSize; } VkPhysicalDeviceLimits;

typedef struct VkPhysicalDeviceSparseProperties { VkBool32 residencyStandard2DBlockShape; VkBool32 residencyStandard2DMultisampleBlockShape; VkBool32 residencyStandard3DBlockShape; VkBool32 residencyAlignedMipSize; VkBool32 residencyNonResidentStrict; } VkPhysicalDeviceSparseProperties;

typedef enum VkVendorId { //vendorID 的枚举值,这个枚举随时可能更新,只有vk.xml和vulkan_core.h才包含了所有reserved Khronos vendor IDs。只有在Khronos注册的vendor IDs才会返回名字,根据 implementation 返回的PCI vendor IDs可以通过PCI-SIG database 进行查询。 VK_VENDOR_ID_VIV = 0x10001, VK_VENDOR_ID_VSI = 0x10002, VK_VENDOR_ID_KAZAN = 0x10003, VK_VENDOR_ID_CODEPLAY = 0x10004, VK_VENDOR_ID_MESA = 0x10005, VK_VENDOR_ID_POCL = 0x10006, } VkVendorId;

在CTS中的使用:vkGetPhysicalDeviceProperties(physical_device, &properties);

在UE中的使用:VulkanRHI::vkGetPhysicalDeviceProperties(Gpu, &GpuProps);//FVulkanDevice::FVulkanDevice(..)FVulkanDevice::QueryGPU(..)

void vkGetPhysicalDeviceProperties2( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);

用于获取通过 vkEnumeratePhysicalDevices 得到的physical device的属性

第一个输入参数为被查询的 physicalDevice。

第二个输入参数为一个指向 VkPhysicalDeviceProperties2 的指针,用于获取被查询出来的数据

typedef struct VkPhysicalDeviceProperties2 { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 void* pNext; //NULL,或者扩展该结构体的另外一个结构体(包含extension定义的属性),结构体的类型是:VkPhysicalDeviceDepthStencilResolveProperties, VkPhysicalDeviceDescriptorIndexingProperties, VkPhysicalDeviceDriverProperties, VkPhysicalDeviceFloatControlsProperties, VkPhysicalDeviceIDProperties, VkPhysicalDeviceMaintenance3Properties, VkPhysicalDeviceMultiviewProperties, VkPhysicalDevicePointClippingProperties, VkPhysicalDeviceProtectedMemoryProperties, VkPhysicalDeviceSamplerFilterMinmaxProperties, VkPhysicalDeviceSubgroupProperties, VkPhysicalDeviceTimelineSemaphoreProperties, VkPhysicalDeviceVulkan11Properties, or VkPhysicalDeviceVulkan12Properties。 pNext中的 sType 必须 unique VkPhysicalDeviceProperties properties; //一个指向 VkPhysicalDeviceProperties 的指针,用于获取被查询出来的数据,与 vkGetPhysicalDeviceProperties 相同。 } VkPhysicalDeviceProperties2;

typedef struct VkPhysicalDeviceVulkan11Properties { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES void* pNext; uint8_t deviceUUID[VK_UUID_SIZE]; uint8_t driverUUID[VK_UUID_SIZE]; uint8_t deviceLUID[VK_LUID_SIZE]; uint32_t deviceNodeMask; VkBool32 deviceLUIDValid; uint32_t subgroupSize; VkShaderStageFlags subgroupSupportedStages; VkSubgroupFeatureFlags subgroupSupportedOperations; VkBool32 subgroupQuadOperationsInAllStages; VkPointClippingBehavior pointClippingBehavior; uint32_t maxMultiviewViewCount; uint32_t maxMultiviewInstanceIndex; VkBool32 protectedNoFault; uint32_t maxPerSetDescriptors; VkDeviceSize maxMemoryAllocationSize; } VkPhysicalDeviceVulkan11Properties;

typedef struct VkPhysicalDeviceVulkan12Properties { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES void* pNext; VkDriverId driverID; char driverName[VK_MAX_DRIVER_NAME_SIZE]; char driverInfo[VK_MAX_DRIVER_INFO_SIZE]; VkConformanceVersion conformanceVersion; VkShaderFloatControlsIndependence denormBehaviorIndependence; VkShaderFloatControlsIndependence roundingModeIndependence; VkBool32 shaderSignedZeroInfNanPreserveFloat16; VkBool32 shaderSignedZeroInfNanPreserveFloat32; VkBool32 shaderSignedZeroInfNanPreserveFloat64; VkBool32 shaderDenormPreserveFloat16; VkBool32 shaderDenormPreserveFloat32; VkBool32 shaderDenormPreserveFloat64; VkBool32 shaderDenormFlushToZeroFloat16; VkBool32 shaderDenormFlushToZeroFloat32; VkBool32 shaderDenormFlushToZeroFloat64; VkBool32 shaderRoundingModeRTEFloat16; VkBool32 shaderRoundingModeRTEFloat32; VkBool32 shaderRoundingModeRTEFloat64; VkBool32 shaderRoundingModeRTZFloat16; VkBool32 shaderRoundingModeRTZFloat32; VkBool32 shaderRoundingModeRTZFloat64; uint32_t maxUpdateAfterBindDescriptorsInAllPools; VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; VkBool32 shaderSampledImageArrayNonUniformIndexingNative; VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; VkBool32 shaderStorageImageArrayNonUniformIndexingNative; VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; VkBool32 robustBufferAccessUpdateAfterBind; VkBool32 quadDivergentImplicitLod; uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; uint32_t maxPerStageUpdateAfterBindResources; uint32_t maxDescriptorSetUpdateAfterBindSamplers; uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; uint32_t maxDescriptorSetUpdateAfterBindSampledImages; uint32_t maxDescriptorSetUpdateAfterBindStorageImages; uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; VkResolveModeFlags supportedDepthResolveModes; VkResolveModeFlags supportedStencilResolveModes; VkBool32 independentResolveNone; VkBool32 independentResolve; VkBool32 filterMinmaxSingleComponentFormats; VkBool32 filterMinmaxImageComponentMapping; uint64_t maxTimelineSemaphoreValueDifference; VkSampleCountFlags framebufferIntegerColorSampleCounts; } VkPhysicalDeviceVulkan12Properties;

typedef struct VkPhysicalDeviceIDProperties { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES void* pNext; uint8_t deviceUUID[VK_UUID_SIZE]; uint8_t driverUUID[VK_UUID_SIZE]; uint8_t deviceLUID[VK_LUID_SIZE]; uint32_t deviceNodeMask; VkBool32 deviceLUIDValid; } VkPhysicalDeviceIDProperties;

typedef struct VkPhysicalDeviceDriverProperties { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES void* pNext; VkDriverId driverID; char driverName[VK_MAX_DRIVER_NAME_SIZE]; char driverInfo[VK_MAX_DRIVER_INFO_SIZE]; VkConformanceVersion conformanceVersion; //CTS的版本 } VkPhysicalDeviceDriverProperties;

typedef enum VkDriverId { //driverID 的枚举值,这个枚举随时可能更新,一个vendor可能有多个driverID,用于表示不同的driver。只有vk.xml和vulkan_core.h才包含了所有reserved Khronos driver IDs。只有在Khronos注册的driver IDs才会返回名字,还有一些没有注册的driver id。 VK_DRIVER_ID_AMD_PROPRIETARY = 1, VK_DRIVER_ID_AMD_OPEN_SOURCE = 2, VK_DRIVER_ID_MESA_RADV = 3, VK_DRIVER_ID_NVIDIA_PROPRIETARY = 4, VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS = 5, VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA = 6, VK_DRIVER_ID_IMAGINATION_PROPRIETARY = 7, VK_DRIVER_ID_QUALCOMM_PROPRIETARY = 8, VK_DRIVER_ID_ARM_PROPRIETARY = 9, VK_DRIVER_ID_GOOGLE_SWIFTSHADER = 10, VK_DRIVER_ID_GGP_PROPRIETARY = 11, VK_DRIVER_ID_BROADCOM_PROPRIETARY = 12, VK_DRIVER_ID_MESA_LLVMPIPE = 13, VK_DRIVER_ID_MOLTENVK = 14, VK_DRIVER_ID_COREAVI_PROPRIETARY = 15, } VkDriverId;

typedef struct VkConformanceVersion { uint8_t major; uint8_t minor; uint8_t subminor; uint8_t patch; } VkConformanceVersion;

在CTS中的使用: vkGetPhysicalDeviceProperties2(get_device().get_gpu().get_handle(), &device_properties);

void vkGetPhysicalDeviceQueueFamilyProperties( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);

用于获取通过 vkEnumeratePhysicalDevices 得到的physical device的 avaliable queues的属性

第一个输入参数为被查询的 physicalDevice。

第二个输入参数用于获取 avaliable或者queried的queue families 的数量。

第三个输入参数可以是NULL,或者一个指向 VkQueueFamilyProperties handle 数组的指针。

如果 pQueueFamilyProperties 为NULL,则可用的 queue families 的数量通过 pQueueFamilyPropertyCount 返回。Implementations 必须至少支持一个queue family。否则, pQueueFamilyPropertyCount 必须和 pQueueFamilyProperties 的元素数量匹配,执行完毕后, pQueueFamilyPropertyCount 将被赋值为真实获取到的 pQueueFamilyProperties 的元素数量。假如 pQueueFamilyPropertyCount 小于可用的 queue family 数量,那么最多可以获取到 pQueueFamilyPropertyCount 个queue family。

虽然我们期望一个physical device中所有相同能力的queue都存在一个family中。但是,尽管也应该这么实现,但依然有可能一个physical device中存在两个不同的queue family使用相同的能力。

typedef struct VkQueueFamilyProperties { VkQueueFlags queueFlags; //用于表示 VkQueueFlagBits , 是用于该queue family中queue的功能 uint32_t queueCount; //为该queue family中 queue的数量。每个 queue family 至少包含一个 queue uint32_t timestampValidBits; //是通过 vkCmdWriteTimestamp 这个API输入的 timestamps 中有意义的位。该数值的有效位是36-64位(其他为应该为0),或者为0(代表不支持timestamps) VkExtent3D minImageTransferGranularity; //是该 queue family的queue中支持的image transfer 操作中的最小粒度,针对压缩纹理其单位为压缩的纹理block,否则为纹素。可能的值为:(0,0,0):必须以整个mip level为单位进行transfer。这种情况下,有如下限制:1.VkOffset3D的参数x,y,z必须为0,2.VkExtent3D的参数width、height、depth必须匹配图片的w、h、d。或者(Ax, Ay, Az),其中三个值均为POT。这种情况下,有如下限制:1.VkOffset3D的参数x,y,z必须为(Ax, Ay, Az)的整数倍,2.VkExtent3D的参数width、height、depth必须是(Ax, Ay, Az)的整数倍,或者x+width、y+height、z+depth等于图片尺寸。3.如果图片为压缩格式,则需要通过压缩texel block尺寸来放大粒度。 } VkQueueFamilyProperties;

typedef enum VkQueueFlagBits { VK_QUEUE_GRAPHICS_BIT = 0x00000001, //说明这个queue支持graphics 操作 VK_QUEUE_COMPUTE_BIT = 0x00000002, //说明这个queue支持compute 操作 VK_QUEUE_TRANSFER_BIT = 0x00000004, //说明这个queue支持 transfer操作 VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, //说明这个queue支持 sparse memory management操作。如果开启了sparse resource feature,那么至少一个queue family支持这个bit // Provided by VK_VERSION_1_1 VK_QUEUE_PROTECTED_BIT = 0x00000010, //说明这个queue支持 VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT。如果physical device支持potectedMemory特性,则至少一个queue family支持这个bit } VkQueueFlagBits;

graphics和compute queue必须支持 minImageTransferGranularity 为(1,1,1),这样的话,在这些queue的image transfer 操作中没有任何限制。其他queue只需要支持整张mip level的transfer,这样的话 minImageTransferGranularity 为(0,0,0)

如果一个implementation 支持graphics操作的queue family,则至少一个physical device的一个queue family必须同时支持graphics和compute操作

如果支持protected memory physical device,则至少一个physical device的一个queue family必须同时支持graphics和compute和rotected memory操作

支持transfer操作的queue中的所有command,都被graphics/compute queue支持。所以,如果一个queue family支持 VK_QUEUE_GRAPHICS_BIT / VK_QUEUE_COMPUTE_BIT,就相当于也同时开启了 VK_QUEUE_TRANSFER_BIT

在CTS中的使用: vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_properties_count, queue_family_properties.data());

在UE中的使用:VulkanRHI::vkGetPhysicalDeviceQueueFamilyProperties(Gpu, &QueueCount, nullptr);VulkanRHI::vkGetPhysicalDeviceQueueFamilyProperties(Gpu, &QueueCount, QueueFamilyProps.GetData());//FVulkanDevice::QueryGPU(..)

void vkGetPhysicalDeviceQueueFamilyProperties2( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);

用于获取通过 vkEnumeratePhysicalDevices 得到的physical device的 avaliable queues的属性

第一个输入参数为被查询的 physicalDevice。

第二个输入参数用于获取 avaliable或者queried的queue families 的数量。和vkGetPhysicalDeviceQueueFamilyProperties 一样

第三个输入参数可以是NULL,或者一个指向 VkQueueFamilyProperties2 handle 数组的指针。

vkGetPhysicalDeviceQueueFamilyProperties2 和 vkGetPhysicalDeviceQueueFamilyProperties 类似,除了会在pNext中多返回一个extended 信息

typedef struct VkQueueFamilyProperties2 { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 void* pNext; //NULL,或者扩展该结构体的另外一个结构体,必须是NULL VkQueueFamilyProperties queueFamilyProperties; //一个指向 VkQueueFamilyProperties 的指针,用于获取被查询出来的数据,与 vkGetPhysicalDeviceQueueFamilyProperties 相同。 } VkQueueFamilyProperties2;

在CTS中的使用: 无

VkResult vkEnumeratePhysicalDeviceGroups( VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);

用于获取支持的device groups

Device与physical device关联。每个device都会有一些queue family,每个queue family包含一个或者多个queue。一个queue family中的多个queue支持相同的操作。

一个Vulkan应用程序会先查询所有的physical devices,然后逐一的查询它们的属性(包含queue和queue family属性),一旦找到一个合适的physical device,该应用程序会创建一个device。该device将会成为该physical device的主要接口。

如果若干个physical devices属于同一个的device group,则可以将一个device与它们同时关联。一个device group的意思是若干个physical devices,互相可以访问内存,以及录制一个single command buffer,可以执行在所有的physical devices上。Device group是通过 vkEnumeratePhysicalDeviceGroups 查询,对应的device是通过device group中physical device的子集,将其传入 VkDeviceGroupDeviceCreateInfo 来创建。同一个device group中的两个physical devices,必须支持相同的extension、feature和属性,通过 vkGetPhysicalDevice* 获取的属性也基本相同(允许因为连接了不同的display、compositor等导致一些特定的query不同,这里就不列出来了)。

第一个输入参数为使用 vkCreateInstance 创建的 instance 。

第二个输入参数用于获取 avaliable或者queried的 device group 的数量。

第三个输入参数可以是NULL,或者一个指向 VkPhysicalDeviceGroupProperties handle 数组的指针。

如果 pPhysicalDeviceGroupProperties 为NULL,则可用的 device group 的数量通过 pPhysicalDeviceGroupCount 返回。否则, pPhysicalDeviceGroupCount 必须和 pPhysicalDeviceGroupProperties 的元素数量匹配,执行完毕后, pPhysicalDeviceGroupCount 将被赋值为真实获取到的 pPhysicalDeviceGroupProperties 的元素数量。假如 pPhysicalDeviceGroupCount 小于可用的 device group 数量,那么最多可以获取到 pPhysicalDeviceGroupCount 个device group,并且返回 VK_INCOMPLETE (而非 VK_SUCCESS,但是也算是成功了),说明并非所有可用的 device group 都被获取到了。

每个physical device 都必须存在于一个device group中

可能会出现的错误:VK_ERROR_OUT_OF_HOST_MEMORY、VK_ERROR_OUT_OF_DEVICE_MEMORY、VK_ERROR_INITIALIZATION_FAILED

typedef struct VkPhysicalDeviceGroupProperties { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES void* pNext; //NULL,或者扩展该结构体的另外一个结构体,必须是NULL uint32_t physicalDeviceCount; //当前device group中physical devices的数量 VkPhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE]; //一个包含 VK_MAX_DEVICE_GROUP_SIZE 个 VkPhysicalDevice 元素的数组,其中包含了当前device group中所有的physical devices,所以,前 physicalDeviceCount 个元素是合法的。 VkBool32 subsetAllocation; //指定根据group创建的device,所分配的device memory的方式,通过 VkMemoryAllocateFlagsInfo 的 deviceMask 设置。如果为 VK_FALSE ,那么所有的device memory都可以被 group中的所有physical devices共享。如果 physicalDeviceCount 为 1,那么 subsetAllocation 必须是 VK_FALSE 。 } VkPhysicalDeviceGroupProperties;

在CTS中的使用: 无

VkResult vkCreateDevice( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);

用于创建关联physical device的logical device

第一个输入参数为通过 vkEnumeratePhysicalDevices 得到的其中一个physical device

第二个输入参数是创建device所需要的信息

第三个输入参数是用于内存分配,可以是NULL

第四个输入参数是为了得到创建出来的device的handle

vkCreateDevice 会确认 ppEnabledExtensionNames 和 pEnabledFeatures 是否支持。如果有extension不支持,则会返回 VK_ERROR_EXTENSION_NOT_PRESENT 的错误,如果有 feature不支持,则会返回 VK_ERROR_FEATURE_NOT_PRESENT 的错误。可以在创建device之前,通过 vkEnumerateDeviceExtensionProperties 查询支持的 extension,以及通过 vkGetPhysicalDeviceFeatures 查询支持的 feature。

验证了 extension和feaature后,device则被创建。

可以通过一个physical device创建多个logical devices。创建logical device可能会因为 device-specific 资源的缺失而失败。如果出现这种情况,则返回 VK_ERROR_TOO_MANY_OBJECTS 的错误。

可能会出现的错误:VK_ERROR_OUT_OF_HOST_MEMORY、VK_ERROR_OUT_OF_DEVICE_MEMORY、VK_ERROR_INITIALIZATION_FAILED、VK_ERROR_EXTENSION_NOT_PRESENT、VK_ERROR_FEATURE_NOT_PRESENT、VK_ERROR_TOO_MANY_OBJECTS、VK_ERROR_DEVICE_LOST

typedef struct VkDeviceCreateInfo { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO const void* pNext; //NULL,或者扩展该结构体的另外一个结构体,如果其中包含 VkPhysicalDeviceFeatures2 结构体,那么 pEnabledFeatures 必须是NULL VkDeviceCreateFlags flags; //reserved for future,必须为0 uint32_t queueCreateInfoCount; //为 pQueueCreateInfos 数组的尺寸,必须大于0 const VkDeviceQueueCreateInfo* pQueueCreateInfos; //一个指向 queueCreateInfoCount 个 VkDeviceQueueCreateInfo 数组的指针,创建logical device的时候同步创建这些queues uint32_t enabledLayerCount; //这个已经废弃 const char* const* ppEnabledLayerNames; //这个已经废弃 uint32_t enabledExtensionCount; //当前device开启的extension数量 const char* const* ppEnabledExtensionNames; //一个指向 enabledExtensionCount 个 extension name元素的数组,在创建device的时候这些extension需要被开启,这些extension所依赖的extension也必须放在这个list中 const VkPhysicalDeviceFeatures* pEnabledFeatures; //NULL,或者一个指针指向 VkPhysicalDeviceFeatures 结构体,其中用bool值指出需要开启的feature。 } VkDeviceCreateInfo;

如果 pNext 包含 VkPhysicalDeviceVulkan11Features ,那么它必须不能包含 VkPhysicalDevice16BitStorageFeatures,VkPhysicalDeviceMultiviewFeatures, VkPhysicalDeviceVariablePointersFeatures,VkPhysicalDeviceProtectedMemoryFeatures,VkPhysicalDeviceSamplerYcbcrConversionFeatures, or VkPhysicalDeviceShaderDrawParametersFeatures

如果 pNext 包含 VkPhysicalDeviceVulkan12Features ,那么它必须不能包含 VkPhysicalDevice8BitStorageFeatures,VkPhysicalDeviceShaderAtomicInt64Features,VkPhysicalDeviceShaderFloat16Int8Features,VkPhysicalDeviceDescriptorIndexingFeatures,VkPhysicalDeviceScalarBlockLayoutFeatures,VkPhysicalDeviceImagelessFramebufferFeatures,VkPhysicalDeviceUniformBufferStandardLayoutFeatures,VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures,VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures,VkPhysicalDeviceHostQueryResetFeatures,VkPhysicalDeviceTimelineSemaphoreFeatures,VkPhysicalDeviceBufferDeviceAddressFeatures, or VkPhysicalDeviceVulkanMemoryModelFeatures

pNext 必须是 NULL,或者VkDeviceGroupDeviceCreateInfo,VkPhysicalDevice16BitStorageFeatures,VkPhysicalDevice8BitStorageFeatures,VkPhysicalDeviceBufferDeviceAddressFeatures,VkPhysicalDeviceDescriptorIndexingFeatures, VkPhysicalDeviceFeatures2,VkPhysicalDeviceHostQueryResetFeatures,VkPhysicalDeviceImagelessFramebufferFeatures, VkPhysicalDeviceMultiviewFeatures,VkPhysicalDeviceProtectedMemoryFeatures,VkPhysicalDeviceSamplerYcbcrConversionFeatures,VkPhysicalDeviceScalarBlockLayoutFeatures,VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures,VkPhysicalDeviceShaderAtomicInt64Features,VkPhysicalDeviceShaderDrawParametersFeatures,VkPhysicalDeviceShaderFloat16Int8Features,VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures,VkPhysicalDeviceTimelineSemaphoreFeatures,VkPhysicalDeviceUniformBufferStandardLayoutFeatures,VkPhysicalDeviceVariablePointersFeatures, VkPhysicalDeviceVulkan11Features,VkPhysicalDeviceVulkan12Features, or VkPhysicalDeviceVulkanMemoryModelFeatures

pNext中的每个sType都必须是独一无二的

一个logical device可以关联若干个physical devices,方法是在pNext中加入 VkDeviceGroupDeviceCreateInfo 结构体

typedef struct VkDeviceGroupDeviceCreateInfo { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO const void* pNext; //NULL,或者扩展该结构体的另外一个结构体 uint32_t physicalDeviceCount; //pPhysicalDevices 数组的元素数量,如果这个数字不为0, vkCreateDevice 的 physicalDevice 参数必须是 pPhysicalDevices 中的一个元素 const VkPhysicalDevice* pPhysicalDevices; //一个 physicalDeviceCount 个 physical device 的数组,其中的元素都属于同一个device group(从 vkEnumeratePhysicalDeviceGroups 获取)。是logical device关联的一个有序list,必须是一个device group的子集,可以与被get出来的时候的顺序不同。该顺序决定了每个physical device 的device index。一些命令和结构体用于若干个physical device会用到device index或者通过device index组成的device mask。数组中的每个元素必须独一无二。 } VkDeviceGroupDeviceCreateInfo;

假如pNext中没有 VkDeviceGroupDeviceCreateInfo ,或者 physicalDeviceCount 为 0,就相当于 physicalDeviceCount 为1,pPhysicalDevices 指向 physicalDevice。这个时候,该physical device的device index为0

typedef struct VkDeviceQueueCreateInfo { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO const void* pNext; //NULL,或者扩展该结构体的另外一个结构体,必须是NULL VkDeviceQueueCreateFlags flags; //flag,用于表明queue的behavior,如果不支持protected memory,则一定不能设置 VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT,必须是 VkDeviceQueueCreateFlagBits 组合出来的,或者0 uint32_t queueFamilyIndex; //设置该device创建的queue family的index。每个 pQueueCreateInfos 中的该元素必须独一无二,除非一个是 protected-capable queue,另外一个不是 protected-capable queue,这样的话,这两个 pQueueCreateInfos 可以使用相同的queueFamilyIndex。 这个index对应 vkGetPhysicalDeviceQueueFamilyProperties 得到的 pQueueFamilyProperties 数组中的index,一定要小于 vkGetPhysicalDeviceQueueFamilyProperties 返回的 pQueueFamilyPropertyCount uint32_t queueCount; //设置 在上述 queue family 中创建多少个 queue。必须大于0,必须小于或者等于 VkQueueFamilyProperties 的 queueCount (通过 vkGetPhysicalDeviceQueueFamilyProperties 获取到的) const float* pQueuePriorities; //一个指向 queueCount 个元素的数组,其中包含的归一化float 值(范围为[0.0,1.0]),代表着每个 queue 的优先级。1.0最高,0.0最低。在同一个device中,优先级高的queue可以被分配更多的processing time。而针对相同优先级的queue,除非有显示的synchronization primitives,否则implementation不规定其调度先后顺序。优先级高的queue可以饿死优先级低的queue(直到优先级高的queue没有更多command需要执行)。一个logical device的queue无法通过优先级的关系饿死其它logical device的queue。当然,也没有强制规定,要求高优先级的queue必须拥有更多的processing time或者更高的服务质量。 } VkDeviceQueueCreateInfo;

typedef enum VkDeviceQueueCreateFlagBits { // Provided by VK_VERSION_1_1 VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 0x00000001, //指定 device queue 是一个 protected-capable 的queue } VkDeviceQueueCreateFlagBits;

typedef struct VkPhysicalDeviceFeatures { VkBool32 robustBufferAccess; VkBool32 fullDrawIndexUint32; VkBool32 imageCubeArray; VkBool32 independentBlend; VkBool32 geometryShader; VkBool32 tessellationShader; VkBool32 sampleRateShading; VkBool32 dualSrcBlend; VkBool32 logicOp; VkBool32 multiDrawIndirect; VkBool32 drawIndirectFirstInstance; VkBool32 depthClamp; VkBool32 depthBiasClamp; VkBool32 fillModeNonSolid; VkBool32 depthBounds; VkBool32 wideLines; VkBool32 largePoints; VkBool32 alphaToOne; VkBool32 multiViewport; VkBool32 samplerAnisotropy; VkBool32 textureCompressionETC2; VkBool32 textureCompressionASTC_LDR; VkBool32 textureCompressionBC; VkBool32 occlusionQueryPrecise; VkBool32 pipelineStatisticsQuery; VkBool32 vertexPipelineStoresAndAtomics; VkBool32 fragmentStoresAndAtomics; VkBool32 shaderTessellationAndGeometryPointSize; VkBool32 shaderImageGatherExtended; VkBool32 shaderStorageImageExtendedFormats; VkBool32 shaderStorageImageMultisample; VkBool32 shaderStorageImageReadWithoutFormat; VkBool32 shaderStorageImageWriteWithoutFormat; VkBool32 shaderUniformBufferArrayDynamicIndexing; VkBool32 shaderSampledImageArrayDynamicIndexing; VkBool32 shaderStorageBufferArrayDynamicIndexing; VkBool32 shaderStorageImageArrayDynamicIndexing; VkBool32 shaderClipDistance; VkBool32 shaderCullDistance; VkBool32 shaderFloat64; VkBool32 shaderInt64; VkBool32 shaderInt16; VkBool32 shaderResourceResidency; VkBool32 shaderResourceMinLod; VkBool32 sparseBinding; VkBool32 sparseResidencyBuffer; VkBool32 sparseResidencyImage2D; VkBool32 sparseResidencyImage3D; VkBool32 sparseResidency2Samples; VkBool32 sparseResidency4Samples; VkBool32 sparseResidency8Samples; VkBool32 sparseResidency16Samples; VkBool32 sparseResidencyAliased; VkBool32 variableMultisampleRate; VkBool32 inheritedQueries; } VkPhysicalDeviceFeatures;

typedef struct VkPhysicalDeviceFeatures2 { VkStructureType sType; void* pNext; VkPhysicalDeviceFeatures features; } VkPhysicalDeviceFeatures2;

在CTS中的使用:vkCreateDevice(context.gpu, &device_info, nullptr, &context.device)

在UE中的使用:VkResult Result = VulkanRHI::vkCreateDevice(Gpu, &DeviceInfo, VULKAN_CPU_ALLOCATOR, &Device); //FVulkanDevice::CreateDevice()

Device将被用于:1.创建queue,2.创建和跟踪各种各样的synchronization constructs,3.分配、释放、管理内存,4.创建和破坏 command buffer和command buffer pool,5.创建、破环和管理graphics state(pipeline、resource descriptor等)

logical device可能会由于很多原因 lost,导致 pending和future的command execution可能会失败,导致resource和backing memory变成undefined。

典型的device loss的原因包括:执行超时(防止拒绝服务)、电源管理、平台资源管理、实现错误。

不遵守有效用法的应用程序也可能导致devie loss。即使device loss被reported,系统依然可能是一个无法恢复的状态,future的api依然invalid。

在这种情况下,某些命令将返回 VK_ERROR_DEVICE_LOST ,logical device会被认为是丢失状态,且无法重置为非丢失状态。然而丢失状态是特定于logical device的,对相应的physical device的其它方面可能没有影响。

在某些情况下,physical device也可能会丢失,尝试创建新的logical device会失败,返回 VK_ERROR_DEVICE_LOST 。这通常表示底层实现或其与主机的链接出现了问题。如果physical device没问题,那么通过它创建的新的logical device,也一定是处于非丢失状态。

虽然logical device丢失肯定可以回复,但在physical device丢失的情况下,应用程序不太可能回复,除非系统上存在其他未受影响的physical device。该错误主要是用于通知用户发生了平台问题,应该进一步调查。比如,底层硬件可能出现了故障或者与其他其他物理断开了链接。在许多情况下,physical device丢失会导致其他更严重的问题,比如操作系统崩溃等,在这种情况下,可能不会通过Vulkan API报告。

当device丢失的时候,它的子对象不会被隐式销毁,并且它们的句柄依然有效。在销毁这些对象的父对象或者device之前,需要先销毁这些对象。使用vkMapMemory映射的设备内存,对应的主机地址空间仍然有效,对这些映射区域的主机内存访问依然有效,但内容未定义。在设备和子对象调用任何API命令仍然是合法的。

一旦device丢失,执行命令可能失败,返回 VK_ERROR_DEVICE LOST 。一些命令不允许runtime error,仍然会正确运行,返回有效结果。

无限期等待device执行的命令(vkDeviceWaitIdle、vkQueueWaitIdle、vkWaitForFences使用最大timeout值,vkGetQueryPoolResults 的flag设置为VK_QUERY_RESULT_WAIT_BIT )必须在有限时间内返回,即使设备丢失,也必须返回 VK_SUCCESS 或者 VK_ERROR_DEVICE_LOST 。对于可能返回 VK_ERROR_DEVICE_LOST 的命令,为了确定command buffer是否处于pending 状态,或者resource被认为是被device使用中,VK_ERROR_DEVICE_LOST 的返回值等同于 VK_SUCCESS

从丢失的device导入或者到处的任何外部内存对象的内容都将变得未定义。与丢失devcie的外部内存对象相关联的,其他logica device或者通过其他API中的对象不会收到影响,只是它们的内容变得未定义。绑定到与丢失device上的外部内存对象相关联的VkDeviceMemory对象的其他logical device上影响的子资源的布局将变为 VK_IMAGE_LAYOUT_UNDEFINED 。

其他logical device上,通过semaphore payload 与丢失的device 关联的 VkSemaphore对象,也是未定义。实现必须确保这些信号量上的挂起和随后提交的等待操作的行为符合wait操作的有效状态的外部信号量的等待操作的信号量状态要求中的定义。

void vkDestroyDevice( VkDevice device, const VkAllocationCallbacks* pAllocator);

第一个输入参数是 vkCreateDevice 创建出来的device的handle

第二个输入参数是用于内存分配,可以是NULL,如果 vkCreateDevice 的时候设置了该参数,这里也需要设置一个对应的 callback 参数。反之,如果 vkCreateDevice 的时候没有设置,那么这里必须是NULL。

为了确保device上没有活动的工作,可以使用 vkDeviceWaitIdle 来确保。在销毁 device之前,应用程序负责销毁/释放 device 通过vkCreate* 或者vkAllocate* 这些API 创建的Vulkan对象。

每个物件的生命周期都与VkDevice对象相关联。所以,为了避免资源泄露,应用程序必须在调用 vkDestroyDevice 之前显示释放所有这些资源。

queue是通过 vkCreateDevice 创建的,而一个logical device关联的所有queue,都是通过对这个device调用 vkDestroyDevice 被销毁的。

在CTS中的使用:vkDestroyDevice(context.device, nullptr);

在UE中的使用:VulkanRHI::vkDestroyDevice(Device, VULKAN_CPU_ALLOCATOR);//FVulkanDevice::Destroy()

		Host access to device must be externally synchronized
		Host access to all VkQueue objects received from device must be externally synchronized
	
void vkGetDeviceQueue( VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);

获取 logical device中某个queue的handle

第一个输入参数是拥有 queue 的logical device

第二个输入参数是 queue 所在的 queue family 的 index,必须是创建logical device的时候,包含在 VkDeviceQueueCreateInfo 中的queue family index

第三个输入参数是 queue 在 queue family 中的 index,必须小于创建logical device的时候,包含在 VkDeviceQueueCreateInfo 中的 queueCount

第四个输入参数是获取到的 queue 的handle

vkGetDeviceQueue 只能获取 VkDeviceQueueCreateInfo 的flag为0的queue,如果想获取flag不为0的queue,需要用 vkGetDeviceQueue2

正如上述所言, vkGetPhysicalDeviceQueueFamilyProperties 可以获取到 physical device 支持的queue family和queue。 vkGetPhysicalDeviceQueueFamilyProperties 获取到的 pQueueFamilyProperties 中的 index 都是独一无二的,这些index被用于创建 queue,与 vkCreateDevice 中的 VkDeviceQueueCreateInfo 中的 queueFamilyIndex 相对应。

queue family index在很多地方会使用到,除了这里之外,还有,在创建 VkCommandPool 的时候,需要在 VkCommandPoolCreateInfo 中设置一个 queue family index。 这个Pool中的Command buffer 只能 submit到该 queue family 中的queue中。

在创建VkImage 和 VkBuffer的时候,也需要通过 VkImageCreateInfo 和 VkBufferCreateInfo 指定 queue families,指定只有这些 queue familyies可以访问这些资源。

当插入一个 VkBufferMemoryBarrier 或者 VkImageMemoryBarrier 的时候,需要设置一个source和一个desination queue family来指定,将buffer或者image的ownership从一个queue family转移到另外一个queue family。

在CTS中的使用:vkGetDeviceQueue(context.device, context.graphics_queue_index, 0, &context.queue);

在UE中的使用:VulkanRHI::vkGetDeviceQueue(Device->GetInstanceHandle(), FamilyIndex, QueueIndex, &Queue);//FVulkanQueue::FVulkanQueue(..)

void vkGetDeviceQueue2( VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);

获取 logical device中在 VkDeviceQueueCreateFlags 中设定了特殊 flag 的 queue 的handle

第一个输入参数是拥有 queue 的logical device

第二个输入参数是一个 VkDeviceQueueInfo2 类型的结构体,包含了 queue 的参数

第三个输入参数是获取到的 queue 的handle

typedef struct VkDeviceQueueInfo2 { VkStructureType sType; //当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2 const void* pNext; //NULL,或者扩展该结构体的另外一个结构体,必须是NULL VkDeviceQueueCreateFlags flags; //是创建该queue时候的 VkDeviceQueueCreateFlags flag,必须是 VkDeviceQueueCreateFlagBits 的合法组合。如果在创建device的时候,没有创建对应flag的queue,则返回NULL uint32_t queueFamilyIndex; //queue 所在的 queue family 的 index,必须是创建logical device的时候,包含在 VkDeviceQueueCreateInfo 中的queue family index uint32_t queueIndex; //queue 在 queue family 中的 index,必须小于创建logical device的时候,包含在 VkDeviceQueueCreateInfo 中的 queueCount } VkDeviceQueueInfo2;

所有工作都是通过 vkQueueSubmit 被submit到一个queue中的。被submit到queue中的command包含一些列针对physical device的操作,比如 synchronization with semaphores and fences。

该submit command的参数中包含一个目标queue,0个或者batches work,一个可选的fence来标志完成。每个batch都包含三个方面内容:1.在执行batch剩下内容之前,等待0个或者多个semaphores。2.0个或者多个work item需要执行。3.0个或者多个semaphores来标志该work item是否完成。

如果queue submission中含一个fense,则表示一个fence signal operation。

一个queue submission包含的所有work,都必须在command返回之前,submit 到queue

在Vulkan中,支持将buffer、image与memory sparase bind。sparase memory binding是一个queue操作,一个包含 VK_QUEUE_SPARSE_BINDING_BIT 的 queue必须支持将device上的一个virtual address和一个physical address映射。这会导致device上page table的一次更新。这个更新必须与queue同步,以避免graphics command在执行过程中page table被破坏。由于需要在queue上bind sparse memory,那么所有依赖被更新的binding 同步的command都需要在binding更新后执行。后面的章节会介绍如何完成这个同步

在CTS中的使用:无

Vulkan

本节教程就到此结束,希望大家继续阅读我之后的教程。

谢谢大家,再见!


原创技术文章,撰写不易,转载请注明出处:电子设备中的画家|王烁 于 2021 年 4 月 20 日发表,原文链接(http://geekfaner.com/shineengine/blog15_Vulkanv1.2_1.html)