VK_ANDROID_external_format_resolve

此扩展允许渲染到具有外部格式的 Android 硬件缓冲区,这些格式无法在 Vulkan 中直接表示为可渲染的格式,包括 YCBCR 格式。

1. 问题陈述

应用程序今天可以使用 OpenGL ES 中的 GL_EXT_yuv_target 渲染到 Android 上的未知格式,这允许直接渲染到具有未知外部 YCBCR 格式的图像。 为了支持这些在 Vulkan 上运行的应用程序(通过移植或通过 ANGLE),Vulkan 也需要类似的功能。

然而,需要克服的一个问题是,GL_EXT_yuv_target 非常不透明,在 OpenGL ES 中,这允许实现隐藏许多棘手的细节。 对于 Vulkan,隐藏这些细节要困难得多,因此需要一种满足所有潜在实现需求的替代方案,包括那些不支持直接渲染到 YCBCR 图像的实现。

2. 解决方案空间

任何解决方案都需要满足以下要求

  • 它必须提供与 GL_EXT_yuv_target 提供的功能相当的功能

  • 必须可以通过此扩展在 ANGLE 中模拟 GL_EXT_yuv_target

  • 它必须可以在所有实现上干净地实现

  • 它必须在支持直接 YCBCR 渲染的实现上表现良好

  • 对于应用程序来说,使用它必须相对简单

  • 它必须支持渲染通道对象和动态渲染

最简单的解决方案是直接允许在 Vulkan 中渲染到 YCBCR 图像;这将满足大多数要求,但可能无法干净地实现,或者需要向开发人员公开许多尴尬的细节。

另一个被提出的想法是,通过类似于在渲染通道结束时用于解析多采样图像的机制来实现某些功能;解析操作。 需要注意确保实现仍然可以使用直接渲染,但它应该能够满足上述所有要求。

3. 提议

此扩展扩展了渲染通道对象和动态渲染功能。

3.1. 动态渲染

使用动态渲染时,会添加一个新的解析模式来指定解析附件将具有外部格式

    VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_ANDROID;

当指定此解析模式时,用作颜色附件的 VkRenderingAttachmentInforesolveImageView 成员必须设置为使用具有非零外部格式的图像创建的 VkImageView。如果外部格式的色度平面是经过子采样的,则实现将通过平均颜色附件中相应的值,或者只是选择其中一个值作为代表来减少相关平面。实现可以在任何时候将颜色附件解析为外部格式解析附件,或者完全绕过写入颜色附件的操作。

当解析模式设置为 VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_ANDROID 时,还适用以下附加约束

  • 如果 nullColorAttachmentWithExternalFormatResolve 属性为 VK_TRUE,则 imageView 必须为 VK_NULL_HANDLE

    • 渲染期间颜色附件中的值从外部格式附件加载,并根据需要重采样到片段速率。

  • 如果 imageView 不为 NULL,则它必须是层数为 1 的单采样图像。

  • VkRenderingInfolayerCountcolorAttachmentCount 成员必须为 1。

  • VkRenderingInfoviewMask 成员必须为 0。

  • 不能存在片段密度图图像

  • 不能存在片段着色率图像

  • 片段着色率必须为 1x1

实现可能需要在创建管线时知道正在使用外部格式 YCBCR 格式,因此当使用动态渲染时,可以将 VkExternalFormatANDROID 链接到 VkGraphicsPipelineCreateInfo,以指示解析图像的格式。渲染时,此处指定的解析图像格式和用于该颜色解析附件的实际图像视图必须相同。包含带有非零值的 VkExternalFormatANDROID 结构的图形管线只能写入单个颜色附件,不得从片段着色器导出深度或模板,并且必须禁用混合。

3.1.1. 继承信息

当动态渲染与辅助命令缓冲区继承一起使用时,必须通过在 VkCommandBufferInheritanceInfopNext 链中包含 VkExternalFormatANDROID,使辅助命令缓冲区知道外部格式,并且外部格式必须与渲染通道实例中的格式匹配。

3.2. 渲染通道对象

