跳到主要内容

受保护的内存

受保护内存将设备内存分为 “受保护设备内存” 和 “未受保护的设备内存”。

通常情况下除非明确共享(例如通过外部内存),大多数操作系统不允许一个应用程序访问另一个应用程序的 GPU 内存。受保护内存的一个常见用法是允许进程修改的DRM 内容(例如,用于图像过滤或合成播放字幕),数据在到达显示屏上呈现前一直保持加密状态。

Vulkan 规范详细解释了“受保护设备内存”的要求,以下是使用受保护内存的详细内容。

检查是否支持

受保护的内存在Vulkan 1.1 中添加,任何 Vulkan 1.0 设备都无法支持受保护的内存。应用程序必须查询并启用VkPhysicalDeviceProtectedMemoryFeatures::protectedMemory字段来检查是否支持该功能。

受保护的队列

受保护队列可以读取受保护和不受保护的内存,但只能写入受保护内存。可以写入未受保护的内存的队列不能从受保护的内存中读取数据。

提示

为防止side-channe攻击,受保护队列通常会禁用性能计数器和其他定时测量系统,或降低其精确度。

使用vkGetPhysicalDeviceQueueFamilyProperties获取每个队列的VkQueueFlags,应用程序可以找到一个公开VK_QUEUE_PROTECTED_BIT的队列族。

vkCreateDevice时,VkDeviceQueueCreateInfo设置VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT,从而告诉驱动程序对VkQueue进行保护。

以下伪代码是应用程序从同一队列族创建2个受保护的VkQueue

VkDeviceQueueCreateInfo queueCreateInfo[1];
queueCreateInfo[0].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
queueCreateInfo[0].queueFamilyIndex = queueFamilyFound;
queueCreateInfo[0].queueCount = 2; // assuming 2 queues are in the queue family

VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.pQueueCreateInfos = queueCreateInfo;
deviceCreateInfo.queueCreateInfoCount = 1;
vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &deviceHandle);

还可以将队列族的队列拆分,让一些队列受到保护,而另一些不受保护。

以下伪代码从同一队列族创建1个受保护和1个不受保护的VkQueue

VkDeviceQueueCreateInfo queueCreateInfo[2];
queueCreateInfo[0].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
queueCreateInfo[0].queueFamilyIndex = queueFamilyFound;
queueCreateInfo[0].queueCount = 1;

queueCreateInfo[1].flags = 0; // unprotected because the protected flag is not set
queueCreateInfo[1].queueFamilyIndex = queueFamilyFound;
queueCreateInfo[1].queueCount = 1;

VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.pQueueCreateInfos = queueCreateInfo;
deviceCreateInfo.queueCreateInfoCount = 2;
vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &deviceHandle);

在获取VkQueue句柄时,应用程序必须使用vkGetDeviceQueue2而不是vkGetDeviceQueue来传递VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT标志:

VkDeviceQueueInfo2 info = {};
info.queueFamilyIndex = queueFamilyFound;
info.queueIndex = 0;
info.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
vkGetDeviceQueue2(deviceHandle, &info, &protectedQueue);

受保护的资源

创建一个受保护的VkImageVkBuffer很简单,只需设置VK_IMAGE_CREATE_PROTECTED_BITVK_BUFFER_CREATE_PROTECTED_BIT

将内存绑定到受保护资源时,VkDeviceMemory必须从具备VK_MEMORY_PROPERTY_PROTECTED_BITVkMemoryType分配。

受保护的交换链

创建受保护的swapchain时,需设置VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR

从受保护的交换链中通过vkGetSwapchainImagesKHR获取的VkImage,执行的操作与使用 VK_IMAGE_CREATE_PROTECTED_BIT创建的image相同。

有时,不知道swapchain是否可以通过VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR标志创建 ,可通过VK_KHR_surface_protected_capabilities 扩展查询。

受保护的命令缓冲区

使用受保护的 VkQueue ,应用程序还可以使用VK_COMMAND_POOL_CREATE_PROTECTED_BIT创建VkCommandPool

VkCommandPoolCreateInfo info = {};
info.flags = VK_COMMAND_POOL_CREATE_PROTECTED_BIT;
info.queueFamilyIndex = queueFamilyFound; // protected queue
vkCreateCommandPool(deviceHandle, &info, nullptr, &protectedCommandPool);

从受保护的命令池分配的所有命令缓冲区都成为“受保护的命令缓冲区”:

VkCommandBufferAllocateInfo info = {};
info.commandPool = protectedCommandPool;
vkAllocateCommandBuffers(deviceHandle, &info, &protectedCommandBuffers);

提交受保护的任务

在提交受保护的任务时,所有提交的VkCommandBuffer也必须受到保护:

VkProtectedSubmitInfo protectedSubmitInfo = {};
protectedSubmitInfo.protectedSubmit = true;

VkSubmitInfo submitInfo = {};
submitInfo.pNext = &protectedSubmitInfo;
submitInfo.pCommandBuffers = protectedCommandBuffers;

vkQueueSubmit(protectedQueue, 1, &submitInfo, fence));

或使用 VK_KHR_synchronization2

VkSubmitInfo2KHR submitInfo = {}
submitInfo.flags = VK_SUBMIT_PROTECTED_BIT_KHR;

vkQueueSubmit2KHR(protectedQueue, 1, submitInfo, fence);