Using Vulkan -- Memory Allocation -- Protected Memory

Using Vulkan -- Memory Allocation -- Protected Memory 受保护内存将设备内存划分为受保护设备内存和非受保护设备内存两类。一般情况下多数操作系统不允许一个应用程序访问另一个应用程序的 GPU 内存除非进行显式共享例如通过外部内存。受保护内存的典型应用场景是存储 DRM 内容进程可对这类内容执行修改操作如图像滤波、合成播放控件和隐藏式字幕但不得将其提取至非受保护内存中。相关数据以加密形式传入且始终保持加密状态直至输出到显示器的像素上。Vulkan 规范中详细阐释了受保护设备内存的强制约束条件。下文将分点说明通过受保护内存正确启用受保护提交所需满足的各项要求。检查支持性受保护内存功能在 Vulkan 1.1 中新增且此前无相关扩展这意味着所有 Vulkan 1.0 设备均不支持该功能。应用程序需查询并启用VkPhysicalDeviceProtectedMemoryFeatures::protectedMemory字段以检查设备对受保护内存的支持性。受保护队列受保护队列可读取受保护内存和非受保护内存但仅能向受保护内存执行写入操作。若某一队列可向非受保护内存写入则无法同时读取受保护内存。为防范侧信道攻击受保护队列的性能计数器及其他计时测量系统通常会被禁用或精度有所降低。应用程序可通过vkGetPhysicalDeviceQueueFamilyProperties获取每个队列的VkQueueFlags从而找到暴露VK_QUEUE_PROTECTED_BIT标志的队列族。这并不代表该队列族中的队列始终为受保护队列而是说明这些队列可被配置为受保护队列。若要告知驱动将VkQueue设为受保护队列需在vkCreateDevice创建设备的过程中于VkDeviceQueueCreateInfo中设置VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT标志。以下伪代码展示了应用程序如何从同一个队列族中创建 2 个受保护VkQueue对象VkDeviceQueueCreateInfo queueCreateInfo[1]; queueCreateInfo[0].flags VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT; queueCreateInfo[0].queueFamilyIndex queueFamilyFound; queueCreateInfo[0].queueCount 2; // 假设该队列族包含2个队列 VkDeviceCreateInfo deviceCreateInfo {}; deviceCreateInfo.pQueueCreateInfos queueCreateInfo; deviceCreateInfo.queueCreateInfoCount 1; vkCreateDevice(physicalDevice, deviceCreateInfo, nullptr, deviceHandle);也可对一个队列族中的队列进行拆分配置使部分为受保护队列、部分为非受保护队列。以下伪代码展示了如何从同一个队列族中创建 1 个受保护VkQueue对象和 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; // 未设置保护标志为非受保护队列 queueCreateInfo[1].queueFamilyIndex queueFamilyFound; queueCreateInfo[1].queueCount 1; VkDeviceCreateInfo deviceCreateInfo {}; deviceCreateInfo.pQueueCreateInfos queueCreateInfo; deviceCreateInfo.queueCreateInfoCount 2; vkCreateDevice(physicalDevice, deviceCreateInfo, nullptr, deviceHandle);此时应用程序需使用vkGetDeviceQueue2而非vkGetDeviceQueue以在获取VkQueue句柄时传入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中分配。受保护交换链创建交换链时设置VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR标志可将其配置为受保护交换链。通过受保护交换链调用vkGetSwapchainImagesKHR获取的所有VkImage与通过VK_IMAGE_CREATE_PROTECTED_BIT标志创建的图像属性一致。部分场景下无法确定是否能通过设置VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR标志创建交换链针对这类平台会暴露 VK_KHR_surface_protected_capabilities 扩展。受保护命令缓冲区使用受保护VkQueue时应用程序可在创建VkCommandPool时设置VK_COMMAND_POOL_CREATE_PROTECTED_BIT标志VkCommandPoolCreateInfo info {}; info.flags VK_COMMAND_POOL_CREATE_PROTECTED_BIT; info.queueFamilyIndex queueFamilyFound; // 受保护队列 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);