对于渲染通道对象,可以通过在子通道中将颜色解析附件设置为具有外部格式的附件,类似地将颜色解析附件重新用于外部 YCBCR 格式解析。这些只能与 VkSubpassDescription2 一起使用,并且对此的限制与对动态渲染的限制类似。

  • 解析附件必须是一个格式为 VK_FORMAT_UNDEFINED 并且在其 pNext 链中包含 VkExternalFormatANDROID 结构的附件

    • 此格式必须与通过帧缓冲区绑定的图像视图的格式匹配

  • 如果 nullColorAttachmentWithExternalFormatResolve 属性为 VK_TRUE,则相应颜色附件的 attachment 成员必须为 VK_ATTACHMENT_UNUSED

  • 如果颜色附件不为 VK_ATTACHMENT_UNUSED,则它必须是单采样附件。

  • viewMask 必须为 0。

  • colorAttachmentCount 必须为 1。

渲染期间写入的颜色附件值以与 VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_ANDROID 指定的方式相同的方式解析。

3.3. 输入附件

如果 nullColorAttachmentWithExternalFormatResolve 属性为 VK_FALSE,则应用程序可以像使用任何其他颜色附件一样正常绑定颜色附件,并且值读取按预期工作。仅当通过 vkGetAndroidHardwareBufferPropertiesANDROID 查询的功能位声明此功能时,才可以将外部格式图像用作输入附件。

但是,如果 nullColorAttachmentWithExternalFormatResolve 属性为 VK_TRUE,则应用程序无法执行此操作,因为没有要使用的附件。相反,解析附件本身应绑定为输入附件(包括附件引用和描述符)。在此特定配置中使用解析附件时,可以像它是实际的颜色附件一样同步,从而允许子通道的自依赖性。如果实现支持此属性,则可以使用外部格式图像作为输入附件,而无需通常由 vkGetAndroidHardwareBufferPropertiesANDROID 声明的功能位。

如果外部格式解析图像作为输入附件读取并且具有子采样的色度平面,则这些将按照上述方法进行重采样,以在预期速率下提供值。它们的值不会通过颜色空间转换进行转换 - 与解析一样,应用程序必须自己转换这些值。

3.4. 格式解析属性

并非所有外部格式都可用于外部格式解析;以下属性结构指示外部格式是否受解析支持

typedef struct VkAndroidHardwareBufferFormatResolvePropertiesANDROID {
    VkStructureType     sType;
    void*               pNext;
    VkFormat            colorAttachmentFormat;
} VkAndroidHardwareBufferFormatResolvePropertiesANDROID;

可以解析到的外部格式将指示颜色附件在渲染时应使用的格式。如果不可解析,则会将其设置为 VK_FORMAT_UNDEFINED

任何可渲染的 Android 硬件缓冲区都必须通过现有格式路径或通过此扩展进行渲染。

对于公开 nullColorAttachmentWithExternalFormatResolve 的实现,该格式不应用于创建图像,但仍有两个额外的用途。

首先,格式的数值类型指示着色器中需要的类型(例如,UNORM 格式表示浮点颜色输出)。

除此之外,它还指示颜色输出保留在颜色缓冲区中时数据的精度;因此,它应始终具有等于或大于硬件缓冲区格式的每通道精度。直接渲染到解析附件并且从不在中间颜色缓冲区中存储数据的实现可以将其设置为足够大的类型,以保证它不会干扰最终值的精度。由于不期望数据保留在颜色缓冲区中,因此应用程序应根据颜色缓冲区格式和 Android 硬件缓冲区格式之间每个通道的最低精度来期望最低精度。

3.5. 设备功能

以下单个功能公开了此扩展中的所有功能

typedef struct VkPhysicalDeviceExternalFormatResolveFeaturesANDROID {
    VkStructureType    sType;
    void*              pNext;
    VkBool32           externalFormatResolve;
} VkPhysicalDeviceExternalFormatResolveFeaturesANDROID;

如果声明了此扩展,则必须支持 externalFormatResolve

3.6. 设备属性

公开以下属性

