Обновившись в конце апреля на новые драйвера AMD 16.4.2, я обнаружил, что все DirectX12-приложения перестали работать. Ничуть не удивившись, я решил подождать устранения проблемы и отложил DirectX12 в сторону. Но месяцы шли, а с новыми драйверами ситуация не менялась.
Гугл показал, что эта проблема носит массовый характер (раз, два, три, четыре), а AMD никак не реагирует. Пользователь форумов AMD tapek путем дебаггинга выяснил, что проблема заключается в использовании новыми версиями драйверов инструкции popcnt из набора SSE4.2.
Загрузив одну из проблемных библиотек (amdxc32.dll) в Hiew, поиском по опкоду инструкции popcnt — F3 0F B8 обнаруживаем, что она вызывается аж целых три раза! Это значит, что она не сильно там нужна и можно придумать ей замену. Эта инструкция возвращает первому аргументу количество единичных бит второго аргумента.
Для замены popcnt возьмем алгоритм Брайана Кернигана (Brian Kernigan/Kernighan).
На С++ он выглядит так:
На асме так:
Ищем в конце секции кода незанятое место, забитое нулями. Там мы и будем писать наш код:

Находим в библиотеке вызов команды popcnt:

И заменяем его на переход на наш код:

В ранее найденном месте пишем наш код и возвращаем управление туда, откуда взяли

После чего повторяем вышеописанное с оставшимися вызовами команды popcnt как в этой библиотеке, так и в amdxc64.dll, подменяем ими оригиналы и получаем снова работающий DirectX12 без SSE4.2.
P.S. линк на модифицированные мною библиотеки для драйверов 16.9.1 от 13 сентября.
Гугл показал, что эта проблема носит массовый характер (раз, два, три, четыре), а AMD никак не реагирует. Пользователь форумов AMD tapek путем дебаггинга выяснил, что проблема заключается в использовании новыми версиями драйверов инструкции popcnt из набора SSE4.2.
Загрузив одну из проблемных библиотек (amdxc32.dll) в Hiew, поиском по опкоду инструкции popcnt — F3 0F B8 обнаруживаем, что она вызывается аж целых три раза! Это значит, что она не сильно там нужна и можно придумать ей замену. Эта инструкция возвращает первому аргументу количество единичных бит второго аргумента.
Для замены popcnt возьмем алгоритм Брайана Кернигана (Brian Kernigan/Kernighan).
На С++ он выглядит так:
int kernigan(int value){ int count = 0; while(value != 0){ value &= (value-1); count++; } return count; }
На асме так:
push ebx push ecx xor eax,eax mov ebx, value kernigan_start: cmp ebx, 0 jz kernigan_end add eax, 1 mov ecx, ebx sub ebx, 1 and ebx, ecx jmp kernigan_start kernigan_end: pop ecx pop ebx retn
Ищем в конце секции кода незанятое место, забитое нулями. Там мы и будем писать наш код:

Находим в библиотеке вызов команды popcnt:

И заменяем его на переход на наш код:

В ранее найденном месте пишем наш код и возвращаем управление туда, откуда взяли

После чего повторяем вышеописанное с оставшимися вызовами команды popcnt как в этой библиотеке, так и в amdxc64.dll, подменяем ими оригиналы и получаем снова работающий DirectX12 без SSE4.2.
P.S. линк на модифицированные мною библиотеки для драйверов 16.9.1 от 13 сентября.
