SVTAV1热点函数-svt_ext_all_sad_calculation_8x8_16x16_avx2

一 svt_ext_all_sad_calculation_8x8_16x16_avx2函数的作用

是SVT-AV1编码其中计算运动估计绝对值差值和SAD计算,这个函数对8x8和16x16大小的块进行SAD计算,并且利用了AVX2指令集来加速计算。

函数参数解释:

src 指向原图像数据的指针

src_stride 源图像的步长,即美航像素之间的字节偏移量。

ref: 指向参考图像数据的指针

ref_stride: 参考图像的步长

mv: 运动矢量,表示当前块相对于参考块的位移

p_best_sad_8x8:指向存储最佳8x8块的SAD值的指针

p_best_sad_16x16:指向存储最佳16x16块的SAD值的指针

p_best_mv8x8 指向存储最佳8x8块对应运动矢量的指针

p_best_mv16x16 一个二维数组,用于存储多个16x16块的SAD值

p_eight_sad16x16 一个二维数组,用于存储多个16x16块的SAD值

p_eight_sad8x8 一个二维数组,用于存储多个8x8块的SAD值

sub_sad 一个布尔值,用于表示是否进行子块SAD计算。

函数的作用,在运动估计过程中,计算当前块与参考块之间的SAD值,找到最佳匹配块。对于16x16的块,会计算多个子块(8x8)的SAD值,并选择具有最小SAD值的块作为最佳匹配块。同时,它还会记录对应的最佳运动矢量。通过这种方式,编码器可以有效的确定如何在参考帧中找到当前块的最佳匹配,从而提高编码效率和压缩性能。

二 函数逐行注释

void svt_ext_all_sad_calculation_8x8_16x16_avx2(uint8_t *src, uint32_t src_stride, uint8_t *ref, uint32_t ref_stride,

uint32_t mv, uint32_t *p_best_sad_8x8, uint32_t *p_best_sad_16x16,

uint32_t *p_best_mv8x8, uint32_t *p_best_mv16x16,

uint32_t p_eight_sad16x16[16][8], uint32_t p_eight_sad8x8[64][8],

Bool sub_sad)

{

// 定义一个静态数组,用于存储16x16块的偏移量,这些偏移量用于确定16x16块在更大图像中的位置

static const char offsets[16] = {0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15};

// 外层循环,遍历16x16块的y轴方向,共4行

for (int y = 0; y < 4; y++) {

// 内层循环,遍历16x16块的x轴方向,共4列

for (int x = 0; x < 4; x++) {

// 根据当前的y和x,计算16x16块的起始位置索引

const uint32_t start_16x16_pos = offsets[4 * y + x];

// 计算对应8x8块的起始位置索引,因为一个16x16块包含4个8x8块

const uint32_t start_8x8_pos = 4 * start_16x16_pos;

// 计算当前16x16块在源图像中的起始地址

const uint8_t *s = src + 16 * y * src_stride + 16 * x;

// 计算当前16x16块在参考图像中的起始地址

const uint8_t *r = ref + 16 * y * ref_stride + 16 * x;

// 初始化两个256位的AVX寄存器,用于存储SAD计算的中间结果

__m256i sad02 = _mm256_setzero_si256();

__m256i sad13 = _mm256_setzero_si256();

// 如果sub_sad为真,表示需要进行子块的SAD计算

if (sub_sad) {

// 循环4次,处理16x16块中的每个4x4子块

for (int i = 0; i < 4; i++) {

// 从源图像中加载两个128位的16像素数据(两个8x8块的行数据)

const __m128i src01 = _mm_loadu_si128((__m128i *)(s + 0 * src_stride));

const __m128i src23 = _mm_loadu_si128((__m128i *)(s + 8 * src_stride));

// 从参考图像中加载四个128位的16像素数据,分别对应四个8x8块的行数据

const __m128i ref0 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 0));

const __m128i ref1 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 8));

const __m128i ref2 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 0));

const __m128i ref3 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 8));

// 将两个128位的源数据合并成一个256位的源数据

const __m256i src0123 = _mm256_insertf128_si256(_mm256_castsi128_si256(src01), src23, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref0和ref2)

const __m256i ref02 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref0), ref2, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref1和ref3)

const __m256i ref13 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref1), ref3, 1);

// 使用AVX指令计算ref02和src0123之间的SAD值,并累加到sad02中,模式为000 000

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 0));