typedef struct VkPhysicalDeviceExternalFormatResolvePropertiesANDROID {
    VkStructureType     sType;
    void*               pNext;
    VkBool32            nullColorAttachmentWithExternalFormatResolve;
    VkChromaLocation    externalFormatResolveChromaOffsetX;
    VkChromaLocation    externalFormatResolveChromaOffsetY;
} VkPhysicalDeviceExternalFormatResolvePropertiesANDROID;
  • 如果 nullColorAttachmentWithExternalFormatResolveVK_TRUE,则应用程序必须通过将 VkRenderingAttachmentInfo::imageView 设置为 NULL 进行动态渲染,或者在使用 VK_ATTACHMENT_UNUSED 创建渲染通道对象时省略颜色附件。

  • externalFormatResolveChromaOffsetX 指示当使用外部格式解析到解析附件或从解析附件加载时,实现使用的 X 轴色度偏移。

  • externalFormatResolveChromaOffsetY 指示当使用外部格式解析到解析附件或从解析附件加载时,实现使用的 Y 轴色度偏移。

色度偏移在 Vulkan 实现的读取和写入操作之间是一致的,但可能与其他写入该数据的系统不一致;这可能会导致在未先写入的情况下从输入附件读取时产生轻微的误差。如果对精度有要求,可以使用 YCBCR 采样进行初始读取,其中偏移量是可配置的,而不是作为输入附件读取。

4. 示例

4.1. 创建渲染通道对象

// Create two attachments, a resolve and color attachment
VkAttachmentDescription2 attachments[2] = {
    {
        VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
        &externalFormat,
        0,
        VK_FORMAT_UNDEFINED,
        1,
        VK_LOAD_OP_LOAD,
        VK_STORE_OP_STORE,
        VK_LOAD_OP_LOAD,
        VK_STORE_OP_STORE,
        VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
        VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL
    },
    {
        VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
        NULL,
        0,
        resolveFormatProperties.colorAttachmentFormat,
        1,
        VK_LOAD_OP_LOAD,
        VK_STORE_OP_STORE,
        VK_LOAD_OP_LOAD,
        VK_STORE_OP_STORE,
        VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
        VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL
     }
};

// Resolve attachment always specified
VkAttachmentReference2 resolveAttachment = {
    VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
    NULL,
    0,
    VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
    0};

// Color attachment must be UNUSED if nullColorAttachmentWithExternalFormatResolve is VK_TRUE
VkAttachmentReference2 colorAttachment = {
    VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2,
    NULL,
    nullColorAttachmentWithExternalFormatResolve ? VK_ATTACHMENT_UNUSED : 1,
    VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
    0};

// No changes to subpass creation
VkSubpassDescription2 subpass = {
    VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2,
    NULL,
    0,
    VK_PIPELINE_BIND_POINT_GRAPHICS,
    0,
    0,
    NULL,
    1,
    &colorAttachment,
    &resolveAttachment,
    NULL,
    0,
    NULL};

// Only add the color attachment information if nullColorAttachmentWithExternalFormatResolve is VK_FALSE
VkRenderPassCreateInfo2 createInfo = {
    VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2,
    NULL,
    0,
    nullColorAttachmentWithExternalFormatResolve ? 1 : 2,
    &attachments,
    1,
    &subpass,
    0,
    0,
    NULL};

VkRenderPass renderPass;
vkCreateRenderPass2(device, &createInfo, NULL, &renderPass);

4.2. 动态渲染

// Do not attach a color image view if nullColorAttachmentWithExternalFormatResolve is VK_TRUE
// Other setup is identical either way
VkRenderingAttachmentInfo colorAttachment = {
    VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
    NULL,
    nullColorAttachmentWithExternalFormatResolve ? VK_NULL_HANDLE : colorImageView;
    VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
    VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_ANDROID,
    externalResolveImageView,
    VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
    VK_LOAD_OP_LOAD,
    VK_STORE_OP_STORE,
    clearValue};

VkRect2D renderArea = { ... };
VkRenderingInfo renderingInfo = {
    VK_STRUCTURE_TYPE_RENDERING_INFO,
    NULL,
    0,
    renderArea,
    1,
    0,
    1,
    &colorAttachment,
    NULL,
    NULL};

vkCmdBeginRendering(commandBuffer, renderingInfo);

5. 问题

5.1. GL_EXT_yuv_target 有一个着色器扩展,但这个没有,为什么?

该扩展的 GLSL 部分包含两个部分

  1. 指定颜色输出将用作 yuv

  2. YUV 转换辅助函数

