高阶滤波和块匹配:利用 Vulkan 新图像处理扩展优化性能和功耗
发表于 2024-03-07 10:47:28

GPU 的主要用途之一是图像(纹理)采样和处理。GPU 内置专用硬件,用于执行最近的邻域、双线性筛选和双三次滤波(使用 VK_EXT_filter_cubic 扩展)。但是,有些用例需要使用更大的内核或自定义内核权重进行采样。这些用例可以使用现有的采样指令在片段或计算着色器中手动实现。但是,需要纹理和着色器单元之间多次往返,从功耗或性能的角度来看,并不理想。

本文将介绍最新的 高通 Adreno GPU如何使用专用硬件,对大内核和自定义内核权重进行采样。我们的Vulkan 驱动程序团队发布了一个新的 VK_QCOM_image_processing 扩展,以利用此硬件功能。经测试,我们发现,与手动编码的实现相比,运行时间下降了 75% 以上,能耗使用量降低了近 90%。

四个新的 GLSL 函数

VK_QCOM_image_processing 扩展公开了四个新的 OpenGL 着色语言(GLSL) 函数。

函数 1 – textureWeightedQCOM

vec4 textureWeightedQCOM(sampler2D tex, vec2 uv, sampler2DArray weights)

此函数将 2D 权重内核与以 uv 为中心的纹理区域相乘,并将结果汇总至返回的 vec4 值中。请注意,此处的权重纹理是 2D 值的数组。数组的每一层称为一个相位。当目标采样偏离像素精确中心时,相位允许使用不同的内核离散化。此函数对于非线性内核函数(如 sinc)非常重要,因为在有限分辨率的内核中,无法很好地捕获这些细节。

许多常见的 2D 图像过滤内核可以表示为两个 1D 过滤内核,被称为可分离过滤器,可以显著节省性能。创建可分离过滤器时,水平权重放置在第 0 层中,垂直权重打包置于纹理数组的第 1 层中。1D 权重必须以 4 个为一组排列。以下示例说明如何将尺寸为 3x3 的两相可分离过滤器打包。H 表示水平,P# 是相位数,[n]是可分离过滤器的第 n 个元素。

Layer 0:

第 0 层:

HP0[0]

HP0[1]

HP0[2]

empty

HP1[0]

HP1[1]

HP1[2]

empty

Layer 1:

第 1 层:

VP0[0]

VP0[1]

VP0[2]

empty

VP1[0]

VP1[1]

VP1[2]

empty

函数 2 – textureBoxFilterQCOM

vec4 textureBoxFilterQCOM(sampler2D tex, vec2 uv, vec2 boxSize)

此函数取一个框内纹素的平均值。平均值按覆盖范围加权,确保偏心采样准确。框的中心由 uv 指定,宽度由 boxSize.x 指定,高度由 boxSize.y 指定。

函数 3 – textureBlockMatchSADQCOM

vec4 textureBlockMatchSADQCOM(sampler2D target, uvec2 targetCoord, sampler2D reference, uvec2 refCoord, uvec2 blockSize)

此函数的作用是衡量目标子区域与参考子区域的相关性(相似性)。targetCoord 和 refCoord 表示图块左下角位置,而返回值则表示了每个组件的绝对差之和 (SAD):

其中 T 是目标纹理,R 是参考纹理。框的宽度和高度分别由 blockSize.x 和 blockSize.y 指定。此处的 uv 坐标指定图块左下角(而不是中心,如上面的加权和框过滤器函数)。

函数 4 – textureBlockMatchSSDQCOM

vec4 textureBlockMatchSSDQCOM(sampler2D target, uvec2 targetCoord, sampler2D reference, uvec2 refCoord, uvec2 blockSize)

此函数的工作方式与 textureBlockMatchSADQCOM 相同,但不是返回绝对差值总和,而是返回每个组件的平方差值和 (SSD):

上述四个函数都只对 2D 图像起作用。目前不支持 mipmap、多层、多采样或深度/模板纹理。

设置代码

本节介绍创建用于此扩展的纹理和采样器所需的新参数。另外,还包括 GLSL 中的示例程序。有关示例和更多详细信息,请参阅规范建议

参数

框过滤(函数 2)

目标采样器

  • 使用 VK_SAMPLER_CREATE_IMAGE_PROCESSING_BIT_QCOM 进行创建
  • unnormalizedCoordinates 可以是 VK_TRUE 或 VK_FALSE
  • addressModes 可以是 CLAMP_TO_EDGE 或 CLAMP_TO_BORDER(边框颜色必须是VK_BORDER_COLOR_TRANSPARENT_BLACK)
  • 缩减模式可以是 MIN、MAX 或 AVERAGE

目标纹理

  • 格式必须支持VK_FORMAT_FEATURE_2_BOX_FILTER_SAMPLED_BIT_QCOM

图块匹配(函数 3 和 4)

目标采样器

  • 使用 VK_SAMPLER_CREATE_IMAGE_PROCESSING_BIT_QCOM 进行创建
  • unnormalizedCoordinates 必须是 VK_TRUE
  • addressModes 可以是 CLAMP_TO_EDGE 或 CLAMP_TO_BORDER(边框颜色必须是VK_BORDER_COLOR_TRANSPARENT_BLACK)
  • 缩减模式可以是 MIN、MAX 或 AVERAGE

目标和参考纹理

  • 必须使用 VK_IMAGE_USAGE_SAMPLE_BLOCK_MATCH_BIT_QCOM 创建
  • 格式必须支持VK_FORMAT_FEATURE_2_BLOCK_MATCHING_BIT_QCOM
  • 布局必须是VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL或VK_IMAGE_LAYOUT_GENERAL

