【发布时间】:2016-04-29 15:50:21
【问题描述】:
我正在使用 Visual Studio 2015,构建 x64 代码,并使用四个 ABGR 像素值的浮点向量,即 Alpha(不透明度)在最重要的位置,蓝色、绿色和红色数字在较低的位置三个位置。
我正在尝试构建一个 PreMultiplyAlpha 例程,该例程将内联/__vectorcall 以高效地将 alpha 预乘为蓝色、绿色和红色,并在完成后将 Alpha 值设置为 1.0f。
实际乘法没问题。这会将 Alpha 传播到所有四个元素,然后将它们全部相乘。
__m128 Alpha = _mm_shuffle_ps(Pixel, Pixel, _MM_SHUFFLE(3, 3, 3, 3));
__m128 ReturnPixel = _mm_mul_ps(Pixel, Alpha);
使用上述方法,只需最少的指令,即可将 alpha 乘以所有颜色:
shufps xmm1, xmm0, 255 ; 000000ffH
mulps xmm1, xmm0
这是一个很好的开始,对吧?
然后我碰壁了...我还没有找到一种直接的方法——甚至是一种棘手的方法——去做看起来应该是一个相当简单的行为,即有效地将最重要的元素 (Alpha) 设置为 1.0 F。也许我只是有一个盲点。
最明显的方法是让 VC++ 2015 创建执行两次 128 位内存访问的机器代码:
ReturnPixel.m128_f32[ALPHA] = 1.0f;
上面生成这样的代码,将整个像素保存在堆栈中,覆盖Alpha,然后从堆栈中加载回来:
movaps XMMWORD PTR ReturnPixel$1[rsp], xmm1
mov DWORD PTR ReturnPixel$1[rsp+12], 1065353216 ; 3f800000H
movaps xmm1, XMMWORD PTR ReturnPixel$1[rsp]
我非常喜欢让代码尽可能简单明了,以便人类维护人员理解,但是这个特殊的例程被大量使用并且需要以最佳速度进行优化。
我尝试过的其他事情似乎会导致编译器发出不必要的指令(尤其是内存访问)...
这会尝试将 A 位置移动到最不重要的单词中,将其替换为 1.0f,然后将其移回。这很不错,但它确实会从内存位置获取单个 32 位 1.0f。
ReturnPixel = _mm_shuffle_ps(ReturnPixel, ReturnPixel, _MM_SHUFFLE(0, 2, 1, 3));
ReturnPixel = _mm_move_ss(ReturnPixel, _mm_set_ss(1.0f));
ReturnPixel = _mm_shuffle_ps(ReturnPixel, ReturnPixel, _MM_SHUFFLE(0, 2, 1, 3));
我收到了这些说明:
movss xmm0, DWORD PTR __real@3f800000
movaps xmm1, xmm0
shufps xmm2, xmm2, 39 ; 00000027H
movss xmm2, xmm1
shufps xmm2, xmm2, 39
任何想法如何在 A 字段(最重要的元素)中保留 1.0f 并使用最少的指令,并且理想情况下除了从指令流中获取的内容之外没有额外的内存访问?我什至考虑过将向量本身分开以在所有位置上实现 1.0f,但我对除法过敏,因为它们至少可以说效率低下...
提前感谢您的想法。 :-)
-诺埃尔
【问题讨论】:
-
如果您使用位掩码执行
&会怎样,例如11111110000000然后|代表浮点数 1? -
假设 SSE4.1 然后看看
_mm_insert_ps? (糟糕——刚刚看到 SSE2 的要求——这有点苛刻......) -
@PaulR:
_mm_blend_ps实际上是最有效的方式。insertps只能在 shuffle 端口上运行。但是对于 SSE2,@flatmouse 的建议似乎是最好的。两个 insn 而不是一个,只要你的编译器可以将常量的设置提升到循环之外就很好了。
标签: visual-c++ sse