PDA

Просмотр полной версии : Идея для авторов игр и дем (AY-плеер)



Barmaley_m
24.10.2011, 00:49
В играх, где на заднем плане присутствует AY-музыка, программист часто сталкивается с проблемой того, что плеер музыки неравномерно нагружает процессор. И в те моменты, когда высокая нагрузка на процессор от графических процедур совпадает с высокой нагрузкой от плеера музыки, можно наблюдать, как игра не укладывается в кадр, происходит срыв плавности движения.

С данной проблемой обычно боролись путем оптимизации плеера музыки, попытками сделать нагрузку от плеера на процессор более равномерной. Уже в плеере PT2 авторы отрабатывают новую ноту на каждый канал не одновременно, а в трех разных фреймах, сохраняя промежуточные результаты. Эти и другие подобные меры хоть и улучшают ситуацию, но все равно не могут обеспечить полностью равномерной нагрузки на процессор. Кроме того, указанные меры часто приводят к повышению общей нагрузки на процессор из-за усложнения плеера, хранения и пересылки промежуточных величин и т.д.

Предлагаю вашему вниманию способ, который, практически не увеличивая среднюю нагрузку на процессор; с минимальными затратами памяти, позволяет решить описанную проблему. Способ универсальный, его можно применить к любому существующему плееру, т.е. разрабатывать новый плеер не требуется.

Идея в том, чтобы организовать очередь из AY-таблиц. Обычно в каждом плеере есть одна таблица, куда плеер складывает значения для последующего вывода в регистры AY. А мы организуем несколько таких таблиц. Например, штук 5. До начала анимации вызываем плеер 5 раз - в результате в очереди будут подготовлены значения для вывода в AY на 5 кадров вперед.

Теперь рассмотрим типичную структуру игрового движка. По прерыванию движок первым делом выводит в AY содержимое AY-таблицы, которое стоит первым в очереди. Это занимает немного времени, и нагрузка на процессор одинакова каждый кадр. Вывод в AY в первую очередь по прерыванию необходим для того, чтобы содержимое регистров AY обновлялось равномерно во времени, а не в зависимости от нагрузки на графический движок.

После обновления регистров AY работает графический движок, который синхронизирован с видео-разверткой. Как правило, нагрузка на процессор от графического движка неравномерна. В идеале она близка к 100% (чтобы возможности процессора полностью использовались для пользы), однако на практике так бывает редко, и нагрузка может колебаться в районе от 60 до 100%.

После отработки графического движка обычно вызывается плеер музыки. При этом, при традиционном подходе, если отработка графики + музыки не уложилась в кадр до следующего прерывания, то происходит потеря кадра, срыв. Однако в нашем случае происходит следующее. Плеер музыки выполняется с разрешенными прерываниями. Поэтому прерывание останавливает исполнение плеера. Процедура обработки прерывания первым делом, как описано выше, выводит в AY содержимое очередной таблицы. В описываемой ситуации плеер не успел подготовить новые значения (так как его выполнение было прервано). Однако у нас же очередь, в которой ждут готовыми целых 5 таблиц AY! Поэтому процедура обработки прерывания спокойно выводит в AY содержимое следующей таблицы из очереди. После этого она отрабатывает графику. А после отработки графики происходит возврат из прерывания, и прерванный код плеера продолжает работу. Длина очереди AY-таблиц в 5 штук обеспечивает то, что даже при одновременном неблагоприятном стечении обстоятельств (высокая нагрузка на процессор от графики за несколько кадров подряд), не будет нарушена ни плавность анимации, ни воспроизведение музыки. При необходимости длину очереди можно увеличить, благо это не затратно по памяти.

А когда нагрузка на процессор от графического движка уменьшится, то произойдет вызов музыкального плеера сразу несколько раз подряд, что обеспечит восполнение сократившейся очереди воспроизведения. Очередь воспроизведения лучше всего организовывать в виде циклического буфера, аналогично буферу клавиатуры. Пример реализации кода с разрешенными прерываниями (длина очереди - 8):