目标和参考纹理描述符

  • 必须使用 VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM 创建

加权图像采样(函数 1)

目标采样器

  • 使用 VK_SAMPLER_CREATE_IMAGE_PROCESSING_BIT_QCOM 进行创建
  • unnormalizedCoordinates 可以是 VK_TRUE 或 VK_FALSE
  • addressModes 可以是 CLAMP_TO_EDGE 或 CLAMP_TO_BORDER(边框颜色必须是VK_BORDER_COLOR_TRANSPARENT_BLACK)
  • 缩减模式可以是 MIN、MAX 或 AVERAGE

权重纹理

  • 必须使用 VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM 创建
  • 格式必须支持VK_FORMAT_FEATURE_2_WEIGHT_IMAGE_BIT_QCOM
  • 布局必须是VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL或VK_IMAGE_LAYOUT_GENERAL
  • 图像类型必须为 VK_IMAGE_TYPE_1D 或 VK_IMAGE_TYPE_2D

权重描述符

  • 必须使用 VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM 创建

权重图像视图

  • 必须使用 VkImageViewSampleWeightCreateInfoQCOM 扩展创建结构
  • ViewType 必须为 VK_IMAGE_VIEW_TYPE_1D_ARRAY 或 VK_IMAGE_VIEW_TYPE_2D_ARRAY

目标纹理

  • 格式必须支持VK_FORMAT_FEATURE_2_WEIGHT_SAMPLED_IMAGE_BIT_QCOM

使用单独纹理和采样器的示例程序片段:

#version 450

#extension GL_QCOM_image_processing : require
#extension GL_EXT_samplerless_texture_functions : require

// Inputs and outputs for fragment shader
layout (location = 0) in vec2 uv;
layout (location = 0) out vec4 fragColor;

// Texture and sampler inputs
layout(set = 0, binding = 0) uniform highp texture2D inputTex;
layout(set = 0, binding = 1) uniform highp texture2DArray kernelTex;
layout(set = 0, binding = 3) uniform highp sampler samplerLinear;
layout(set = 0, binding = 4) uniform highp sampler samplerKernel;

void main()
{
  fragColor = textureWeightedQCOM(sampler2D(inputTex, samplerLinear), uv, sampler2DArray(kernelTex, samplerKernel));
}
使用组合纹理和采样器的示例程序片段

#version 450

#extension GL_QCOM_image_processing : require
#extension GL_EXT_samplerless_texture_functions : require

// Inputs and outputs for fragment shader
layout (location = 0) in vec2 uv;
layout (location = 0) out vec4 fragColor;

// Texture and sampler inputs
layout(set = 0, binding = 0) uniform highp sampler2D inputTexSampler;
layout(set = 0, binding = 1) uniform highp sampler2DArray kernelTexSampler;

void main()
{
  fragColor = textureWeightedQCOM(inputTexSampler, uv, kernelTexSampler);
}

结果:运行时间更短,功耗更低

下列图表显示了使用 VK_QCOM_image_processing 扩展而不是分段程序手动实现相同操作在性能和能耗方面的巨大优势。

经在 SM8550 Android 设备上测试表明,与手动实现相比,该扩展使运行时间缩短了 75%,功耗降低了 90%。

我们衡量的工作负载使用 8x8 加权内核对 4 倍降尺度算法执行 3 次迭代以生成缩略图。图表中的值根据分段或计算机着色器的手动实现进行了调整,以便比较。

如何使用这些函数

以下是这些函数发挥作用的部分示例应用程序:

在图像模糊、锐化、边缘检测、特征检测、高阶过滤器内核应用、上采样、下采样和多级纹理(mipmap)生成时,使用 TextureWeightedQCOM(函数 1)。该函数支持不同的还原模式,如最小和最大还原,对于膨胀或侵蚀等过滤器很有用。

使用 TextureBoxFilterQCOM(函数 2)对图像进行模糊处理,检测平均曝光级别、明亮区域和黑暗区域等特征。

在特征检测、运动跟踪和图像对齐应用中使用 TextureBlockMatchSADQCOM(函数 3)和 TextureBlockMatchSSDQCOM(函数 4)。但是,对于运动估计或光流应用,我们建议使用专门为此开发的更高级别、优化更好的扩展:GL_QCOM_motion_estimation和VK_NV_optical_flow。

轮到你了 - 试用扩展

我们可以看到,尽量使用此扩展在性能和电池续航方面均有巨大的优势。

满足以下要求即可使用扩展:

  • SDK – 1.3.222 及更新版本的 Vulkan SDK
  • 硬件 – 支持 SM8550 及更新版本
  • Adreno GPU 驱动程序 –  512.649 及更新版本

您也可以在公开的手册页面规范提议中找到更多信息。我们很期待看到您在其他应用中也能使用这些扩展。先试用一下,告诉我们您的想法。

我们还准备了一个简单的执行泛光(bloom)操作的示例程序。它包括使用扩展的着色器和手动实现的着色器,以便进行比较。您可以在此处下载。

CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
微博关注
【免责声明:CSDN本栏目发布信息,目的在于传播更多信息,丰富网络文化,稿件仅代表作者个人观点,与CSDN无关。其原创性以及文中陈述文字和文字内容未经本网证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本网不做任何保证或者承诺,请读者仅作参考,并请自行核实相关内容。您若对该稿件有任何怀疑或质疑,请立即与CSDN联系,我们将迅速给您回应并做处理。】