Спрайт и Тайл — думал это одно и тоже. Представим себе массивы 32 x 24 таких массивов 3 штуки.
char layer[3][32*24];
Я перешел на Си, что было понятно о чем я. Это не сам спрайт, разумеется. Но, ячейка массива содержит число от 0 до 255, являющееся индексом (idx) спрайта. До спрайта мы еще не добрались, так как лежит он в каком то линейном участке памяти по смещению idx*sizeof(sprite). Если спрайт 32х32 8 бит (говорю «если» потому что уже не помню точно что там было) цвета каждой точки то его sizeof = 1024 байта или 1Кб. Вопрос доступны ли они все в пространстве процессора? Конечно нет, так как даже 64 таких спрайта займут все адресное пространство к примеру 6502. Но железке на это наплевать так как считанный адрес спрайта, например address = layer[0][idx] << 10, это просто шина нужной разрядности с которой адрес приходит на ША блочного ОЗУ ПЛИС. Конечно там начинается сложность с тем что развертка по строке движется линейно по экрану, переходя с одного спрайта на другой и поэтому часть адреса постоянна, а часть обновляется счетчиком — но не об этом сейчас. И так у нас три канала по которым летят данные из области ОЗУ с битмапом спрайта, с его непосредственным содержимым. Эти три канала D0,D1,D2 мы можем перемешать по принципу встреченного байта прозрачности (для простоты просто нуля). Если в D0 в данном такте появился 0 то значит нужно выдать ARGB из канала D1, но если и в канале D1 у нас тоже прозрачный пиксель (ноль), то берем ARGB из канала D2. Это все довольно просто описывается в HDL, не в ЯВУ.
Имея такую аппаратную примочку мы освобождаем процессор только от маскирования потока из областей спрайтов и необходимости постоянно лить данные спрайтов, когда они меняются на экране, в фрейм-буфер. Для Спрайтового движка понятия фрейм-буфер я считаю не применимо. Это по сути текстовый экран только буква не 8х8 а 32х32 и летит не с знакогенератора (который может быть и в ПЗУ), а из области ОЗУ со спрайтом. Мы меняем код спрайта в этом слое и в течении 12 тактов, без каких либо затрат на пересылку байт битмапа 32х32 (а это прилично!) получаем смену изображения. Все что нам нужно это просто загодя (за 12 тактов раньше) начать формировать растр по строке. Так как данные будут сыпаться в этой конвейер друг за другом, перерывов между ними не будет и работать можно с дичайшим FPS меняя спрайты всего экрана чуть ли не 100 раз за кадр. Заменить 768 байт на новые — это не вопрос даже для 6502 с 1,75МГц клока.
Но… грустно становится когда нам надо произвести скролинг внутри знакоместа, ну кого к примеру устроит резкий прыжок танчика на 32 точки вперед? И вот тут бывает так что танчик то не 32х32, а нечто побольше. Мало того хотелось бы двигать его не только прямо, а скажем по диагонали или вообще под произвольным углом. И мы начинаем копировать байты внутри спрайтовой области ОЗУ. А поскольку плана у нас три, и мы хотели бы что то еще двигать на задних планах, траву, кроны деревьев, какие либо еще произвольные объекты. То увы даже загнав имеющуюся корку 6502 на 50МГц (выше не получалось) удается разве что 20FPS обеспечить. Разговор конечно не про один объект, а про кучу различных. И даже 6809 выдавал до 30FPS на сценах, как ниже. Не из за одного «танчика», конечно это все затевалось. У меня к сожалению ничего продемонстрировать не осталось из того проекта, так как уже на симулях стало понятно — что это все не то. Но можно показать к чему пришли.
В итоге это все вылилось в фрейм-буфер 1024х768х8бит с двойной буферизацией, и то что там применено в качестве процессора :) отдельная песня — вывозило только 75% заполнения экрана. Но вот даже такая демка, которую для этой аппаратуры писал мой товарищ — 8 битные корки и даже 16 битные корки процессоров ложило наглухо. А это наверное 10%-20% имеющейся пропускной. Demo clear render
Такое ощущение, что тут речь не про спрайты как они преимущественно понимаются — массив имеющих независимые координаты объектов на экране где мы выбираем какие тайлы в спрайтах отображаются, но их количество как правило не покрывает все пиксели экрана и главное что они все могут быть выведены хоть в одну координату оставив остальной экран пустой. Тут же больше похоже что речь про тайловые фоны — квадратно-гнездовые сетки из тайлов иногда аппаратно скроллируемые, но как одно целое. Они по определению всегда покрывают весь экран и образуют квадратно-гнездовую сетку. В последнем случае становится понятно почему нужно попиксельно возится со скроллингом «спрайтов» в памяти спрайтов. Но даже если и так, то всё-равно не очень понятно что тут 8-битные процессоры не вывозили — если выводом такой графики заведует отдельная микросхема то они хотя бы небольшое количество тайлов и не вижу куда бы тут танчики не влезли — подвижных там не очень много… Не кажется чем то сверхъестественным.
т.е. стандартно мы бы накладывали поверх спрайт скажем с травой, спрайт с дорогой, поверх которой накладывался бы «танчик». Т.е. все три спрайта находятся в своих пространствах по одной координате X,Y. Но маскируются друг другом с приоритетом по каналу. Канал 1 перекрывает канал 2, канал 2 перекрывает канал 3, пиксель с цветом «прозрачный» может вылезти на канал 1 из любого канала. Наверное я все еще сильней запутал, но смысл очень простой. Есть три экрана спрайтов заданных байтом 32х24 знакоместа. Это три независимых плана, которые контроллер объединяет в один видимый на экране монитора. Спрайты находятся в своих областях ОЗУ как объекты 32х32х256. Прозрачный цвет не маскируется (допустим 0x00) и через него мы можем увидеть любой пиксель спрайта нижнего плана (2,3).
Спасибо за статейку! Читать такие вещи очень интересно, на хабре читал про 6809 с огромным интересом и продолжение про apple вполне себе дополняет. Особенно интересно было узнать что у 65С816 есть такие инструкции и DP.
Как то с другом мы обсуждали возможности в разрезе Clear reander engine vs Sprite/Tile Engine. Ну и закусились на тему, а что было бы если бы у бабушки были бы… если бы было три спрайтовых плана с приоритетом отрисовки (подобие Z-буфера). Было бы мол тогда интересней программить в ЯВУ те же танчики? Ну сказано сделано, примерно недели две возни с верилогом и готовый адаптер для отрисовки такой графики появился. Повозится конечно пришлось, что бы выйти на времянки 1024х768 (ЕМНИП). 12 стадий в конвейере устройства, причем регистры на стадии в ФУ конвейера, вовсе нет — простенькие мультиплексоры даже пришлось огораживать. Какое то время мне казалось что я не вывожу на этой ПЛИС задачу и как бы вообще на голую шину не пришлось ставить регистры в отрезки между ФУ. Но нет обошлось 12 стадиями. Жуткое чудо юдо.
Да, но это я к тому что вот это вот убер устройство хотели повесить на корку 6502, ну и оно не потянуло. Потом я рассматривал 6809 и тоже нет, не потянуло :) Потому что, если бы только проблемы отрисовки спрайтов поверх других спрайтов с аппаратным перекрытием, а ведь еще и скроллинг этих спрайтов попиксельно в памяти самих спрайтов. После этого как то утвердился в мысли о том что такого рода аппаратура мало что даст для программера, нужен именно прокаченный процессор и желательно такой который может за такт не одну инструкцию, и при это еще бы и асинхронно.
По ссылке там есть еще вторая часть статьи где описывается трюк с туманом. Я глубоко не вчитывался, так поверхностно текст просканировал глазами и похоже что какие то трюки с лукап-таблицами и вроде тоже непростые.
Ссылку на пост на Хабре мне прислали товарищи, и хотя читать её было весело, по сути ничего нового для квалифицированного Z80 кодера там не сообщалось. А вот этот трюк со вторым планом я пока нигде не встречал, спасибо за пересказ. Нужно будет прочесть первоисточник целиком, похоже что там у ребят всё очень благополучно с креативным мышлением.
Последний билд у меня тоже вылетает, но то что я выложил работает.
Можно еще вот что попробовать — уже после запуска подпапка CPSSoftware содержит ini-файлы создаваемые в момент запуска. В моей сборке там есть пути из моего компьютера. Удалить всю папку и попробовать снова.
хоть из-за неё и страдает быстродействие, но действительно достаточно элегантно и просто можно расширять схему
Имхо они просто попали в «ловушку легаси».
Первые калькуляторы на арифметике +-*/ делались чисто под эти операции — BCD-логика разряд за разрядом — дешевая память в виде shift registers просто просилась в такие алгоритмы. А дальше уже остановится не могли усложняя раз за разом на базе предыдущего опыта.
Спасибо за статью, и особенно за ссылку в комменте на статью Фролова на хабре!
У меня в детстве был МК-61, и я даже неплохо по нему упарывался, но честно говоря никода даже не задумывался как он реализован внутри.
Думал, что в микрухах просто обычные элементы зашиты. А оказалось, что всё по серьёзному и современному — микрокод и всё такое.
Так же интересная архитектура с этим кольцевым буффером, хоть из-за неё и страдает быстродействие, но действительно достаточно элегантно и просто можно расширять схему.
В SNES и N64 местные чипы региональной защиты тоже сделаны на микроконтроллере Sharp SM-5. Также он использовался во множестве LCD-игр не от Nintendo (Tiger, Konami).
Поход на 6502 уже выразился тут в моих статья про программирование на NES/Famicom/Денди: hype.retroscene.org/blog/967.html :)
Кстати на nesdev.com вчера (с этой же статьи по сути) мне рассказали, что похожий чип Sharp SM-59x трудился в NES в чипе региональной защиты CIC в американских картриджах. Т.е. Nintendo с Sharp так сказать совсем даже не прекратила отношения по этой линии тогда. :)
Вот так сукины дети прикрутили CRC вместо счетчика. Так то оно конечно и быстрей и меньше ресурсов сдвигать и хорить. Ай да сукины дети! Примерно такая же идея мне пришла по поводу расчета CRC ключевых слов Форта или Бейсика, для того что бы не проводить поиск по токену, а иметь всегда уникальное смещение в результате расчета по токену из строки полинома CRC-16, да можно даже CRC-8 :)
P.S. Спасибо за статью! Порадовали. Где то в комментах проскакивало, что Вы собираетесь пойти крестовым походом на 6502, буду рад почитать.
char layer[3][32*24];
Я перешел на Си, что было понятно о чем я. Это не сам спрайт, разумеется. Но, ячейка массива содержит число от 0 до 255, являющееся индексом (idx) спрайта. До спрайта мы еще не добрались, так как лежит он в каком то линейном участке памяти по смещению idx*sizeof(sprite). Если спрайт 32х32 8 бит (говорю «если» потому что уже не помню точно что там было) цвета каждой точки то его sizeof = 1024 байта или 1Кб. Вопрос доступны ли они все в пространстве процессора? Конечно нет, так как даже 64 таких спрайта займут все адресное пространство к примеру 6502. Но железке на это наплевать так как считанный адрес спрайта, например address = layer[0][idx] << 10, это просто шина нужной разрядности с которой адрес приходит на ША блочного ОЗУ ПЛИС. Конечно там начинается сложность с тем что развертка по строке движется линейно по экрану, переходя с одного спрайта на другой и поэтому часть адреса постоянна, а часть обновляется счетчиком — но не об этом сейчас. И так у нас три канала по которым летят данные из области ОЗУ с битмапом спрайта, с его непосредственным содержимым. Эти три канала D0,D1,D2 мы можем перемешать по принципу встреченного байта прозрачности (для простоты просто нуля). Если в D0 в данном такте появился 0 то значит нужно выдать ARGB из канала D1, но если и в канале D1 у нас тоже прозрачный пиксель (ноль), то берем ARGB из канала D2. Это все довольно просто описывается в HDL, не в ЯВУ.
Имея такую аппаратную примочку мы освобождаем процессор только от маскирования потока из областей спрайтов и необходимости постоянно лить данные спрайтов, когда они меняются на экране, в фрейм-буфер. Для Спрайтового движка понятия фрейм-буфер я считаю не применимо. Это по сути текстовый экран только буква не 8х8 а 32х32 и летит не с знакогенератора (который может быть и в ПЗУ), а из области ОЗУ со спрайтом. Мы меняем код спрайта в этом слое и в течении 12 тактов, без каких либо затрат на пересылку байт битмапа 32х32 (а это прилично!) получаем смену изображения. Все что нам нужно это просто загодя (за 12 тактов раньше) начать формировать растр по строке. Так как данные будут сыпаться в этой конвейер друг за другом, перерывов между ними не будет и работать можно с дичайшим FPS меняя спрайты всего экрана чуть ли не 100 раз за кадр. Заменить 768 байт на новые — это не вопрос даже для 6502 с 1,75МГц клока.
Но… грустно становится когда нам надо произвести скролинг внутри знакоместа, ну кого к примеру устроит резкий прыжок танчика на 32 точки вперед? И вот тут бывает так что танчик то не 32х32, а нечто побольше. Мало того хотелось бы двигать его не только прямо, а скажем по диагонали или вообще под произвольным углом. И мы начинаем копировать байты внутри спрайтовой области ОЗУ. А поскольку плана у нас три, и мы хотели бы что то еще двигать на задних планах, траву, кроны деревьев, какие либо еще произвольные объекты. То увы даже загнав имеющуюся корку 6502 на 50МГц (выше не получалось) удается разве что 20FPS обеспечить. Разговор конечно не про один объект, а про кучу различных. И даже 6809 выдавал до 30FPS на сценах, как ниже. Не из за одного «танчика», конечно это все затевалось. У меня к сожалению ничего продемонстрировать не осталось из того проекта, так как уже на симулях стало понятно — что это все не то. Но можно показать к чему пришли.
В итоге это все вылилось в фрейм-буфер 1024х768х8бит с двойной буферизацией, и то что там применено в качестве процессора :) отдельная песня — вывозило только 75% заполнения экрана. Но вот даже такая демка, которую для этой аппаратуры писал мой товарищ — 8 битные корки и даже 16 битные корки процессоров ложило наглухо. А это наверное 10%-20% имеющейся пропускной.
Demo clear render
Как то с другом мы обсуждали возможности в разрезе Clear reander engine vs Sprite/Tile Engine. Ну и закусились на тему, а что было бы
если бы у бабушки были бы… если бы было три спрайтовых плана с приоритетом отрисовки (подобие Z-буфера). Было бы мол тогда интересней программить в ЯВУ те же танчики? Ну сказано сделано, примерно недели две возни с верилогом и готовый адаптер для отрисовки такой графики появился. Повозится конечно пришлось, что бы выйти на времянки 1024х768 (ЕМНИП). 12 стадий в конвейере устройства, причем регистры на стадии в ФУ конвейера, вовсе нет — простенькие мультиплексоры даже пришлось огораживать. Какое то время мне казалось что я не вывожу на этой ПЛИС задачу и как бы вообще на голую шину не пришлось ставить регистры в отрезки между ФУ. Но нет обошлось 12 стадиями. Жуткое чудо юдо.Да, но это я к тому что вот это вот убер устройство хотели повесить на корку 6502, ну и оно не потянуло. Потом я рассматривал 6809 и тоже нет, не потянуло :) Потому что, если бы только проблемы отрисовки спрайтов поверх других спрайтов с аппаратным перекрытием, а ведь еще и скроллинг этих спрайтов попиксельно в памяти самих спрайтов. После этого как то утвердился в мысли о том что такого рода аппаратура мало что даст для программера, нужен именно прокаченный процессор и желательно такой который может за такт не одну инструкцию, и при это еще бы и асинхронно.
Можно еще вот что попробовать — уже после запуска подпапка CPSSoftware содержит ini-файлы создаваемые в момент запуска. В моей сборке там есть пути из моего компьютера. Удалить всю папку и попробовать снова.
Первые калькуляторы на арифметике +-*/ делались чисто под эти операции — BCD-логика разряд за разрядом — дешевая память в виде shift registers просто просилась в такие алгоритмы. А дальше уже остановится не могли усложняя раз за разом на базе предыдущего опыта.
У меня в детстве был МК-61, и я даже неплохо по нему упарывался, но честно говоря никода даже не задумывался как он реализован внутри.
Думал, что в микрухах просто обычные элементы зашиты. А оказалось, что всё по серьёзному и современному — микрокод и всё такое.
Так же интересная архитектура с этим кольцевым буффером, хоть из-за неё и страдает быстродействие, но действительно достаточно элегантно и просто можно расширять схему.
Кстати на nesdev.com вчера (с этой же статьи по сути) мне рассказали, что похожий чип Sharp SM-59x трудился в NES в чипе региональной защиты CIC в американских картриджах. Т.е. Nintendo с Sharp так сказать совсем даже не прекратила отношения по этой линии тогда. :)
P.S. Спасибо за статью! Порадовали. Где то в комментах проскакивало, что Вы собираетесь пойти крестовым походом на 6502, буду рад почитать.