QUEUE_FULL:
HALT
MAINLOOP:
LD A,(BUF_WR_POS) ;указатель конца очереди (куда добавляются элементы)
LD C,A
INC A
AND 7
LD B,A
LD A,(BUF_RD_POS); указатель начала очереди (откуда извлекаются элементы)
CP B
JR Z,QUEUE_FULL; очередь заполнена - ждать освобождения, что произойдет в процедуре обработки прерывания
; в очереди есть место - вызвать плеер
PUSH BC
CALL MUSIC_PLAY
POP BC
; увеличить указатель записи в очередь
LD A,B ; B - указатель конца очереди после увеличения на 1
LD (BUF_WR_POS),A
JR MAINLOOP

В процедуре обработки прерывания вызывать музыкальный плеер не нужно. Нужно только извлекать очередной элемент из очереди и выводить в AY содержимое таблицы, а также вызывать графический движок:
ISR:
PUSH AF
PUSH BC
LD A,(BUF_WR_POS)
LD C,A
LD A,(BUF_RD_POS)
CP C
JR Z,QUEUE_EMPTY; Очередь пуста - пропустить очередной вывод в AY
; А - место в очереди, откуда следует осуществить AY-вывод
PUSH AF
CALL AY_TRANSFER ; Вывести в регистры AY первый элемент очереди
POP AF
INC A
AND 7
LD (BUF_RD_POS),A
QUEUE_EMPTY:
CALL GRAPHICS_ENGINE
POP BC
POP AF
EI
RET

Titus
24.10.2011, 00:59
Дело в том, что флуктуации нагрузки на процессор в хорошо оптимизированных AY-плейерах очень малы. Эффект был бы заметнее, если б плейер кушал десятки тысяч тактов с неравномерностью нагрузки до нескольких тысяч тактов. А современные плейеры занимают по 1-2 тысяче тактов, и при этом весьма стабильны. Т.е. идея хорошая, но мало-применимая, имхо.

Barmaley_m
24.10.2011, 02:48
А современные плейеры занимают по 1-2 тысяче тактов, и при этом весьма стабильны. Т.е. идея хорошая, но мало-применимая, имхо.
Даже если плеер нагружает процессор равномерно - то его процедуру надо вызывать 1 раз в кадр - не больше, но и не меньше. А мой подход позволяет иногда (в самых напряженных кадрах) отказаться от вызова плеера и использовать все процессорное время только на графику, при этом без ущерба для музыки. Вот в чем принципиальное преимущество!

Кроме того, если плеер нагружает процессор равномерно - то на обеспечение этой равномерности, скорее всего, затрачены какие-то ресурсы, которые можно высвободить, если не требовать от плеера равномерности.

Vitamin
24.10.2011, 07:27
Имхо, идея имеет право на жизнь. Плюс опыт разработки многопоточной программы (классическая схема писатель-очередь-читатель) с использованием примитивов синхронизации.

Barmaley_m
24.10.2011, 09:41
Плюс опыт разработки многопоточной программы (классическая схема писатель-очередь-читатель) с использованием примитивов синхронизации.
Я просто не хотел усложнять описание привлечением этих страшных терминов - все понятно и без них. Однако да, так и есть. Мой способ есть реализация вытесняющей многозадачности. Программа работает в 2 потока, один имеет высокий приоритет (графика и вывод очереди в AY), второй поток имеет низкий приоритет (плеер). Примитивы синхронизации не используются, но используется очередь в виде циклического буфера - это один из немногих известных примитивов для безопасного обмена данными между потоками, не требующий блокировок (типа запрещения прерываний, Spinlock и т.д.). Так что да, теоретическое обоснование у нас не отстает :)

newart
24.10.2011, 10:30
А современные плейеры занимают по 1-2 тысяче тактов
Современные это какие?

Плеер Vortex Tracker'a заточен под размер, и кушает 2-5к тактов.

Titus
24.10.2011, 11:32
Современные это какие?

Плеер Vortex Tracker'a заточен под размер, и кушает 2-5к тактов.
Я их не пользую, не могу сказать. Но даже я писал в 95 году плейер для ST в 3000 тактов. А после меня писали и того меньше.

