【转载】Vulkan 1.2 知识串讲(1)—— INSTANCE & DEVICE & QUEUE
文章转自
电子设备中的画家|王烁 于 2021 年 4 月 20 日发表——Vulkan 1.2 知识串讲(1)
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);
在CTS中的使用:
result = vkCreateInstance(&instance_info, nullptr, &handle);
Vulkan中没有global state,每个应用程序的所偶遇状态都存储在VkInstance中:
- 第一个输入参数为创建instance所用到到的信息:
typedef struct VkInstanceCreateInfo {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,必须是NULL
const void* pNext;
//reserved for future,必须是0
VkInstanceCreateFlags flags;
//NULL,或者一个包含应用程序信息的结构体,用于告诉instance应用程序信息。
//如果使用NULL,或者使用一个0版本的apiVersion,则相当于使用了VK_MAKE_API_VERSION(0,1,0,0) 的apiVersion
const VkApplicationInfo* pApplicationInfo;
//启用的global layer的数量
uint32_t enabledLayerCount;
//启用的global layer的名字,第一个是最靠近应用程序的layer,最后一个是最靠近驱动的layer
const char* const* ppEnabledLayerNames;
//启用的global extension的数量
uint32_t enabledExtensionCount;
//启用的extension的名字,这里包含了所有的extension
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;
typedef struct VkApplicationInfo {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_APPLICATION_INFO
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,必须是NULL
const void* pNext;
//NULL,或者应用程序名字
const char* pApplicationName;
//应用程序版本
uint32_t applicationVersion;
//NULL,或者创建该应用程序的引擎名字
const char* pEngineName;
//引擎版本
uint32_t engineVersion;
//应用程序所支持的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
uint32_t apiVersion;
} 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
。
void vkDestroyInstance( VkInstance instance, const VkAllocationCallbacks* pAllocator);
在CTS中的使用:
vkDestroyInstance(handle, nullptr);
与
vkCreateInstance
对应,删除instance。删除instance之前,需要删除所有通过instance创建的物件。
第一个输入参数 可以是
NULL
,或者一个合法的
VkInstance
。
第二个输入参数如果不是
NULL
,则必须是一个合法的
VkAllocationCallbacks
结构体:如果
vkCreateInstance
的时候使用
VkAllocationCallbacks
了,那么删除的时候也需要提供一个与之兼容的
VkAllocationCallbacks
;如果
vkCreateInstance
的时候没有使用
VkAllocationCallbacks
,那么删除的时候
VkAllocationCallbacks
也必须是
NULL
。
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);
在CTS中的使用:
//获取physical devices的数量
vkEnumeratePhysicalDevices(context.instance, &gpu_count, nullptr);
//获取physical devices的list
vkEnumeratePhysicalDevices(context.instance, &gpu_count, gpus.data());
该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
。
void vkGetPhysicalDeviceProperties( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties);
在CTS中的使用:
vkGetPhysicalDeviceProperties(physical_device, &properties);
用于获取通过
vkEnumeratePhysicalDevices
得到的physical device的属性。
第一个输入参数为被查询的 physicalDevice。
第二个输入参数为一个指向
VkPhysicalDeviceProperties
的指针,用于获取被查询出来的数据:
typedef struct VkPhysicalDeviceProperties {
//当前device支持的Vulkan版本,需要注意的是,这里的apiVersion可能会和 vkEnumerateInstanceVersion 获取到的不同,可能高或者低。
//这种情况,应用程序不能使用超过given object相关的vulkan 版本的特性。
//vkEnumerateInstanceVersion 返回的 pApiVersion ,为instance及其children相关的版本,
//而非 VkPhysicalDevice 以及其 children。
//VkPhysicalDeviceProperties::apiVersion 为 VkPhysicalDevice 及其 children 相关的版本。
uint32_t apiVersion;
//vendor公司设定的驱动版本,driverVersion 的编码格式为 implementation-defined,可能与apiVersion的编码格式不同。
//应用程序需要根据vendor提供的信息,来从driverVersion 获取版本信息
uint32_t driverVersion;
//当前physical device对应的vendor公司的标识
uint32_t vendorID;
//当前physical device的标识符
uint32_t deviceID;
//当前device的类型
VkPhysicalDeviceType deviceType;
//一个包含 VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 元素的数组,包含了physical device的name
char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
//一个包含 VK_UUID_SIZE 元素的数组,表示当前设备中的一个唯一标识符
uint8_t pipelineCacheUUID[VK_UUID_SIZE];
//当前physical device的限制
VkPhysicalDeviceLimits limits;
//当前physical device的various sparse 相关属性
VkPhysicalDeviceSparseProperties sparseProperties;
} VkPhysicalDeviceProperties;
physical device type不会影响操作。然而可能会与系统的其他公开属性或者功能相关,比如有多少内存。
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;
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;
//vendorID 的枚举值,这个枚举随时可能更新,只有vk.xml和vulkan_core.h才包含了所有reserved Khronos vendor IDs。
//只有在Khronos注册的vendor IDs才会返回名字,根据 implementation 返回的PCI vendor IDs可以通过PCI-SIG database 进行查询。
typedef enum VkVendorId {
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;
void vkGetPhysicalDeviceProperties2( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);
在CTS中的使用:
vkGetPhysicalDeviceProperties2(get_device().get_gpu().get_handle(), &device_properties);
用于获取通过
vkEnumeratePhysicalDevices
得到的physical device的属性
第一个输入参数为被查询的 physicalDevice。
第二个输入参数为一个指向
VkPhysicalDeviceProperties2
的指针,用于获取被查询出来的数据
typedef struct VkPhysicalDeviceProperties2 {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体(包含extension定义的属性),结构体的类型是:
//VkPhysicalDeviceDepthStencilResolveProperties, VkPhysicalDeviceDescriptorIndexingProperties,
//VkPhysicalDeviceDriverProperties, VkPhysicalDeviceFloatControlsProperties,
//VkPhysicalDeviceIDProperties, VkPhysicalDeviceMaintenance3Properties,
//VkPhysicalDeviceMultiviewProperties, VkPhysicalDevicePointClippingProperties,
//VkPhysicalDeviceProtectedMemoryProperties, VkPhysicalDeviceSamplerFilterMinmaxProperties,
//VkPhysicalDeviceSubgroupProperties, VkPhysicalDeviceTimelineSemaphoreProperties,
//VkPhysicalDeviceVulkan11Properties, or VkPhysicalDeviceVulkan12Properties。
//pNext中的 sType 必须 unique
void* pNext;
//一个指向 VkPhysicalDeviceProperties 的指针,用于获取被查询出来的数据,与 vkGetPhysicalDeviceProperties 相同。
VkPhysicalDeviceProperties properties;
} VkPhysicalDeviceProperties2;
void vkGetPhysicalDeviceQueueFamilyProperties( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
在CTS中的使用:
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_properties_count, queue_family_properties.data());
用于获取通过
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 {
//用于表示 VkQueueFlagBits , 是用于该queue family中queue的功能
VkQueueFlags queueFlags;
//为该queue family中 queue的数量。每个 queue family 至少包含一个 queue
uint32_t queueCount;
//是通过 vkCmdWriteTimestamp 这个API输入的 timestamps 中有意义的位。该数值的有效位是36-64位(其他为应该为0),或者为0(代表不支持timestamps)
uint32_t timestampValidBits;
//是该 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尺寸来放大粒度。
VkExtent3D minImageTransferGranularity;
} 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
。
void vkGetPhysicalDeviceQueueFamilyProperties2( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
在CTS中的使用: 无
用于获取通过
vkEnumeratePhysicalDevices
得到的physical device的 avaliable queues的属性。
第一个输入参数为被查询的 physicalDevice。
第二个输入参数用于获取 avaliable或者queried的queue families 的数量。和
vkGetPhysicalDeviceQueueFamilyProperties
一样。
第三个输入参数可以是
NULL
,或者一个指向
VkQueueFamilyProperties2
handle 数组的指针。
vkGetPhysicalDeviceQueueFamilyProperties2
和
vkGetPhysicalDeviceQueueFamilyProperties
类似,除了会在pNext中多返回一个extended 信息
typedef struct VkQueueFamilyProperties2 {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,必须是NULL
void* pNext;
//一个指向 VkQueueFamilyProperties 的指针,用于获取被查询出来的数据,与 vkGetPhysicalDeviceQueueFamilyProperties 相同。
VkQueueFamilyProperties queueFamilyProperties;
} VkQueueFamilyProperties2;
VkResult vkEnumeratePhysicalDeviceGroups( VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
在CTS中的使用: 无
用于获取支持的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 {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,必须是NULL
void* pNext;
//当前device group中physical devices的数量
uint32_t physicalDeviceCount;
//一个包含 VK_MAX_DEVICE_GROUP_SIZE 个 VkPhysicalDevice 元素的数组,其中包含了当前device group中所有的physical devices,所以,前 physicalDeviceCount 个元素是合法的。
VkPhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE];
//指定根据group创建的device,所分配的device memory的方式,通过 VkMemoryAllocateFlagsInfo 的 deviceMask 设置。
//如果为 VK_FALSE ,那么所有的device memory都可以被 group中的所有physical devices共享。
//如果 physicalDeviceCount 为 1,那么 subsetAllocation 必须是 VK_FALSE 。
VkBool32 subsetAllocation;
} VkPhysicalDeviceGroupProperties;
VkResult vkCreateDevice( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
在CTS中的使用:
vkCreateDevice(context.gpu, &device_info, nullptr, &context.device) ;
用于创建关联physical device的logical device。
Device将被用于:1.创建queue,2.创建和跟踪各种各样的synchronization constructs,3.分配、释放、管理内存,4.创建和破坏 command buffer和command buffer pool,5.创建、破环和管理graphics state(pipeline、resource descriptor等)
第一个输入参数为通过
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 {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,如果其中包含 VkPhysicalDeviceFeatures2 结构体,那么 pEnabledFeatures 必须是NULL
const void* pNext;
//reserved for future,必须为0
VkDeviceCreateFlags flags;
//为 pQueueCreateInfos 数组的尺寸,必须大于0
uint32_t queueCreateInfoCount;
//一个指向 queueCreateInfoCount 个 VkDeviceQueueCreateInfo 数组的指针,创建logical device的时候同步创建这些queues
const VkDeviceQueueCreateInfo* pQueueCreateInfos;
uint32_t enabledLayerCount; //这个已经废弃
const char* const* ppEnabledLayerNames; //这个已经废弃
//当前device开启的extension数量
uint32_t enabledExtensionCount;
//一个指向 enabledExtensionCount 个 extension name元素的数组,在创建device的时候这些extension需要被开启,这些extension所依赖的extension也必须放在这个list中
const char* const* ppEnabledExtensionNames;
//NULL,或者一个指针指向 VkPhysicalDeviceFeatures 结构体,其中用bool值指出需要开启的feature。
const VkPhysicalDeviceFeatures* pEnabledFeatures;
} VkDeviceCreateInfo;
pNext中的每个sType都必须是独一无二的。
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 包含
VkPhysicalDeviceVulkan11Features
,那么它必须不能包含:
VkPhysicalDevice16BitStorageFeatures
,
VkPhysicalDeviceMultiviewFeatures
,
VkPhysicalDeviceVariablePointersFeatures
,
VkPhysicalDeviceProtectedMemoryFeatures
,
VkPhysicalDeviceSamplerYcbcrConversionFeatures
, or
VkPhysicalDeviceShaderDrawParametersFeatures
。
如果 pNext 包含
VkPhysicalDeviceVulkan12Features
,那么它必须不能包含 :
VkPhysicalDevice8BitStorageFeatures
,
VkPhysicalDeviceShaderAtomicInt64Features
,
VkPhysicalDeviceShaderFloat16Int8Features
,
VkPhysicalDeviceDescriptorIndexingFeatures
,
VkPhysicalDeviceScalarBlockLayoutFeatures
,
VkPhysicalDeviceImagelessFramebufferFeatures
,
VkPhysicalDeviceUniformBufferStandardLayoutFeatures
,
VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures
,
VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures
,
VkPhysicalDeviceHostQueryResetFeatures
,
VkPhysicalDeviceTimelineSemaphoreFeatures
,
VkPhysicalDeviceBufferDeviceAddressFeatures
, or
VkPhysicalDeviceVulkanMemoryModelFeatures
。
一个logical device可以关联若干个physical devices,方法是在pNext中加入
VkDeviceGroupDeviceCreateInfo
结构体:
typedef struct VkDeviceGroupDeviceCreateInfo {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体
const void* pNext;
//pPhysicalDevices 数组的元素数量,如果这个数字不为0, vkCreateDevice 的 physicalDevice 参数必须是 pPhysicalDevices 中的一个元素
uint32_t physicalDeviceCount;
//一个 physicalDeviceCount 个 physical device 的数组,其中的元素都属于同一个device group(从 vkEnumeratePhysicalDeviceGroups 获取)。
//是logical device关联的一个有序list,必须是一个device group的子集,可以与被get出来的时候的顺序不同。
//该顺序决定了每个physical device 的device index。
//一些命令和结构体用于若干个physical device会用到device index或者通过device index组成的device mask。数组中的每个元素必须独一无二。
const VkPhysicalDevice* pPhysicalDevices;
} VkDeviceGroupDeviceCreateInfo;
假如pNext中没有
VkDeviceGroupDeviceCreateInfo
,或者 physicalDeviceCount 为 0,就相当于 physicalDeviceCount 为1,pPhysicalDevices 指向 physicalDevice。这个时候,该physical device的device index为0。
typedef struct VkDeviceQueueCreateInfo {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,必须是NULL
const void* pNext;
//flag,用于表明queue的behavior,如果不支持protected memory,则一定不能设置 VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT,必须是 VkDeviceQueueCreateFlagBits 组合出来的,或者0
VkDeviceQueueCreateFlags flags;
//设置该device创建的queue family的index。
//每个 pQueueCreateInfos 中的该元素必须独一无二,除非一个是 protected-capable queue,另外一个不是 protected-capable queue,这样的话,这两个 pQueueCreateInfos 可以使用相同的queueFamilyIndex。
//这个index对应 vkGetPhysicalDeviceQueueFamilyProperties 得到的 pQueueFamilyProperties 数组中的index,一定要小于 vkGetPhysicalDeviceQueueFamilyProperties 返回的 pQueueFamilyPropertyCount
uint32_t queueFamilyIndex;
//设置 在上述 queue family 中创建多少个 queue。必须大于0,必须小于或者等于 VkQueueFamilyProperties 的 queueCount (通过 vkGetPhysicalDeviceQueueFamilyProperties 获取到的)
uint32_t queueCount;
//一个指向 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或者更高的服务质量。
const float* pQueuePriorities;
} VkDeviceQueueCreateInfo;
typedef enum VkDeviceQueueCreateFlagBits {
// Provided by VK_VERSION_1_1
//指定 device queue 是一个 protected-capable 的queue
VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 0x00000001,
} 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;
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);
在CTS中的使用:
vkDestroyDevice(context.device, nullptr);
第一个输入参数是
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
被销毁的。
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);
在CTS中的使用:
vkGetDeviceQueue(context.device, context.graphics_queue_index, 0, &context.queue);
获取 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。
void vkGetDeviceQueue2( VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
在CTS中的使用:无
获取 logical device中在
VkDeviceQueueCreateFlags
中设定了特殊 flag 的 queue 的handle。
第一个输入参数是拥有 queue 的logical device。
第二个输入参数是一个
VkDeviceQueueInfo2
类型的结构体,包含了 queue 的参数。
第三个输入参数是获取到的 queue 的handle 。
typedef struct VkDeviceQueueInfo2 {
//当前结构体的类型,必须是 VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2
VkStructureType sType;
//NULL,或者扩展该结构体的另外一个结构体,必须是NULL
const void* pNext;
//是创建该queue时候的 VkDeviceQueueCreateFlags flag,必须是 VkDeviceQueueCreateFlagBits 的合法组合。
//如果在创建device的时候,没有创建对应flag的queue,则返回NULL
VkDeviceQueueCreateFlags flags;
//queue 所在的 queue family 的 index,必须是创建logical device的时候,包含在 VkDeviceQueueCreateInfo 中的queue family index
uint32_t queueFamilyIndex;
//queue 在 queue family 中的 index,必须小于创建logical device的时候,包含在 VkDeviceQueueCreateInfo 中的 queueCount
uint32_t queueIndex;
} 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. 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更新后执行。后面的章节会介绍如何完成这个同步。
本节教程就到此结束,希望大家继续阅读我之后的教程。
谢谢大家,再见!