Использовать SSE - начать со страницы 131.
Основной рабочий процесс
Загрузка 4 пикселей из src (16 1-байтовых чисел) RGBA RGBA RGBA RGBA (потоковая загрузка)
Загрузить еще 4, которые вы хотите смешать с srcbytetop RGBx RGBx RGBx RGBx
Сделайте немного, чтобы член A в 1 заполнял каждый слот I.e
xxxA xxxB xxxC xxxD -> AAAA BBBB CCCC DDDD
В моем решении, приведенном ниже, я решил вместо этого повторно использовать существующий массив «maskcurrent», но интеграция альфа в поле «A», равное 1, потребует меньше нагрузки из памяти и, следовательно, будет быстрее. Swizzling в этом случае, вероятно, будет: И с маской для выбора A, B, C, D. Сдвиг вправо 8, Или с оригиналом, сдвиг вправо 16 или снова.
Добавьте приведенное выше к вектору, в котором все -255 в каждом слоте
Умножьте 1 * 4 (источник с 255-альфа) и 2 * 3 (результат с альфа).
Для этого вы должны быть в состоянии использовать инструкцию SSE2 «умножить и отбросить младшие 8 битов».
сложите эти два (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 в Википедии . Вы также можете посмотреть другие списки процессоров в Википедии, но мое исследование показывает, что процессоры AMD, выпущенные около 4 лет назад, поддерживают как минимум SSE2.
Вы должны ожидать, что хорошая реализация SSE2 будет работать примерно в 8-16 раз быстрее, чем ваш текущий код. Это связано с тем, что мы исключаем ветви в цикле, обрабатываем 4 пикселя (или 12 каналов) одновременно и повышаем производительность кэша с помощью потоковых инструкций. В качестве альтернативы SSE вы, вероятно, могли бы сделать ваш существующий код намного быстрее, исключив проверки if, которые вы используете для насыщения. Помимо этого мне нужно будет запустить профилировщик для вашей рабочей нагрузки.
Конечно, лучшее решение - использовать аппаратную поддержку (т.е. код вашей проблемы в DirectX) и сделать это на видеокарте.