对于辅助函数,没有实现可以在着色器代码中加速这些操作;因此它们已被省略,而倾向于使用高级语言转换(例如 glslang)来提供这些函数。

yuv 输出标记在 GLSL 中是必要的,因为它会严重影响编译,并且这是 OpenGL ES 向编译器公开此类信息的唯一方法。然而,在 Vulkan 中,我们可以使用管道对象来代替;管道包含足够的信息以在 API 级别提供此信息。这意味着此扩展将无法与 VK_EXT_shader_object 或任何类似的扩展一起使用,除非进行进一步的工作。所有这些都是由于时间限制而做出的有意选择。

5.2. 谁负责 YCBCR 色彩空间的相互转换?

应用程序负责这些转换;实现仅负责在色度平面上不同采样率之间重新采样值。输出值期望与 GL_EXT_yuv_target 的输出值期望一致。

5.3. 附件清除如何工作?

附件清除的操作方式与对颜色附件的写入操作相同,这意味着与颜色附件写入一样,值必须具有外部格式的正确色彩空间,并且通道顺序必须符合预期。

5.4. YUV 输出的预期通道顺序是什么?

在 GL_EXT_yuv_target 中,预期的映射是将 Y、CB 和 CR 通道分别映射到输出的 R、G 和 B 通道。但是,Vulkan 建立了一个约定,即 CB 和 CR 应映射到 B 和 R 通道,与它们的色度指定相匹配。

此扩展与 Vulkan 约定相匹配,要求将 Y、CB 和 CR 通道分别映射到输出中的 G、B 和 R 通道。

导入的外部格式的通道顺序和色彩空间对于 Vulkan 实现是不透明的。因此,所有外部解析和输入附件访问都被视为它们是 ycbcr 恒等模型中的颜色图像,而没有范围扩展。例如,具有四个分量的图像被视为 R = Cr、G = Y 和 B = Cb。这意味着实际上

  • 输入附件读取将颜色分量作为 vec4(R, G, B, A) 呈现给着色器,并将 yuv 分量作为 (V, Y, U, A) 呈现给着色器

  • 外部格式解析将着色器输出变量中的颜色分量作为 vec4(R, G, B, A) 获取,并将 yuv 分量作为 (V, Y, U, A) 获取

  • 传递给开始渲染/渲染通道的清除颜色对于颜色分量取 (R, G, B, A),对于 yuv 分量取 (V, Y, U, A)

实现不得公开表示深度或模板格式的外部格式。应用程序必须使用 VkFormat 导入深度图像,以便渲染到它们。没有深度、颜色或 yuv 分量的图像超出了 Vulkan 接口的范围,并由其应使用的有效颜色分量的格式定义,例如 RAW10 格式。

5.5. 当支持 nullColorAttachmentWithExternalFormatResolve 时,如何对空附件执行重采样?

从子采样平面读取最近的样本,以填充片段着色器中的值。

5.6. 是否有办法可以更好地统一 nullColorAttachmentWithExternalFormatResolve 路径的两个选项?

这很可能可以做到,但由于时间限制而被跳过。未来的扩展应该能够更好地统一这些路径。

5.7. 如果 nullColorAttachmentWithExternalFormatResolveVK_FALSE,如何在渲染时保留外部图像的内容?

当渲染通道开始时,如果 nullColorAttachmentWithExternalFormatResolveVK_FALSE,则会从颜色附件加载数据,而不是从外部图像取消解析。这可能会导致在开始新的渲染时数据无法保留。

如果外部图像的内容仅由应用程序使用外部格式解析进行渲染,则如果使用 VK_STORE_OP_STORE,则颜色附件数据将包含来自先前渲染的任何未解析数据,从而保留渲染数据。但是,如果外部图像的内容未反映在颜色附件中,则应用程序需要在渲染开始之前,在单独的通道中手动取消解析图像。

如果渲染开始时不需要保留内容,则可以像往常一样执行渲染,而没有特殊要求。

6. 未来工作

此扩展相当有限,因为它旨在匹配 GL_EXT_yuv_target,并且由于时间限制而不再做更多的事情。可以引入进一步的扩展来扩展功能,以包括多重采样、存储颜色和 YCBCR 图像、对精度进行更严格的控制以及色彩空间转换等。