Use SSE http://www2.units.it/~csia/calcolointensivo/tartaglia/intel/cce/intref_cls.pdf- 从第 131 页开始。
基本工作流程
从src加载4个像素(16个1字节数字)RGBA RGBA RGBA RGBA(流加载)
再加载 4 个要与 srcbytetop 混合的 RGBx RGBx RGBx RGBx
-
进行一些调整,使 1 中的 A 项填满每个槽,即
xxxA xxxB xxxC xxxD -> AAAA BBBB CCCC DDDD
在下面的解决方案中,我选择重新使用现有的“maskcurrent”数组,但将 alpha 集成到 1 的“A”字段中将需要更少的内存负载,因此速度更快。在这种情况下,混合可能是:并使用掩码选择 A、B、C、D。右移 8,或者使用原始,右移 16,或再次。
将以上内容添加到每个槽中均为 -255 的向量中
-
乘以 1 * 4(源与 255-alpha)和 2 * 3(结果与 alpha)。
为此,您应该能够使用“乘法并丢弃底部 8 位”SSE2 指令。
将这两个(4 和 5)加在一起
将它们存储在其他地方(如果可能)或在您的目的地之上(如果必须)
这是您的起点:
//Define your image with __declspec(align(16)) i.e char __declspec(align(16)) image[640*480]
// so the first byte is aligned correctly for SIMD.
// Stride must be a multiple of 16.
for (int y = top ; y < bottom; ++y)
{
BYTE* resultByte = GET_BYTE(resultBits, left, y, stride, bytepp);
BYTE* srcByte = GET_BYTE(srcBits, left, y, stride, bytepp);
BYTE* srcByteTop = GET_BYTE(srcBitsTop, left, y, stride, bytepp);
BYTE* maskCurrent = GET_GREY(maskSrc, left, y, width);
for (int x = left; x < right; x += 4)
{
//If you can't align, use _mm_loadu_si128()
// Step 1
__mm128i src = _mm_load_si128(reinterpret_cast<__mm128i*>(srcByte))
// Step 2
__mm128i srcTop = _mm_load_si128(reinterpret_cast<__mm128i*>(srcByteTop))
// Step 3
// Fill the 4 positions for the first pixel with maskCurrent[0], etc
// Could do better with shifts and so on, but this is clear
__mm128i mask = _mm_set_epi8(maskCurrent[0],maskCurrent[0],maskCurrent[0],maskCurrent[0],
maskCurrent[1],maskCurrent[1],maskCurrent[1],maskCurrent[1],
maskCurrent[2],maskCurrent[2],maskCurrent[2],maskCurrent[2],
maskCurrent[3],maskCurrent[3],maskCurrent[3],maskCurrent[3],
)
// step 4
__mm128i maskInv = _mm_subs_epu8(_mm_set1_epu8(255), mask)
//Todo : Multiply, with saturate - find correct instructions for 4..6
//note you can use Multiply and add _mm_madd_epi16
alpha = *maskCurrent;
red = (srcByteTop[R] * alpha + srcByte[R] * (255 - alpha)) / 255;
green = (srcByteTop[G] * alpha + srcByte[G] * (255 - alpha)) / 255;
blue = (srcByteTop[B] * alpha + srcByte[B] * (255 - alpha)) / 255;
CLAMPTOBYTE(red);
CLAMPTOBYTE(green);
CLAMPTOBYTE(blue);
resultByte[R] = red;
resultByte[G] = green;
resultByte[B] = blue;
//----
// Step 7 - store result.
//Store aligned if output is aligned on 16 byte boundrary
_mm_store_si128(reinterpret_cast<__mm128i*>(resultByte), result)
//Slow version if you can't guarantee alignment
//_mm_storeu_si128(reinterpret_cast<__mm128i*>(resultByte), result)
//Move pointers forward 4 places
srcByte += bytepp * 4;
srcByteTop += bytepp * 4;
resultByte += bytepp * 4;
maskCurrent += 4;
}
}
要了解哪些 AMD 处理器将运行此代码(当前使用 SSE2 指令),请参阅维基百科的 AMD Turion 微处理器列表 http://en.wikipedia.org/wiki/List_of_AMD_Turion_microprocessors。您还可以查看 Wikipedia 上的其他处理器列表,但我的研究表明,大约 4 年前的 AMD cpu 都至少支持 SSE2。
您应该期望良好的 SSE2 实现的运行速度比当前代码快大约 8-16 倍。这是因为我们消除了循环中的分支,一次处理 4 个像素(或 12 个通道),并通过使用流指令提高缓存性能。作为 SSE 的替代方案,您可以通过消除用于饱和的 if 检查来使现有代码运行得更快。除此之外,我还需要对您的工作负载运行分析器。
当然,最好的解决方案是使用硬件支持(即在 DirectX 中对问题进行编码)并在显卡上完成。