// 再次计算ref02和src0123之间的SAD值,模式为101 101,并累加到sad02中

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 45));

// 计算ref13和src0123之间的SAD值,模式为010 010,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 18));

// 再次计算ref13和src0123之间的SAD值,模式为111 111,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 63));

// 更新源和参考图像的指针,移动到下一行的对应位置

s += 2 * src_stride;

r += 2 * ref_stride;

}

// 将sad02中的值左移1位,相当于乘以2,进行数据调整

sad02 = _mm256_slli_epi16(sad02, 1);

// 将sad13中的值左移1位,相当于乘以2,进行数据调整

sad13 = _mm256_slli_epi16(sad13, 1);

} else {

// 循环8次,处理16x16块中的每个8x8块

for (int i = 0; i < 8; i++) {

// 从源图像中加载两个128位的16像素数据(两个8x8块的行数据)

const __m128i src01 = _mm_loadu_si128((__m128i *)(s + 0 * src_stride));

const __m128i src23 = _mm_loadu_si128((__m128i *)(s + 8 * src_stride));

// 从参考图像中加载四个128位的16像素数据,分别对应四个8x8块的行数据

const __m128i ref0 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 0));

const __m128i ref1 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 8));

const __m128i ref2 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 0));

const __m128i ref3 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 8));

// 将两个128位的源数据合并成一个256位的源数据

const __m256i src0123 = _mm256_insertf128_si256(_mm256_castsi128_si256(src01), src23, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref0和ref2)

const __m256i ref02 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref0), ref2, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref1和ref3)

const __m256i ref13 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref1), ref3, 1);

// 使用AVX指令计算ref02和src0123之间的SAD值,并累加到sad02中,模式为000 000

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 0));

// 再次计算ref02和src0123之间的SAD值,模式为101 101,并累加到sad02中

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 45));

// 计算ref13和src0123之间的SAD值,模式为010 010,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 18));

// 再次计算ref13和src0123之间的SAD值,模式为111 111,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 63));

// 更新源和参考图像的指针,移动到下一行的对应位置

s += src_stride;

r += ref_stride;

}

}

// 忽略p_eight_sad8x8参数,可能用于其他功能

(void)p_eight_sad8x8;

// 将sad02的低128位部分提取出来,存储到sad0中

const __m128i sad0 = _mm256_castsi256_si128(sad02);

// 将sad13的低128位部分提取出来,存储到sad1中

const __m128i sad1 = _mm256_castsi256_si128(sad13);

// 将sad02的高128位部分提取出来,存储到sad2中

const __m128i sad2 = _mm256_extracti128_si256(sad02, 1);

// 将sad13的高128位部分提取出来,存储到sad3中

const __m128i sad3 = _mm256_extracti128_si256(sad13, 1);

// 对sad0中的每个16位元素,找到最小值及其位置索引,存储到minpos0中

const __m128i minpos0 = _mm_minpos_epu16(sad0);

// 对sad1中的每个16位元素,找到最小值及其位置索引,存储到minpos1中

const __m128i minpos1 = _mm_minpos_epu16(sad1);

// 对sad2中的每个16位元素,找到最小值及其位置索引,存储到minpos2中

const __m128i minpos2 = _mm_minpos_epu16(sad2);

// 对sad3中的每个16位元素,找到最小值及其位置索引,存储到minpos3中

const __m128i minpos3 = _mm_minpos_epu16(sad3);

// 将minpos0和minpos1的低16位元素组合成一个新的128位数据

const __m128i minpos01 = _mm_unpacklo_epi16(minpos0, minpos1);

// 将minpos2和minpos3的低16位元素组合成一个新的128位数据

const __m128i minpos23 = _mm_unpacklo_epi16(minpos2, minpos3);

// 将minpos01和minpos23的低32位元素组合成一个新的128位数据

const __m128i minpos0123 = _mm_unpacklo_epi32(minpos01, minpos23);

// 提取minpos0123中的SAD值部分,存储到sad8x8中

const __m128i sad8x8 = _mm_unpacklo_epi16(minpos0123, _mm_setzero_si128());

// 提取minpos0123中的位置索引部分,存储到pos8x8中

const __m128i pos8x8 = _mm_unpackhi_epi16(minpos0123, _mm_setzero_si128());

// 加载当前8x8块的最佳SAD值

