При выводе движущегося рисунка - изображение иногда дёргается

Тема в разделе "WASM.GUI", создана пользователем TimaFt, 25 авг 2005.

  1. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    В той же книге дальше:
    Особенно система любит поднимать приоритет тредам где есть работа с оконными сообщениями, и они с успехом отберут кванты у синтезатора музыки :derisive:

    Нет нет, не нужно уходить в сторону :) Я говорю именно о музыке, которую нужно синтезировать. И делать это часто для огромного буфера очень накладно.

    KiReadyThread будет вызвена, а что дальше - зависит от многих условий (см. статью 90210 или сорцы). Впрочем, таймер так же приведёт к подобному, но асинхронно (читай: неизвестно когда :))
     
  2. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    О чём я и написал выше.

    Тогда пора задуматься об оптимизации синтезатора.

    Понятно, что в большинстве случаев WaitForSingleObject вернёт управление быстрее, чем что-то вроде:
    Код (Text):
    1. while(!event) Sleep(n);
    Но всё равно нет гарантии, что это произойдёт сразу, а не через неопределённое время, пропорциональное количеству тредов.

    Пока что мы имеем 2 пути решения проблем со своевременным заполненем буфера для аудио:

    1. Поднять приоритет треда. Заюзать интерфейс уведомлений, доступный в DX7. Таким образом мы добиваемся, чтобы наш тред выполнялся планировщиком как можно чаще, но транжирим процессорное время.

    2. Увеличить размер буфера, чтобы запас времени между вызовами треда-синтезатора был больше, чем фактический максимальный период между этими вызовами. Процессорное время при этом расходуется оптимально, но памяти потребляется больше, чем в п1. Если музыка меняется чаще, чем продолжительность одного полного фрейма, то часть заполненного буфера придётся отбрасывать и заполнять снова. Такая ситуация (очень интерактивное музыкальное сопровождение) возникает не часто, IMHO.
     
  3. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    То есть, сначала вводим искусственную проблему, а потома начинаем её решать. :)
    Нет, синтезатор некуда уже оптимизировать. Зато уменьшив буфер в 20 раз, в 20 снизим пиковые затраты на синтез.

    Я лишь предлагаю выбрать лучший вариант из того, что в наших силах. Гарантий нет, но их больше (особенно если поднять приоритет).

    Во-первых: поднять приоритет и использовать уведомления - это разные способы. Их можно применять одновременно, что бы улучшить результат.

    Во-вторых: выделенное не верно. Это расходится с практикой, и я даже не могу придумать хоть один теоретический довод в пользу этого. "А вам слабо?" :derisive:

    Опять же, про процессорное время - это не так.

    Часто или не часто, но высокий приоритет решает эту проблему. Какие проблемы он создаёт?

    То есть вопрос такой - есть ли хоть одна причина, почему высокий приоритет - плохо? Про время CPU повторюсь - мой синтезатор грузит проц на 1-2% (видимо т.к. он очень примитивный) точнее цифры назвать сложно, они сильно похожи на погрешность измерений :)
     
  4. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Допустим, я слушаю музыку и одновременно дизассемблирую большой файл в иде. Высокий приоритет треда с музыкой замедлит процесс дизассемблирования, возможно даже на порядок. В гипотетическом примере с игрой тоже не ясно, почему синтезатор должен отбирать время, которое можно с пользой потратить на расчёт каких-нибудь матриц для графики?

    По сути, тред-синтезатор первым делом заполняет буфер до отказа, потом он периодически проверяет не освободилось ли место в этом буфере и дозаполняет его новыми сэмплами. "Опорожнение" буфера происходит линейно (со скоростью, пропорциональной частоте дискретизации), а заполнение - нет, т.к. зависит от планировщика. Если планировщик включает синтезатор слишком часто, то КПД синтезатора понижается (overhead > полезного времени синтеза). Если же планировщик включает синтезатор слишком редко - повышается риск возникновения артефактов (зацикливания, шум и т.п.)

    Чем выше приоритет треда-синтезатора, тем менее кооперативным становится его процесс. Если учесть, что тред-синтезатор в основном занимается вычислениями и не вызывает никаких API, то кооперативность такого треда падает ещё больше.

    Предлагаю очень простой эксперимент:

    1 тред синтезирует фоновый музон (minifmod как раз использует TIME_CRITICAL).
    2 тред (основной) через таймер обновляет бегущую строку, или звёздное небо, или плазму/огонь... в общем, простейшая анимация. Частоту таймера специально задаём минимальной: порядка 10-20мс.

    Что у нас получается? Анимация дёргается и это заметно даже на моём P4 с HT.
     
  5. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    По какой причине произойдёт замедление? Количество тактов CPU, требуемое для синтеза музыки, не зависит от приоритета синтезатора.

    Может быть, проблема как раз и кроется в том, что он _проверяет_? Ведь это лишние действия, совершенно не нужные, т.к. есть механизм нотификаций.

    Было бы неплохо выразить это в цифрах. Впрочем - это не имеет никакого отношения к приоритету, а лишь к размеру буфера.

    И я даже скажу, почему так получится - синхронизировать вывод графики нужно другими методами, а не таймером. Этот тред, кстати, и был им посвещён с самого начала.
     
  6. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Но время то уходит на переключение задач. Т.к. я не использую уведомления (в WINMM их просто нет, а в DX они появились не сразу), то время уходит ещё и на проверку (типа поллинга) состояния буфера.

    Нотификации не устраивают из соображений совместимости. Да и кода больше получается, как ни странно. Справедливости ради нужно отметить, что _проверка_ занимает не так много мремени, т.к. выполняется асинхронно, но всё равно миллисекунды на неё уходят.

    Если бы всё было так просто, я бы уже вывел формулу для нахождения золотой середины.

    Тут вообще нет синхронизации специально что-бы показать пагубное влияние высокоприоритетных тредов. Юзерный таймер очень чувствителен к таким явлениям.
     
  7. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    Дык оно и так будет уходить - в системе несколько сот тредов и работающих несколько десятков. Не думаю, что переключение длится долше среднестатистического вызова API, коих делается много.

    Ну понятно - используешь не лучший способ, и отсюда проблемы :) У тебя же либа - можно было дать пользователь выбор, нужна ли ему поддержка хлама (win95).

    Да?
    Код (Text):
    1. // опрос
    2. void inline do_sound()
    3. {
    4.     while ( true )
    5.     {
    6.         unsigned long   write_position;
    7.         sound_buffer->GetCurrentPosition(0, &write_position);
    8.         if( soundbuffer_position <= write_position )
    9.             break;
    10.         sound_buffer->Play(0, 0, DSBPLAY_LOOPING);
    11.         Sleep(1);
    12.     }
    13.     void              * write_cursor;
    14.     unsigned long       size;
    15.     if ( sound_buffer->Lock(soundbuffer_position, soundbuffer_chunk_size, &write_cursor, &size, 0, 0, 0) )
    16.         sound_buffer->Restore();
    17.     else
    18.     {
    19.         copy_by_dwords(write_cursor, &local_buffer, size);
    20.         sound_buffer->Unlock(write_cursor, size, 0, 0);
    21.         soundbuffer_position += soundbuffer_chunk_size;
    22.         if ( soundbuffer_position >= soundbuffer_size )
    23.             soundbuffer_position = 0;
    24.     }
    25. }
    Код (Text):
    1. // события
    2. void inline AY_emulator::play_chunk()
    3. {
    4.     make_chunk();
    5.  
    6.     unsigned long cause = WaitForMultipleObjects(chunks, event, 0, INFINITE);
    7.     soundbuffer_position = (cause - WAIT_OBJECT_0) * soundbuffer_chunk_size;
    8.  
    9.     unsigned long     * write_cursor;
    10.     unsigned long       size;
    11.     if ( sound_buffer->Lock(soundbuffer_position, soundbuffer_chunk_size, (void **) &write_cursor, &size, 0, 0, 0) )
    12.         sound_buffer->Restore();
    13.     else
    14.     {
    15.         // Правый канал + левый = 2 + 2 байта - копируем двойными словами.
    16.         for( unsigned i = size/sizeof(long); i; --i )
    17.             write_cursor[i-1] = local_buffer[i-1];
    18.         sound_buffer->Unlock(write_cursor, size, 0, 0);
    19.     }
    20. }
    На что влияние-то? Если на мои треды - то там не будет таймера ;) Так же можно сказать - кривой тред с высоким приоритетом повесит систему. Но надо делать прямые, занимать проц льшь на короткие промежутки.
     
  8. S_T_A_S_

    S_T_A_S_ New Member

    Публикаций:
    0
    Регистрация:
    27 окт 2003
    Сообщения:
    1.754
    МИЛЛИсекунды - это ОЧЕНЬ много. Один кадр на LCD мониторе длится 16.7 миллисекунд (@60Hz)
     
  9. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    S_T_A_S_
    Имеет смысл в одном из следующих релизов опционально включить поддержку уведомлений. Добавил в TODO.

    А код, который создаёт и регистрирует этот самый event, тоже нужно учитывать. И получение интерфейса нотификаций: QueryInterface. А потом ещё SetNotificationPositions. Кода получается даже больше чем в 2 раза.

    Сравнительно.
     
  10. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Ещё немного поэкспериментировал с приоритетом треда-синтезатора и размером буфера. Если тред не будет вызываться достаточно часто, то большой буфер всё равно не спасёт положение. Вывод: S_T_A_S_ был прав.