Обновившись в конце апреля на новые драйвера AMD 16.4.2, я обнаружил, что все DirectX12-приложения перестали работать. Ничуть не удивившись, я решил подождать устранения проблемы и отложил DirectX12 в сторону. Но месяцы шли, а с новыми драйверами ситуация не менялась.

Гугл показал, что эта проблема носит массовый характер (раз, два, три, четыре), а 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

Ищем в конце секции кода незанятое место, забитое нулями. Там мы и будем писать наш код:

image

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

image

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

image

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

image

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

P.S. линк на модифицированные мною библиотеки для драйверов 16.9.1 от 13 сентября.
Поделиться с друзьями
-->

Комментарии (35)


  1. lolipop
    24.09.2016 04:49
    +7

    Вот это подход!


    1. 0xcffaedfe
      24.09.2016 06:20

      Это не подход, это костыль временная мера.
      Ибо мало того что следующий апдейт заставить патчить либо искать пропатченные либы фиг знает с какой встроенной заразой, так еще в один прекрасный момент amd может пересобрать дрова с поддержкой остальных инструкций из набора.


  1. VioletGiraffe
    24.09.2016 08:14
    +1

    Проц без SSE4.2 — это уже довольно-таки древность. Слабо понимаю, зачем такой машине поддержка ДХ12.


    1. Zifix
      24.09.2016 08:58
      +4

      Если поставить например копеечный E5450 в материнку на 775 и купить видеокарту среднего уровня — вполне себе жизнеспособный вариант получается.


    1. DuMOHsmol
      24.09.2016 11:58
      +6

      А ещё есть AMD Phenom II, в разогнанном виде они до сих пор неплохи.


      1. VioletGiraffe
        24.09.2016 13:36
        -7

        «неплохи» = «приложения запускаются и даже как-то работают»?


        1. strapony
          24.09.2016 14:17
          +2

          Неплохи = невооружённым глазом разницы с i5 на соседнем ПК в повседневном использовании не видно.


          На домашнем сервере, где тоже phenom II был, но на шесть ядер, разницу при переходе на Xeon заметил только в диспетчере задач — загрузка процессора упала с ~25-30% до 5-10% при полудюжине запущенных hyper-v машин.


          1. VioletGiraffe
            24.09.2016 14:20

            Если не загружен — тогда, конечно, не будет заметно. А я, например, её везде вижу. Начиная с игр и заканчивая временем компиляции проекта и временем обработки изображений в программе, над которой я работаю. Разница Core i7 с 8-ядерным Будьдозером — более 2 раз, так-то. И это старый Sandy Bridge.


            1. DuMOHsmol
              24.09.2016 15:30
              +2

              А что не так с играми? Doom играется, Deus Ex играется, оба упираются в мою видеокарту (GTX660), а не процессор. Понятно, что i7 будет намного лучше, но списывать со счетов камни без SSE 4.2 всё же ещё не стоит. Тем более, что DX12 вроде как раз и должен снижать нагрузку на процессор.


              1. VioletGiraffe
                24.09.2016 15:36
                -1

                А в играх просадки ФПС намного меньше с современными процессорами. Посмотрите, например, http://wccftech.com/intel-skylake-6700k-6600k-amd-fx-8370/


                1. Zifix
                  24.09.2016 15:47
                  +3

                  Позвольте, но далеко не каждый может себе позволить купить топовый i7 и R9 Fury Triple Dissipation.

                  Что им теперь, в игры не играть? Запускать что-то, требующее выше 10 DirectX даже не мечтать?

                  Покупают ведь люди и новые компьютеры с Core i3, и вполне себе радуются жизни, хотя они может быть тоже раза в два слабее «старого i7».


                  1. VioletGiraffe
                    24.09.2016 15:53
                    -1

                    Просадки до 15 ФПС чувствительны безо всяких Fury. А часто и до 25 ФПС (правильнее было бы, наверное, считать время задержки, если это фриз, а не переводить в ФПС). А если у компа средний ФПС такой, то он, пардон, совсем не игровой. На старой технике можно играть с пониженными настройками, но это совсем не то удовольствие. И не всегда помогает — одна из недавних игр на моей предыдущей карточке АМД 6950 была неиграбельна даже на минимальных и с внутренним масштабированием разрешения 50% (то есть 0.5 от экранного).


                    1. sumanai
                      24.09.2016 16:44
                      +4

                      но это совсем не то удовольствие

                      Как люди раньше без графона играли…


                      1. VioletGiraffe
                        24.09.2016 16:46
                        -2

                        Как люди раньше деревянными игрушками играли? А ещё раньше только и делали, что еду искали, не до игр было.


                        1. andrey_aksamentov
                          26.09.2016 08:06

                          Вам смешно, а я еще помню время когда у меня half life 1 тормозил… В те времена 25 фпс считалось вполне игровым режимом.
                          После обновления компа, еще некоторое время выставлял в драйвере принудительное ограничение в 25 кадров, было непривычно когда их больше 30…

                          Я почти на 90% уверен что комфортно можно и сейчас играть на 25кадрах, если не будет фризов и лагов, и это ограничение будет искусственным.


                          1. VioletGiraffe
                            26.09.2016 08:09

                            В принципе, да, хотя ещё зависит от движка. Для плавности на 25 лучше добавить немного motion blur, это здорово помогает.


                  1. dadyjo
                    26.09.2016 18:21
                    -1

                    У меня i3 3.8 ГГц производительности хватает, многопоточность для игр улучшает графику но fps проседает, это даже в примечании к настройке «Использовать несколько ядер» в игре написано.


  1. VBKesha
    24.09.2016 11:27

    Хорошая работа!


  1. Mpa3b_ru21
    24.09.2016 13:45

    dxdiag вылетает с ошибкой. но успевает показать, что DirectX ? 12.


    1. Bazanra
      24.09.2016 13:45

      С какой ошибкой?


      1. Mpa3b_ru21
        24.09.2016 18:36

        Имя сбойного приложения: dxdiag.exe, версия: 10.0.14393.0, метка времени: 0x57899b4a
        Имя сбойного модуля: amdxc64.dll, версия: 21.19.134.1, метка времени: 0x57d02449
        Код исключения: 0xc0000005
        Смещение ошибки: 0x0000000000035a0e
        Идентификатор сбойного процесса: 0x1e80
        Время запуска сбойного приложения: 0x01d216792db489b7
        Путь сбойного приложения: C:\WINDOWS\system32\dxdiag.exe
        Путь сбойного модуля: C:\WINDOWS\system32\amdxc64.dll
        Идентификатор отчета: f3e4b1e0-edb0-4f0b-8246-4a0f881523f9
        Полное имя сбойного пакета:
        Код приложения, связанного со сбойным пакетом:


        1. Bazanra
          24.09.2016 19:00

          Здесь что-то не так. Драйвер 16.9.1 от 13 сентября использует amdxc64.dll из DriverStore, а не из system32.


          1. Hanabishi
            25.09.2016 13:43

            Он же их копирует в System32 (и SysWOW64). DriverStore лишь хранилище, разве нет?


            1. Bazanra
              25.09.2016 13:45

              Раньше так и было, но начиная с одной из недавних версий это изменилось — теперь он использует их напрямую из хранилища, не копируя в System32/SysWOW64.


  1. Godless
    24.09.2016 15:16
    +2

    используйте сдвиги с переходом через cf и сложение с нулем и cf. ускорится все немного.


  1. BigW
    24.09.2016 16:37
    +6

    А почему jmp, а не call и ret? Тогда можно адреса возврата не высчитывать, а просто заменить инструкцию и выровнять код… Я так понимаю вы алгоритм Кернигана 3 раза вставили?


    1. Bazanra
      24.09.2016 19:13

      С call и ret мой промах, возьму на заметку.
      Алгоритм вставлял 6 раз — по 3 раза в x32 и x64 библиотеки. Как написать одну общую функцию (пока) не придумал — вызов popcnt занимает 9 байт, а на передачу только второго аргумента в стек уйдет 7 байт.


      1. BigW
        25.09.2016 13:26

        а, ясно. каждый раз разный аргумент, который [edi][edx]*4[00000010… Тогда, да… проще jmp…


  1. Deamhan
    24.09.2016 19:21
    +3

    Насчёт SSE4.2: всё не совсем так. POPCNT содержится и в SSE4A, а этот набор был уже в первом Phenom (2007 год, пруф: https://en.wikipedia.org/wiki/SSE4). Так что владельцам phenom/phenom II не о чем париться. Скорее всего амд включили не SSE4.2, а SSE4A.


  1. Ivan_83
    25.09.2016 02:20

    Как то так было бы быстрее:
    /* Returns: Population Count (Ones Count) = log2 = popcount. */

    static inline size_t
    u32_digit_bits(uint32_t digit) {
    register size_t n;
    register uint32_t reg = digit;

    if (0 == reg)
    return (reg);
    reg -= ((reg >> 1) & 0x55555555);
    reg = (((reg >> 2) & 0x33333333) + (reg & 0x33333333));
    reg = (((reg >> 4) + reg) & 0x0f0f0f0f);
    reg += (reg >> 8);
    reg += (reg >> 16);
    n = (reg & 0x0000003f);

    return (n);
    }

    Ещё пачка вариантов тут: https://habrahabr.ru/post/276957/

    То что вызывается оно всего 3 раза не значит что это происходит редко, может это самые нагруженные циклы.

    Я согласен что так делать очень не хорошо, могли бы сделать через cpuid детект и вызов соответствующей ветки кода на лету, как делают все приличные люди.

    У себя дома систему и все приложения вот только сейчас, при апгрейде до фрибсд11 пересобрал с sse 4.1 и то сомневаюсь, ибо теперь переткнуть винт/сколонировать и запустить систему на более простых системах не получится (есть дома такие), а вот профита не так чтобы заметно. Какие то оптимизированные вещи может и стали работать в 2-3 раза быстрее, но на общем фоне не заметно совсем.

    2 VioletGiraffe
    2 Deamhan
    У меня дома только один проц имеет поддержку SSE 4.2 и в нём же есть AVX — и тот самый бюджетный под ам1 сокет :)
    Но это ещё не самое страшное, ибо у меня штуки 3 относительно приличных проца в которых SSE4 вообще нет — имя им DualCore под 775 сокет, а SSE 4.1 в коредуо е8400/8500.
    И меня не напрягает ни разу на них работать, вполне достаточно.


    1. Bazanra
      25.09.2016 02:55
      +1

      При выборе алгоритма я рассмотрел еще больше вариантов (их тесты тут), и выбрал Кернигановский по той причине, что он очень прост и в ассемблерном коде занимает меньше всего места, а, значит вероятность допустить ошибку меньше.


      1. PsyHaSTe
        26.09.2016 17:26

        Всегда можно написать на С и скомпилировать. GCC в алгоритме любой сложности не ошибется. Лишь бы влезть в сегмент.


  1. theg4sh
    25.09.2016 20:26

    Bazanra и правильно сделал что пропатчил.
    У них явно что-то не так либо с разработкой, либо с тестированием. Год назад фиксил их драйвер для старенькой видяхи HD7770 под Linux (появились мелкие изменения в api ядра) и смотрю ситуация вообще не изменилась. Фикс был отправлен, но до выпуска официального патча прошло еще полгода.


    1. splav_asv
      25.09.2016 20:44

      Под Linux у них генеральное направление сменилось на AMDGPU и код прямо в ядре, обновляется соответственно отлично. А вот сил на перевод драйвера на работу с открытым модулем ядра видать ушло очень много и на старое железо слегка забили.


      1. theg4sh
        25.09.2016 22:28

        splav_asv, спасибо, в курсе. Драйвер amdgpu, имхо, хорошо себя показал. Только неприятный отпечаток все равно остался, да и в силу обстоятельств железку уже обновил.
        «Забивательство» AMD (да и других больших кампаний) на устаревшие девайсы, скорее всего, идет со стороны биснеса от вопроса «как стимулировать оборот?» и тут уже ничего не поделать, остается только поддержка сообществом.