__m128i best_sad8x8 = _mm_loadu_si128((__m128i *)(p_best_sad_8x8 + start_8x8_pos));

// 比较当前计算的sad8x8和已有的最佳SAD值,生成掩码

const __m128i mask = _mm_cmplt_epi32(sad8x8, best_sad8x8);

// 更新最佳SAD值,取当前计算值和已有值中的较小者

best_sad8x8 = _mm_min_epi32(best_sad8x8, sad8x8);

// 将更新后的最佳SAD值存储回p_best_sad_8x8数组中

_mm_storeu_si128((__m128i *)(p_best_sad_8x8 + start_8x8_pos), best_sad8x8);

// 设置运动矢量mv的128位数据

const __m128i mvs = _mm_set1_epi32(mv);

// 加载当前8x8块的最佳运动矢量

__m128i best_mv8x8 = _mm_loadu_si128((__m128i *)(p_best_mv8x8 + start_8x8_pos));

// 根据位置索引计算当前块的运动矢量

const __m128i mv8x8 = _mm_add_epi16(mvs, pos8x8);

// 根据掩码更新最佳运动矢量,当当前SAD更小时使用新的运动矢量

best_mv8x8 = _mm_blendv_epi8(best_mv8x8, mv8x8, mask);

// 将更新后的最佳运动矢量存储回p_best_mv8x8数组中

_mm_storeu_si128((__m128i *)(p_best_mv8x8 + start_8x8_pos), best_mv8x8);

// 计算sad0和sad1的和,得到sum01

const __m128i sum01 = _mm_add_epi16(sad0, sad1);

// 计算sad2和sad3的和,得到sum23

const __m128i sum23 = _mm_add_epi16(sad2, sad3);

// 计算sum01和sum23的和,得到16x16块的SAD值

const __m128i sad16x16_16 = _mm_add_epi16(sum01, sum23);

// 将16位的SAD值扩展为32位,便于后续处理

const __m256i sad16x16_32 = _mm256_cvtepu16_epi32(sad16x16_16);

// 将计算得到的16x16块的SAD值存储到p_eight_sad16x16数组中

_mm256_storeu_si256((__m256i *)(p_eight_sad16x16[start_16x16_pos]), sad16x16_32);

// 对sad16x16_16中的每个16位元素,找到最小值及其位置索引,存储到minpos16x16中

const __m128i minpos16x16 = _mm_minpos_epu16(sad16x16_16);

// 提取最小值

const uint32_t min16x16 = _mm_extract_epi16(minpos16x16, 0);

// 如果当前计算的最小SAD值小于已有的最佳SAD值,则更新

if (min16x16 < p_best_sad_16x16[start_16x16_pos]) {

// 更新最佳SAD值

p_best_sad_16x16[start_16x16_pos] = min16x16;

// 提取位置索引部分

const __m128i pos16x16 = _mm_srli_si128(minpos16x16, 2);

// 计算对应的运动矢量

const __m128i mv16x16 = _mm_add_epi16(mvs, pos16x16);

// 更新最佳运动矢量

p_best_mv16x16[start_16x16_pos] = _mm_extract_epi32(mv16x16, 0);

}

}

}

}

相关推荐
Ulana15 分钟前
计算机基础10大高频考题解析
java·人工智能·算法
windfantasy199016 分钟前
NCT与GESP哪个更好?线上监考与线下考点的便利性对比
人工智能
执笔论英雄18 分钟前
【LORA】
人工智能
Jerryhut31 分钟前
Bev感知特征空间算法
人工智能
xian_wwq41 分钟前
【学习笔记】基于人工智能的火电机组全局性能一体化优化研究
人工智能·笔记·学习·火电
春风LiuK1 小时前
虚实无界:VRAR如何重塑课堂与突破研究边界
人工智能·程序人生
歌_顿1 小时前
Embedding 模型word2vec/glove/fasttext/elmo/doc2vec/infersent学习总结
人工智能·算法
胡萝卜3.01 小时前
深入C++可调用对象:从function包装到bind参数适配的技术实现
开发语言·c++·人工智能·机器学习·bind·function·包装器
Echo_NGC22371 小时前
【KL 散度】深入理解 Kullback-Leibler Divergence:AI 如何衡量“像不像”的问题
人工智能·算法·机器学习·散度·kl
愤怒的可乐1 小时前
从零构建大模型智能体:OpenAI Function Calling智能体实战
人工智能·大模型·智能体