受保护的内存
受保护内存将设备内存分为 “受保护设备内存” 和 “未受保护的设备内存”。
通常情况下除非明确共享(例如通过外部内存),大多数操作系统不允许一个应用程序访问另一个应用程序的 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);
受保护的资源
创建一个受保护的VkImage
或VkBuffer
很简单,只需设置VK_IMAGE_CREATE_PROTECTED_BIT
和VK_BUFFER_CREATE_PROTECTED_BIT
。
将内存绑定到受保护资源时,VkDeviceMemory
必须从具备VK_MEMORY_PROPERTY_PROTECTED_BIT
的VkMemoryType
分配。
受保护的交换链
创建受保护的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));
VkSubmitInfo2KHR submitInfo = {}
submitInfo.flags = VK_SUBMIT_PROTECTED_BIT_KHR;
vkQueueSubmit2KHR(protectedQueue, 1, submitInfo, fence);