psb
24.10.2011, 11:42
забыли, кажется, что некоторые плееры вовсю используют стек и нельзя их прерывать прерыванием...

jerri
24.10.2011, 12:35
Barmaley_m, Хорошая годная идея

Давай сорцы плеера и игралки

---------- Post added at 12:35 ---------- Previous post was at 12:23 ----------


забыли, кажется, что некоторые плееры вовсю используют стек и нельзя их прерывать прерыванием...

Значит надо переписывать плеер, раз он такой неудачный

Destr
24.10.2011, 12:39
Значит надо переписывать плеер, раз он такой неудачный
И переписать так, чтоб он был пусть и побольше размером, но чтоб всегда и везде одно и тоже количество тактов занимал. Хоть тресни планета, но кол-во тактов плеера = const.

Barmaley_m
24.10.2011, 20:44
Если плеер использует стек не по назначению - значит его использовать в такой схеме нельзя. Но, к счастью, таких плееров меньшинство. Некоторые, думаю, можно адаптировать, если кому-то сильно надо. А тебя, Destr, я не понимаю. ЗАЧЕМ нужно делать такой плеер, чтобы занимал постоянное кол-во тактов? Я как раз в теме веду речь о том, что это требование можно снять.

---------- Post added at 18:44 ---------- Previous post was at 18:42 ----------

Jerri, "сорцы плеера и игралки" - это типа как "дай мне листик и бумажку"? ;)

jerri
24.10.2011, 20:57
нет конечно
но если возьмешь плеер от PT3 и переделаешь его на неиспользование стека то будет хорошо

Barmaley_m
24.10.2011, 22:18
А что, даже плеер PT3 написан так некультурно? Ндааа, как все запущено.

Нет, браться за такие вещи я не буду, потому что в сутках только 24 часа. Могу только дать исходник моего плеера PT2, который использует стек по назначению, а также не использует самомодифицирующийся код. Когда-то я разместил этот плеер в ПЗУ, а прерывания шуровали с частотой 1000Гц, причем это были NMI. И все работало как часы.

Barmaley_m
24.10.2011, 22:26
Вот исходник моего плеера PT2. По размеру он меньше, чем оригинальный плеер. По тактам в максимуме больше, как в среднем - не проверял.

GriV
15.01.2012, 09:10
Пропустил я тему как-то.

В демах "диафильм" и "руки вверх" я использовал именно такой метод.
Только 8 сырых данных мне мало было :) Генерилось почти 32 кб сырых данных в кольцо, лежащее в основной памяти, чтобы во время произношения фонем не было глюков с музыкой. Точнее, чтобы данные в бипере не сильно портились задержкой в воспроизведении (при паузах появляются щелчки в бипере, чем больше пауза, тем больше щелчки, сейчас там слышно просто лёгкий треск - около 500 тактов на прерывание).
Я не осилил стандартный плеер, оставил стековые операции, из-за этого он вызывался только 6 раз в прерывание (в случае отсуствия других задач, конечно), что, впрочем более чем хватало.

Destr
15.01.2012, 12:54
Вообще всем шарящим людям собратся-бы да и написать наконец-то адекватный плеер:
Чтобы тактов мало жрал.
Чтобы громкость на лету.
Чтобы мог играть что надо, но и если что - другое (разные треки)

И в принципе (если уж pt3 формат так убог) - преутилиту замутить, которая в нужный вид приведёт (только не надо раскрытия регистров, плз!).

Чтобы для плеера, значит.
И пускай музыкальный файл после этого вспухнет ну скажем в 2 раза.
Пусть.
Памяти у нас с избытком, а вот проц Z80 как был тормозом, так и остался.
Так что ПЛЕЕР БЫСТРЫЙ НУЖЕН ВСЕМ!
(и пох если с пресэмплингом, главное - скорость!)

GriV
15.01.2012, 17:43
Отправляю сюда (http://zx.pk.ru/showthread.php?t=5335) и сюда (http://zx.pk.ru/showthread.php?t=14307) в частности. Просьба пользоваться подсказками по разделам.