http://zx-pk.ru/showpost.php?p=771519&postcount=117
http://zx-pk.ru/showpost.php?p=771698&postcount=119
Вид для печати
обнаружилось что рилтайм граф не на всех видеокартах рисовался, на лаптопе не рисовался, ломал голову почему, оказалось дело в rhwvalue для transformed color вершин.
Также заметил интересную особенность, Direct3D девайс запоминает дисплей на котором был создан и если перетянуть окно на другой дисплей с другой частотой, он продолжает обновляться с частотой другого видеоадаптера (на котором был создан). Нашел способ вычислить на каком адаптере сейчас работает эмулятор - его можно найти по имени дисплея, а имя дисплея можно вытянуть из hWnd. Это дает возможность получить частоту дисплея, но нужно еще придумать способ заставить Direct3D применить новую частоту.
Кстати люди пишут, что если через твики менять частоту дисплея, то DirectX дает неправильную частоту... Так что с этим тоже засада, похоже реальную частоту нужно только замерять... :)
Еще интересный факт - по графу теперь стало видно, что на каждой машине синхронизация от звука работает совершенно по разному :eek_std:
Вторым открытием стало то что Win 7 иногда чегото творит, что в результате кадры успешно и вовремя отправляются, но система их забывает показать, наблюдается обычно если чтото тяжелое начинает ворочаться на уровне ядра (антивирус)
PS: также был обнаружен и пофиксен баг с синхронизацией потоков в реализациях IHostKeyboard
а из неприятностей - Full Screen всегда работает на одном экране, даже не на том, на котором создан(Цитата:
девайс запоминает дисплей на котором был создан и если перетянуть окно на другой дисплей с другой частотой, он продолжает обновляться с частотой другого видеоадаптера (на котором был создан).
Зарелизил новую версию: 2.8.3.37808
Что нового:
- исправлена ошибка синхронизации потоков;
- добавлен рилтайм граф, отображающий время между сменой кадров и затраченного времени на обновление и отправку данных на видеокарту и звуковую карту;
- исправлено измерение fps (теперь для большей точности интервал измерения 1 секунда);
- исправлена заливка фона для индикатора fps (раньше на некоторых видеокартах его просто не было);
- исправлена синхронизация XNA4 хоста;
- исправлен сброс Z80 (USR 46578);
- исправлен порт IRB у AY8910 для PENTEVO (подвисало при переходе в 48К бейсик);
- исправлена эмуляция флага только чтение для HDD (теперь операция записи выполняется как положено, но запись на диск не происходит);
- исправлена обработка ошибок для лучшей совместимости с Linux;
- добавлена возможность закрыть консоль логов по Ctrl+C/Ctrl+Break;
- отключена кнопка закрытия у консоли логов, т.к. приводила к мгновенному закрытию эмулятора без сохранения данных (также консоль логов отключена по дефолту в log4net.config);
- масштабный рефакторинг кода практически завершен, теперь UI полностью отделен от движка, крупные изменения завершены (ну разве что Hardware часть будет вынесена в отдельную или несколько отдельных сборок), изменения теперь будут более точечными;
- также поменяласть структура имени типа при загрузке/сохранении конфигурации машины (в связи с масштабной сменой структуры);
- также, если нет конфига машины, то по дефолту теперь открывается конфигурация из machines.config, помеченная аттрибутом isDefault="true"
http://savepic.ru/6597201.png
Я смотрю идея с графом понравилась, в unreal тоже прикрутили :)
возился с синхронизацией видео, в конце концов попробовал синхронизацию просто от времени сделать и на ней к моему удивлению эмуль стал работать очень плавно и отзывчиво :)
Вот как выглядит граф при синхронизации по времени:
http://savepic.ru/6636814.png
частота кадров иногда дергается между 50.000 и 50.001, а так стоит как вкопанная :biggrin:
но к сожалению в этом режиме, также как и в спектакуляторе и unreal, происходит смена четных/нечетных кадров каждые 5-10 секунд. Что заметно в ZXTIME-15... Но плавность скролов и отзывчивость эмулятора с такой синхрой просто обалденная. Вобщем маст хев :) Буду добавлять синхру по времени (сейчас пока отладочный вариант -кривоватый). :smile:
да, а других источников точного измерения времени на PC вроде как и нету :)
Кстати заодно заменил поэлементное копирование массивов с графикой и звуком на kernel32 MemoryCopy. Если вывести время копирования на граф, то с достаточно заметного уровня оно упало практически до уровня, который на графе даже не виден :)
Проверял еще как эмит кода в рантайме на моно под линуксом работает - все ок :)
Проверял точками останова по условию в отладчике Adlers, я ему подсказал вариант с эмитом кода в рантайме для условных точек останова и он на удивление быстро этот вопрос разобрал и реализовал. код конечно требует переработки, но работает все на удивление быстро. например условие остановки AF==0005 на скорость эмуляции практически не влияет :)
Под моно в линуксе сейчас нет видео, поэтому проверял вслепую через встроенный отладчик Adlers, путем установки брейкпонита по обращению к памяти: br memread 0003
Все работает - после сброса вываливается в отладчик, т.е. эмит кода в рантайме под моно работает :)
Возможностей для оптимизации еще уйма :)
Обновил эмулятор до версии 2.8.4.37878:
- исправлен фулскрин на системах с несколькими дисплеями;
- заметная оптимизация производительности для video/sound rendering;
- оптимизация измерения fps;
- добавлен Time Sync (синхронизация кадров по времени);
- значительно улучшен VBlank Sync;
- Настройки меню View переработаны (добавлены пункты "Frame Sync Source" и "Video Filter");
- значительно уменьшена нагрузка на процессор для Time sync;
- значительно уменьшена нагрузка на процессор для VBlank sync;
- рефакторинг кода (CPU, Engine, Circuits, HDD emulation)
Основная фишка этого релиза - переработанная синхронизация видео/звука. Добавлена возможность синхронизации по времени. Качество VBlank Sync улучшено в разы.
Теперь при включении синхронизации от видео эмулятор не жрет процессор! :biggrin: При этом качество еще улучшилось.
Синхронизация по Video, теперь работает лучше чем во всех остальных эмуляторах и процессор при этом не жрет :biggrin:
Правда ресамплер пока заточен под 75 Гц дисплеи, на 60 Гц будут наблюдаться биения, нужно будет переделать.
http://savepic.su/4696998.png
В релизе замечен небольшой баг - после включения Max Speed, синхронизация сбрасывается на Sound, хотя отображается та что была выбрана. Фиксится выбором Frame Sync Source после каждого нажатия Max Speed. В следующем релизе исправлю.
думаю прикрутить запись видео в MP4, в отладочном варианте уже работает :)
Пишет в реальном времени очень даже неплохо и проц не особо жрет :)
Заметил, что при синхронизации от звука можно таскать окно с эмулем или заниматься иной активной деятельностью и эмуль не тормозит, а при синхронизации от видео или таймера - при таскании окна или иной активности в винде - сразу тормозит и заикается звук.
при синхронизации по VBlank, ScanLine используется как таймер с частотой FPS * DisplayMode.Height для прогнозирования времени которое осталось до VBlank. Когда время вычислено, если его достаточно, то делается Thread.Sleep :)
Кроме того момент VBlank засекается по системному таймеру и если на следующем ожидании кадра окажется что прошло время равное или большее чем frequency / FPS, то обновляется время последнего VBlank и производится немедленный возврат, чтобы попытаться успеть.
Идея с прогнозированием времени для Thread.Sleep подсмотрена в unreal :smile: Только в unreal для прогнозирования используется ненадежный RDTSC. А у меня системный таймер.
Может возникнуть проблема, если DirectX говорит одну частоту FPS, а на самом деле другая. Или если реальный FPS сильно отличается от той, которую дает DirectX. Тогда время неточно прогнозироваться будет. Думаю можно сделать адаптивный алгоритм для коррекции реального fps путем замера реального времени между VBlank.
Кстати интересный вопрос - можно как-то узнать количество ScanLines для текущего режима? Я сейчас тупо беру Height дисплея, что естественно не очень правильно, т.к. реальное число ScanLines больше Height, т.е. частота ScanLines вычисляется меньше реальной.
---------- Post added at 14:55 ---------- Previous post was at 14:45 ----------
я именно на ffmpeg и сделал, с помощью AForge.Video :smile: прикручивание заняло минут 15-20, включая загрузку библиотек :biggrin:
Проблемное место с видео - как писать видео, если меняется разрешение экрана. На лету разрешение видеофайла не поменяешь. Думаю в таких случаях можно начинать писать следующий файл после каждой смены разрешения, например test.mp4, test.1.mp4, test.2.mp4, test.3.mp4 и т.д. Или просто прекращать запись.
Я тоже читал, что в XP (или даже 98) дискретность 10мс, поэтому принудительно в эмуле ставил дискретность самую маленькую 1мс. И на всех виндах от XP до 7 это прокатывало, она была действительно 1мс.
И все же, если дискретность 1мс, не топорно ли засыпать на время с такой грубой дискретностью? Ибо можно промахнуться от начала VBlank на порядка 10%.
там запас 5 мс вычитается, оставшиеся 5 мс эмуль опрашивает VBlank и крутит Thread.SpinLock. Если частоту процессора правильно вычислить, то можно попробовать остаток времени в Thread.SpinWait отдавать, но это не очень надежно, т.к. частота может гулять плюс минус лапоть на всяких Turbo Boost и т.п. новомодных технологиях
не совсем, это цикл операций, в которых не задействуются вычислительные блоки процессора, таким образом они могут быть использованы другими потоками. Но при этом наш поток не переключается на чужие задачи.
Потестил на других машинах, оказалось не везде такая красивая картинка, на некоторых машинах довольно сильный разброс. Видимо там где time-slice довольно большой и Thread.Sleep со слишком большими квантами времени работает.
А есть способ уменьшить кванты времени на Win 7? в старых системах помню можно было какой-то апишный вызов сделать и система переходила в рилтайм режим с низким time slice
---------- Post added at 19:12 ---------- Previous post was at 19:10 ----------
я сам давно хочу его реализовать, но для этого нужно разобраться как оно работает :)
1) На сколько я знаю (может я отстал от новых процессоров) каждое ядро имеет свой набор вычислительных блоков.
2) Зачем нужен этот цикл? Какая от него польза, если это просто пустой цикл на заданное число итераций?
---------- Post added at 23:31 ---------- Previous post was at 23:05 ----------
В EmuStidio я делаю так:
Выполнять в realtime-приоритете процесса. В этом приоритете ни один другой процесс не может получить квант времени, пока вы его не отдадите по Sleep. Т.е. если ваш процесс его не отдаст, то система зависнет железно (во всяком случае с одним ядром, проверял на XP). Возможно, что на современных виндах такой приоритет можно получить только под админом, я не проверял. Альтернативный вариант - это приоритет чуть меньше - high priority. Но он не дает гарантий точного измерения.
Как известно, высота экрана в сканлайнах состоит из видимой части (которую нам сообщает система) и невидимой (VBlank части). Невидимая часть на LCD мониторах может быть равна 0 или 1. Для CRT-мониторов невидимая часть может быть несколько десятков сканлайнов.
Алгоритм таков:
1) Определяем видимую высоту экрана в сканлайнах через dwHeight
2) Ждем пока луч не дойдет до середины экрана. Делаем это так:
a) Sleep(1)
b) Считываем номер сканлайна
c) Если номер сканлайна меньше dwHeight/2, то цикл --> a)
Таким образом мы отдаем около 50% быстродействия системе, дабы система не тормознула во время дальнейших измерений, ведь мы в realtime-приоритете.
3) Далее определяем точку перехода через 0-й сканлайн:
a) Считываем номер сканлайна
b) Если номер сканлайна не меньше предыдущего, то цикл --> a)
Таким образом полная высота экрана будет равна максимальному номеру сканлайна плюс 1
Данный процесс я делаю несколько раз подряд, и проверяю, чтобы все 3 последовательных результата (за 3 кадра) были одинаковыми. Если так, то считаю измерение выполненным правильно.
Теперь об установки минимальной дискретности для Sleep (т.е. для переключения процессов). Я это делаю через timeBeginPeriod(1). Во всех системах это прокатывает. Какое получилось реальное разрешение таймера можно потом проверить так:
Код:TIMECAPS tc;
timeGetDevCaps(&tc,sizeof(TIMECAPS));
printf("Timer Period Min - %dms\n", tc.wPeriodMin);
На одном ядре обычно два потока исполняется, при этом они шарят между собой вычислительные блоки. Если нужный блок занят одним потоком, то второй простаивает или пытается выполнить другие инструкции, если это возможно до выполнения текущей. Кроме того ядра тоже шарят между собой какието блоки, например общий кэш. Просто цикл, даже из nop загружает блоки процессора и второй поток из-за этого тормозится. Плюс к этому время холостого цикла процессор может использовать для синхронизации кэша или для своих оптимизаций. Если выполнять тупой цикл, то процессор не получит такой возможности
Это не так, даже с рилтаймом система отобрает процессор для своих более важных задач. Да и зависаний никаких нет от рилтайма. Сегодня возился, пробовал и realtime+highest приоритет (это максимум) - синхра стабильнее, но видно что система отбирает процессор
---------- Post added at 23:00 ---------- Previous post was at 22:58 ----------
Смысл в том что раз уж берем блокируем поток на холостой цикл, то нужно хотябы поделиться блоками процессора с другими потоками и дать прцессору пооптимизировать свои кэши. Это лучше чем тупо жрать вхолостую ресурсы
Кстати если процесс сам себе приоритет ставит, то это очень плохо. Во первых пермиссии может не быть. Во вторых мы мешаем системе и юзеру управлять приоритетами процессов.
В тесте к эмулятору есть код который рилтайм ставит и этот код валится под линуксом из-за отсутствия пермиссии
Это не холостой цикл. Это аналог nop'а, только он дает процессору возможность использовать это время с пользой. В итоге операции на которые процессор бы всеравно потратил время позже, выполняются раньше, а мы не теряем поток.
---------- Post added at 23:13 ---------- Previous post was at 23:09 ----------
Это очень плохо, более того, если процесс сам пытается запрввлять своим приоритетом, то есть серьезный повод отказаться от использования такого процесса. Процесс может упрввлять только приоритетами своих потоков.
Для системы не реального времени (Windows) приходится идти на ухищирения, чтобы реализовать те фичи, которые она кошерными методами не предоставляет.
---------- Post added at 00:17 ---------- Previous post was at 00:16 ----------
Я понимаю, что это nop, холостой цикл и т.д.) Смысле его у тебя в эмуляторе какой?
Ты же в этом nop'e не можешь ничего делать. Не можешь задать nop на определенное время. Просто nop неопределенной длительности и ничего не делающий. Зачем?
если задача требует того чтобы забрать процессор в эксклюзивное пользование на длительное время, то задача плохая ее нужно переделать, чтобы она получала кванты времени именно тогда когда нужно, в этом и весь смысл этой борьбы. Тем более если это длительное время будет использовано для холостого цикла :smile:
В том-то и дело что это не nop, а специальный вызов который дает процессору понять что мой поток ничего не делает, а только ждет событи которое должно наступить раньше чем квант времени на переключение задач, поэтому отдать процессор другой задаче я сейчас не могу, но и ближайшие несколько тактов делать точно ничего не буду.
Процессор получает возможность использовать эти такты для выполнения задач по оптимизации, которые у него накопились или заюзать блоки моего ядра для более быстрого выполнения инструкций другим потоком, ну или на худой конец отключить часть блоков чтобы снизить потребление тока. Задать число таких тактов какраз можно. Только проблема в том что вычислить их проблематично, т.к. частота современных процессоров динамически меняется.
А если просто цикл крутить или nop выполнять, то это может помешать другим потокам выполниться более быстро, заблокирует процессору возможность обновить на этих тактах ячейки кэша и т.п.
Last Ninja Remix 2 на Скорпе с ПрофПЗУ после входа в монитор по NMI и выхода кажет черный экран. Потому что включена вторая экранная область. Сдается мне, проблема в чтении 1ffd и 7ffd. Или в ПрофПЗУ :) Игра 48-я, но при сохранении образа памяти ПрофПЗУ пишутся 256 Кб. Unreal ведет себя так же. По мотивам этой темы.
Как раз не на длительное время, а на короткие отрезки времени.
Моя цель в том, чтобы нужное время для эмуляции процессор гарантированно получил, выполнил всю работу а остальное время отдал системе. Если же приоритет на время выполнения необходимых эмуляторных задач будет недостаточный, то любая фоновая активность заставит твой (или мой, или любой другой эмулятор) заикаться в звуке или кадрах.
---------- Post added at 13:34 ---------- Previous post was at 13:32 ----------
Теперь я понял) Ты ждешь события. Что за событие?
Разве события могут приходить чаще, чем раз в квант (1мс?). Когда я экспериментировал с событиями от звукового буфера, они приходили именно так, с дискретностью в квант. А иной раз задерживались и приходили скопом по несколько штук даже.